Documentation
Going to production
The list of things that break when the integration stops being an experiment.
None of this is required for the API to work. All of it is what separates an integration that works from one that is still working at three in the morning on a Tuesday.
Secrets
- The
client_secretbelongs in a secret manager or a server environment variable. Never in the repository, a frontend, a mobile app or a URL. - One credential per integration, not one shared across several. When something leaks, you want to revoke that and not everything.
- Ask only for the scopes you use. It is the difference between a bounded incident and one that is not.
- To swap secrets with no downtime, create a parallel credential and revoke the old one once traffic has moved. Rotating is the emergency path: the new secret is generated by the rotation, so there is a
401window until you deploy it. - Do not log
Authorizationorclient_secret. If your framework logs headers, filter them explicitly.
Tokens
- Cache the access token and refresh it 60 seconds before
expires_in. A token expiring mid-flight causes an avoidable401. - On a
401, request a fresh token and retry once. If it repeats, it is not the token: it is the credential, the user or the company. - With multiple processes, share the token instead of each requesting its own: the
client_idbucket is shared. - Do not parse the JWT. Its claims are an internal detail; if you need to know whether it is still valid, use
introspect.
Turns
- Generate the
Idempotency-Keyoutside the retry loop, and persist it alongside the job. If your queue re-enqueues, the key has to survive that. - Raise your HTTP client's read timeout above 30 minutes, or use the stream. Almost every client's default is too short for a real turn.
- One chat per conversation. The one-API-turn-per-chat limit is not about volume: it is what prevents cancelling one from cancelling the other.
- Cancel explicitly. Closing the connection cancels nothing: the turn keeps running and keeps billing until it finishes or expires.
Errors
- Branch on
message_code, never onmessage. - Retry
429,500,502,503and504with exponential backoff and jitter. Do not retry contract4xx: insisting does not help. - Honor
Retry-Afterwhen it is present. - A turn that failed with
502or504already consumed its key: to try again, generate a new one. - Never retry an interaction that returned
409: applying the same approval twice is worse than not knowing whether it applied.
Observability
- Store every response's
X-Request-Id— success and error — in your logs. It is what to quote so we can trace one of your requests on our side. - Use
metadatato tie each turn to a record of yours. It comes back in the response and in the message listing, so you do not need a mapping table. - Monitor
X-RateLimit-Remainingand throttle before hitting zero. - Alert on turn
usage: a prompt change that doubles the tokens breaks nothing, which is exactly why nobody notices.
Usage and license
Each turn draws from the niucredits pool of the credential's owning user's license. When it runs out, the send is rejected even if you have request quota to spare.
- Watch usage with Analytics before it becomes an incident.
- Deleting a chat does not refund usage: the delete is logical and the spend already happened.
- If the owning user leaves the company, their credential stops working. For long-lived integrations, use an organization credential or a dedicated service account.
Contract changes
The version travels in the URL (/api/v1). Within v1, changes are forward compatible: new fields can appear in responses and new values in an enum, but no field disappears and no existing field changes meaning.
- Ignore fields you do not know instead of failing to deserialize them.
- Do not assume an enum is closed:
steps[].typeand errorcodevalues can grow. - For requests it is the opposite: bodies are strict, and a field the API does not know returns
422. Send only what is documented.