Headers
Every request carries four headers:Canonical string
The signature is an HMAC-SHA256 (keyed with your secret) over six newline-joined lines:signature, sort by key then by value, and re-encode each as encodeURIComponent(key)=encodeURIComponent(value) joined with &. An empty query is an empty line.
Dynamic path segments are signed as part of
PATH exactly as sent - e.g.
/api/agent/analyses/38f2e1fc-.../enrich. Sign the raw body bytes you actually transmit, byte for
byte.Reference implementation
The Quickstart includes a complete ~40-linesignedFetch for Node.js. Port it to any language with an HMAC-SHA256 primitive; the canonical string above is the whole contract.
Validity rules
- Clock skew: timestamps outside the accepted window are rejected. Keep the client clock NTP-synced and generate the timestamp at send time.
- Nonce: use a fresh nonce per request; replayed nonces are rejected.
- Scopes: keys carry scopes (
analysis:write,export:read, …). A key without the required scope receives403. - Failed auth always returns a stable
{ "error", "code" }JSON envelope - see Errors.