Integration Conventions
The mechanics shared by every APEX product: headers, error shapes, status-code semantics, retries, async patterns, discovery, and rate limits. Read this once and most product chapters become predictable.
Headers
| Header | Direction | Convention |
|---|---|---|
Authorization |
request | Bearer <token or API key> — see Authentication |
X-Correlation-ID |
request | Your transaction identifier. Honoured if present, generated if absent, echoed on the response, and attached to the platform's usage record for the call. Send one per business transaction — see Usage & Cost. |
X-Tenant-ID |
request | Tenant carriage for Document Operations and Quantum only — other products carry tenant in the path, query, or body per the tenancy table. |
x-correlation-id, x-tenant-id |
response | Echoed back for log correlation. Echo additionally returns X-Request-ID and X-Response-Time. |
Idempotency-Key |
request | Provisioning calls only (provision:<uuid>) — makes tenant creation replay-safe. |
Request bodies are JSON (Content-Type: application/json) except Document Operations uploads, which are multipart/form-data. Binary downloads (rendered PDF/DOCX, captured media, job results) return the appropriate content type with an attachment disposition.
Error envelopes — per product, not one platform shape
Do not write a single generic error parser. Each product family has a stable, documented shape:
| Products | Shape |
|---|---|
| Echo, Vector (most routes), Document Operations | { "ok": false, "data": null, "error": { "code", "message", "detail" }, "meta": { "request_id", "timestamp", "version" } } |
| Vector (authentication failures) | { "error": "AUTH_REQUIRED" \| "AUTH_INVALID" \| "TENANT_MISMATCH", "detail": … } |
| Forge portal | { "error": { "code", "message" } } |
| Zenith, Pulse, Quantum | { "detail": "<descriptive message>" } (Quantum uses coded errors such as VALIDATION_ERROR) |
Success envelopes mirror the same split: enveloped products wrap payloads in data; Zenith and the Echo /send response are raw JSON. Each OpenAPI specification states the shape per operation — trust the spec.
Status-code semantics
| Status | Meaning on APEX | Retry? |
|---|---|---|
400 |
Malformed request, unsupported parameter, channel not configured | No — fix the request |
401 |
Credential missing, malformed, or expired | Mint a fresh token, then retry once |
403 |
Valid credential, not permitted: tenant mismatch, missing entitlement, tier below feature | Never retry unchanged — wiring or configuration issue |
404 |
Unknown tenant or resource — including the fail-closed "tenant not configured for this product" state | No — resolve through configuration |
409 |
Conflict: quota exceeded, job not yet complete, simulation not running | Retry only after the conflicting condition changes |
422 |
Validation failure: unknown pack, unroutable message, schema violation | No — fix the payload |
429 |
Rate limited | Yes — honour Retry-After |
5xx |
Service or upstream failure (502 often surfaces a provider rejection verbatim) |
Yes — exponential backoff with jitter, bounded attempts |
Retry rules
- Retry
429afterRetry-After, and502/503/504with exponential backoff plus jitter (e.g. 1s, 2s, 4s, cap ~30s, max 4–5 attempts). - Never blind-retry
4xx. A403retried in a loop is the classic integration bug. - Make resubmission safe where the API supports it: Echo
/sendaccepts a client-suppliedid; tenant provisioning takesIdempotency-Key. Reuse the same value on retry. - Long-running work should not be retried at the submit layer while a job is still in flight — poll the job you already own.
The three async patterns
Submit-then-poll (Zenith ingestion, Document Operations jobs, Echo delivery): submit returns an identifier immediately; poll the status resource until completed or failed. Poll every 2–5 seconds with a sensible ceiling; the Quickstart shows the loop.
Server-Sent Events (Pulse simulation and signal streams; the planned LLM gateway streaming mode): Content-Type: text/event-stream, one JSON event per message, terminal event marks completion. Reconnect on drop; do not treat a dropped stream as job failure — poll the resource to confirm state.
Signed webhooks (Echo status and inbound-message delivery): Echo pushes events to your configured endpoints, signed with X-Echo-Signature — an HMAC-SHA256 hex digest of the raw body using your tenant's webhook secret. Verify the signature before trusting the payload, respond 2xx quickly (do the work asynchronously), and make handlers idempotent — deliveries retry with backoff. Webhook endpoints and secrets are tenant configuration, set by your operator.
Discovery: the composition contract
Every product serves GET /forge-contract.json — a machine-readable composition contract listing the module's identity, declared endpoints, events, and MCP tools. Use it for smoke checks and version pinning in CI; a contract change you did not expect is your earliest drift signal.
Rate limits
Messaging and intelligence products enforce ~120 requests/minute per credential; Vector applies per-tenant hourly caps on heavy operations (/verify, /score-risk, /check-entity). All return 429 + Retry-After. Design for backpressure: queue outbound work in your application rather than bursting at the platform.