Partner API

Authentication and safe retries

Exact HMAC signing, replay protection and financial intent handling.

Sign exact bytes

All integration routes in the catalogue require these headers:

X-Crisp-Partner-Key: cpk_…
X-Crisp-Timestamp: ISO-8601 UTC timestamp
X-Crisp-Nonce: unique nonce, 16–128 characters
X-Crisp-Signature: base64 HMAC-SHA-256

Canonical input (newline separators, no final newline):

timestamp
nonce
UPPERCASE_METHOD
/v1/exact-percent-encoded-path
sha256_hex(raw_body_bytes)

The path includes /v1 and excludes the query string under the current contract. Sign the encoded path actually sent; do not decode and rebuild it. An empty body has the SHA-256 of zero bytes. Serialize JSON once, sign those bytes, and send the same bytes. HTTPS is mandatory; signatures do not replace TLS. The downloadable client implements this format.

Nonce characters: letters, digits, ., _, ~, -; length 16–128. Default timestamp tolerance is five minutes. Nonces are single-use, including GETs. Traffic ingestion is the designated exception: durable tenant/event ID deduplication is used instead of persisting another nonce row. Always generate a fresh nonce.

Idempotency is not authentication

Financial and other durable mutations marked Required in the catalogue need Idempotency-Key: 8–200 letters/digits or ., _, :, -.

  • Persist one intent key before the first submit. Use it for retries of that same path, user and byte-identical body; never recycle it for another intent.
  • Generate new authentication timestamp/nonce/signature for every attempt.
  • A double-click, timeout or 502 does not mean the original action failed.
  • Do not automatically generate a new key to get around an in-progress or ambiguous response. Correct the cause or reconcile the existing action.
  • Quotes, prewarm, realtime sessions and capability minting are not durable money movements and do not require that header. Page views use eventId.

Idempotency responses have retention; do not assume a key is permanent history. Keep your own durable action records. Contact CRISP before retrying an old uncertain financial request whose lifecycle cannot be established.

Responses

{ "data": { "status": "ready" }, "requestId": "request-uuid" }
{ "error": { "code": "account_not_ready", "message": "…", "requestId": "request-uuid" } }
StatusHandling
200Successful read/action; inspect resource state
202Durable acceptance; NOT proof of execution, settlement or final transfer
400Correct invalid fields; do not blindly retry
401Check key, canonical bytes, clock and nonce
403Scope/authority denied; do not retry with another user's identity
404Check account/resource and authenticated tenant
409Inspect code: in-progress, not-ready, reused nonce, conflict or ambiguous outcome are different cases
429Back off, honor Retry-After when supplied; agree deployment limits with CRISP
502/503 or timeoutBack off reads; preserve original financial intent and reconcile before replacement

The example client surfaces errors/statuses and never auto-retries. Log safe request IDs, your intent ID and timestamps, not credentials or entire user payloads.

CRISP Partner Developer Documentation