API conventions
Idempotency, errors, pagination, versioning and rate limits for the Voice Engine API.
Coming soon. The Voice Engine API is in development. This page describes the planned design; details may change before release.
- Base URL:
https://api.trillet.ai/partner/v1 - Format: JSON request and response bodies, UTF-8, timestamps in ISO 8601 UTC.
- IDs: Trillet IDs are opaque strings. Store them as strings.
Idempotency
Networks fail. Send an Idempotency-Key header on every request that creates something, so a retry never creates it twice. Tenant creation requires one.
curl -X POST https://api.trillet.ai/partner/v1/tenants \
-H "Authorization: Bearer tpk_live_..." \
-H "Idempotency-Key: create-tenant-cust_8842" \
-H "Content-Type: application/json" \
-d '{ "externalId": "cust_8842", "name": "Harbour Dental" }'
| Situation | Result |
|---|---|
| First request with a key | Runs normally. The response is stored for 24 hours. |
| Retry with the same key and the same body | The stored response is returned with Idempotent-Replayed: true. Nothing runs twice. |
| Same key, different body | 422 idempotency_key_reused |
| Same key while the first request is still running | 409 idempotency_key_in_use. Retry after Retry-After. |
The first request failed with a 5xx | Not stored. A retry runs again. |
Keys are 1 to 255 printable characters and are scoped to your partner account. A UUID, or your own operation ID, works well.
Errors
Every error has the same shape:
{
"error": {
"type": "invalid_request",
"code": "tenant_deleted_restorable",
"message": "A deleted tenant with this externalId can still be restored.",
"param": "externalId",
"requestId": "req_7Qe2..."
}
}
Handle errors by code, not by message. Include requestId when you contact support.
| Status | Meaning |
|---|---|
400 | The request is malformed or a required field is missing |
401 | Authentication failed |
403 | Not allowed for this key |
404 | Not found, or not yours |
409 | Conflict, for example a duplicate externalId or a request still in progress |
422 | The request is well formed but cannot be applied |
429 | Rate limited |
5xx | A problem on our side. Safe to retry with the same Idempotency-Key. |
Pagination
List endpoints return a page and a cursor:
{
"data": [ { "id": "ten_1" }, { "id": "ten_2" } ],
"hasMore": true,
"nextCursor": "ten_2"
}
Pass limit (1 to 100, default 20) and starting_after=<nextCursor> to get the next page.
Versioning
The major version is in the path (/partner/v1). Within a version we only add things: new endpoints, new optional fields, new event types. Your code should ignore fields and event types it does not recognise.
Breaking changes ship as a new major version, with notice and an overlap period. Webhook payloads carry an apiVersion field.
Rate limits
Limits apply per key. The default is 100 requests per second in bursts and 6,000 per minute. A 429 response includes Retry-After. Contact us if your integration needs more.