Skip to main content

API Contract

This document captures the runtime contract for the HTTP and gRPC surfaces: versioning, envelopes, error codes, rate limits, pagination, and idempotency expectations.

Base URLs & Versioning

  • HTTP base path: /v1 (example: http://localhost:8080/v1/tenants/{tenant_id}/orders).
  • OpenAPI: /api-docs/openapi.json and interactive docs at /docs/ (alias /swagger-ui/).
  • Version headers: responses include x-api-version (current 1.0.0) and x-api-supported-versions (minimum supported version). Requests may optionally send x-api-version; unsupported versions return 400.
  • Deprecation: deprecation/sunset headers are wired in the middleware for future sunset notices (none active now).

Authentication

  • HTTP (tenant): x-stateset-api-key: <tenant-key> per request.
  • HTTP (operator / dashboard): x-stateset-admin-key: <platform-admin-key> is restricted to platform-admin /v1/admin/* calls and org-bound operator-safe tenant reads inside the WorkOS-authenticated dashboard proxy.
  • gRPC: same key via metadata header x-stateset-api-key on port 50051.

Webhook Authentication

  • Shopify webhook ingress (/v1/tenants/{tenant_id}/webhooks/shopify) uses x-shopify-hmac-sha256.
  • Amazon SNS webhook ingress (/v1/tenants/{tenant_id}/webhooks/amazon) uses SNS RSA signature verification.
  • TikTok webhook ingress (/v1/tenants/{tenant_id}/webhooks/tiktok) uses HMAC SHA-256 signature verification from x-tts-signature/x-tiktok-signature.
  • Walmart webhook ingress (/v1/tenants/{tenant_id}/webhooks/walmart) uses HMAC SHA-256 signature verification from x-walmart-signature.
  • eBay webhook ingress (/v1/tenants/{tenant_id}/webhooks/ebay) uses HMAC SHA-256 signature verification from x-ebay-signature.
  • Etsy webhook ingress (/v1/tenants/{tenant_id}/webhooks/etsy) uses HMAC SHA-256 signature verification from x-etsy-signature.
  • WooCommerce webhook ingress (/v1/tenants/{tenant_id}/webhooks/woocommerce) uses HMAC SHA-256 signature verification from x-wc-webhook-signature.
  • ACP webhook ingress (/v1/tenants/{tenant_id}/webhooks/acp/orders) uses HMAC SHA-256 with:
    • x-acp-timestamp: Unix seconds/milliseconds or RFC3339 timestamp
    • x-acp-signature: HMAC of "{timestamp}.{raw_body}" (hex or base64; v1=/sha256= prefixes supported)
  • Shopify secret is read from {TENANT_ID}_SHOPIFY_WEBHOOK_SECRET (for tenant acme: ACME_SHOPIFY_WEBHOOK_SECRET).
  • TikTok secret is read from {TENANT_ID}_TIKTOK_WEBHOOK_SECRET (for tenant acme: ACME_TIKTOK_WEBHOOK_SECRET).
  • Walmart secret is read from {TENANT_ID}_WALMART_WEBHOOK_SECRET.
  • eBay secret is read from {TENANT_ID}_EBAY_WEBHOOK_SECRET.
  • Etsy secret is read from {TENANT_ID}_ETSY_WEBHOOK_SECRET.
  • WooCommerce secret is read from {TENANT_ID}_WOOCOMMERCE_WEBHOOK_SECRET.
  • ACP secret is read from {TENANT_ID}_ACP_WEBHOOK_SECRET (for tenant acme: ACME_ACP_WEBHOOK_SECRET).
  • Unsigned ACP webhooks are rejected unless STATESET_ALLOW_UNSIGNED_ACP_WEBHOOKS=true.

Response Envelopes & Correlation

  • Success shape:
  • Error shape:
  • Every response also carries x-request-id; it matches meta.requestId and can be used for support tickets and trace correlation.

Error Codes

Rate Limiting

  • Keyed by client IP via tower_governor. Bucket: burst 500, refill one token every 100ms (~10 requests/second sustained).
  • On 429 responses:
    • Headers: x-ratelimit-limit, x-ratelimit-remaining, x-ratelimit-after, Retry-After (seconds), x-request-id.
    • Body: standard error envelope with code=rate_limit_exceeded and details.retryAfterSeconds.
  • Per-tenant concurrency is additionally shaped by semaphores for each integration to keep within vendor quotas.

Idempotency & Retries

  • POST /v1/tenants/{tenant_id}/orders is idempotent on shopify_order_id; if the order already exists in StateSet the existing stateset_order_id is returned without creating a duplicate.
  • POST /v1/tenants/{tenant_id}/orders/batch accepts up to 200 orders and returns per-item success/failure details; set stopOnError=true to fail-fast (otherwise it continues through all entries).
  • Batch imports support preview mode with dryRun=true (request body) or ?dry_run=true (query param). In dry-run mode the API validates each item, reports action (would_create / would_reuse_existing / failed), and performs zero writes.
  • POST /v1/tenants/{tenant_id}/orders/backfill/shopify/{shopify_order_id} is an operator repair endpoint: it fetches the order snapshot from Shopify and persists it locally when missing. It returns 201 when it inserted a new row and 200 when the order already existed locally.
  • All write endpoints (POST/PUT/PATCH/DELETE) also accept an idempotency-key header. Successful responses are cached for 24h and replayed on retry with idempotency-replayed: true; if a matching request is still running, the server returns 409 operation_in_progress. When PostgreSQL is enabled, idempotency state is shared across replicas; otherwise it falls back to process-local memory.
  • Sync triggers (/sync/*) enqueue background jobs; use /v1/tenants/{tenant_id}/jobs/{job_id} to poll instead of holding the original request open. Triggering twice will create two jobs, so prefer client-side de-duplication when retrying.
  • GET /v1/tenants/{tenant_id}/jobs/throughput returns time-bucketed job analytics (created/finished/succeeded/failed) for dashboard charts. Query params: hours (default 24) and bucket_minutes (default 60).
  • Downstream calls use exponential backoff (see src/resilience.rs) and classify retryable upstream errors (timeouts, 5xx, 429) automatically.
  • Webhook operator tooling supports both single replay (POST /v1/tenants/{tenant_id}/webhook-events/{webhook_id}/replay) and bulk replay (POST /v1/tenants/{tenant_id}/webhook-events/replay?state=failed&limit=50).
  • Bulk replay supports additional safety controls:
    • exact topic filtering via topic
    • preview mode via dry_run=true (returns selected ids without queueing)
    • source-level throttling via max_per_source=<n>
  • Targeted replay is available via POST /v1/tenants/{tenant_id}/webhook-events/replay-by-id with JSON body (webhookIds, optional dryRun).

Auditability

  • Bulk and operator repair actions emit data_access audit events (resource types: orders_batch_import, orders_batch_import_preview, orders_backfill_shopify, webhook_events_bulk_replay, webhook_event_replay).
  • Audit entries are best-effort persisted to the database (when configured) and always logged to structured application logs.
  • x-stateset-user-email and user-agent headers are captured when present for operator attribution.
  • Admin audit queries support resource_type filtering: GET /v1/admin/audit-logs?event_type=data_access&resource_type=orders_batch_import.

Admin Onboarding Readiness

  • GET /v1/admin/tenants/{tenant_id}/readiness returns a scored onboarding report for customer go-live checks.
  • GET /v1/admin/tenants/{tenant_id}/onboarding-plan returns prioritized action items derived from the readiness report.
  • PATCH /v1/admin/tenants/{tenant_id}/onboarding-plan/actions/{action_id} persists operator workflow state for each action (pending, in_progress, done, blocked) with optional notes.
  • PATCH /v1/admin/tenants/{tenant_id}/onboarding-plan/actions supports bulk workflow updates for multiple actions in a single request.
  • GET /v1/admin/tenants/{tenant_id}/onboarding-plan/actions/history returns append-only action history with actor/timestamp context.
  • PATCH /v1/admin/tenants/{tenant_id}/onboarding-profile upserts tenant onboarding metadata (ownerEmail, ownerName, goLiveTargetAt, notes).
  • GET /v1/admin/onboarding/portfolio returns a cross-tenant onboarding pipeline summary (scores, risk, blockers, workflow counts, next action, owner/go-live metadata). Supports optional filters via readinessStatus, integration, hasBlockers, minRiskScore, minBlockedActionAgeHours, ownerAssigned, and overdueGoLive; sorting via sortBy (risk|score|tenant_id) + sortOrder (asc|desc); includes summary counters/thresholds for highRiskTenants, staleBlockerTenants, unassignedOwnerTenants, and overdueGoLiveTenants; use includeIntegrationTests=true to run live health checks in the aggregation response.
  • The response includes:
    • score (0-100) and grade (A+..F)
    • overallStatus (ready, attention_required, not_ready)
    • required check results (tenant runtime, integration config, channel/destination coverage, auth readiness, Shopify webhook secret, connectivity)
    • per-integration connectivity test results
    • guide readiness for Shopify -> NetSuite and Shopify -> DCL with missing requirements

Pagination & Filtering

  • GET /v1/tenants/{tenant_id}/orders supports page (default 1), page_size (default 50, max 100), status, created_after, created_before, order_number, shopify_order_id, tags.
  • Response uses PaginatedResponse<T>: items plus pagination metadata (page, pageSize, totalItems, totalPages, hasNext, hasPrevious).
Example:

Examples

  • Create order (idempotent on Shopify order id):
  • Create orders in batch (up to 200 per request):
  • Error response (validation): 422 with body shown above and x-request-id for correlation.

gRPC quick start