Runtime API reference
The customer-side runtime contracts: the signed intent header your backend verifies, the act-as-user contract it observes, and the witness payload shape.
Three contracts connect Syncanix to your running backend: the signed intent header that authorizes each tool call, the act-as-user fields that carry the end-user identity, and the witness payloads that report API shapes. This page is the field-level reference; the guides cover the flows.
Intent verification โ the X-Syncanix-Intent header
Every tool call Syncanix sends to your backend carries a signed intent. Your SDK (or your own code) verifies the HMAC-SHA256 signature with your tenant secret, checks the expiry, and checks that the method and path match the actual request โ then exposes the decoded payload to your handler. The authenticated act-as-user envelope additionally binds the operation, the audience, the call arguments, and a single-use nonce.
X-Syncanix-Intent: base64url(JSON({ "payload": <obj>, "signature": "<hex>" }))
payload = { v: 2, toolCallId, tenantId, sub, aud, operation, argsHash, nonce, issuedAt, expiresAt, requiresStepUp? }
signature = hex(HMAC-SHA256(JSON(payload), secret))
// v1 (legacy) payloads โ { toolCallId, tenantId, userId?, method, path, issuedAt,
// expiresAt } with no "v" field โ remain accepted during the dual-accept window.The v2 envelope is what makes a chat-initiated call safe to act on: sub is the audit principal the action runs as, aud binds the token to your API so it can't be replayed against another tenant, argsHash pins the exact arguments so the body can't be tampered with after signing, and the single-use nonce stops the whole token being replayed inside its lifetime.
- toolCallId
- Unique ID of this tool call โ use it as your idempotency key for retried deliveries.
- tenantId
- Your Syncanix workspace (tenant) the call belongs to.
- sub
- The signed-in end user the assistant is acting for, and the principal every action is authorized and audited against. Mandatory on the v2 envelope.
- aud
- The customer API audience this intent is bound to โ so tenant separation no longer rests on the HMAC secret alone. Reject a token whose aud is not yours.
- operation
- The transport-aware operation this intent authorizes (e.g. HTTP method and path). Verification fails if it does not match the actual request.
- argsHash
- Lowercase-hex SHA-256 of the canonicalized call arguments. Recompute it from the request body and reject on mismatch โ this closes body-tampering replay.
- nonce
- A single-use value your verifier records and refuses to accept twice, closing replay-within-TTL.
- issuedAt, expiresAt
- Unix timestamps bounding the intent lifetime. Expired intents are rejected.
- requiresStepUp (optional)
- True when the action requires a fresh step-up verification. Reject unless your step-up gate has run.
Legacy v1 tokens โ no v field, binding only method and path with an optional userId โ remain accepted during the dual-accept rollout window, so already-integrated backends keep verifying while you upgrade. New tokens are minted as v2.
Verification failures return 403 with a machine-readable reason such as missing-header, malformed, bad-signature, expired, method-mismatch, or path-mismatch; the stronger act-as-user binding adds operation, argument, audience, and replay checks. The same reason strings are used across the SDKs.
Act-as-user โ what your backend observes
When the assistant acts for a signed-in user, the act-as-user guide covers the end-to-end flow. At the contract level, your backend observes exactly two things: the intent payload names the acting user (sub on the authenticated act-as-user envelope; userId on a legacy anonymous one), and write actions arrive only after Syncanix has run its confirmation gate.
Authorization stays yours: treat the acting-user subject (sub) as the identity to authorize against โ exactly as if that user had called the endpoint directly. Syncanix authorizes that the CALL was intended; your backend authorizes what that USER may do.
Witness โ the runtime schema reporter
The witness middleware observes your API traffic to keep the capability catalog accurate. For each observed request and response it infers a shape descriptor โ the structure WITHOUT the values: scalars report only their type (string, number, integer, boolean, null), arrays report their item shape, objects report their property shapes. Shapes for the same method and path are merged across observations.
Values pass through redaction BEFORE inference, so the inferrer never sees sensitive strings; divergent or empty structures collapse to unknown rather than guessing.