Data Retention & End-User Budgets
Restrict requests to zero-data-retention routes, and cap what each of your own end users can spend per month.
Overview
Two controls apply to POST /v1/chat/completions, POST /v1/responses, POST /v1/messages and POST /v1/embeddings:
- Zero data retention (ZDR) — serve a request only on a route whose model provider does not retain prompts or responses, and store no prompt or response content on our side.
- Per-end-user budgets — a monthly credit cap per end user of your application, keyed on the
userfield you already send.
Zero data retention
Turning it on
Send provider.zdr in the request body:
{
"model": "your-model-id",
"messages": [{ "role": "user", "content": "Summarise this contract." }],
"provider": { "zdr": true }
}provider.zdr must be a JSON boolean; any other value returns 422. On /v1/messages the same provider object is accepted as a CallMissed extension.
ZDR can also be enforced without the flag:
- Per API key — turn on Zero data retention on the key in the dashboard.
- Account-wide — turn on the data-retention policy in the dashboard (owners and admins).
When the key or account policy is on, every request is treated as ZDR. A request can switch ZDR on, never off: "zdr": false does not override a key or account policy.
Which models qualify
Every entry in GET /v1/models carries a boolean zero_data_retention. It is true only when the route serving that model is backed by the provider's own published zero-retention terms. Today no model in the catalogue is marked true. A ZDR request is refused rather than served on a route that retains data.
When no route qualifies
A ZDR request for a model with zero_data_retention: false is rejected before any model is called and is not billed:
{
"error": {
"message": "Model 'your-model-id' has no zero-data-retention route. ...",
"type": "invalid_request_error",
"code": "zdr_unavailable"
}
}The request is never downgraded to a route that retains data, including through fallbacks: models in your models fallback list that have no zero-data-retention route are skipped.
On /v1/chat/completions, bot_id knowledge-base retrieval also returns zdr_unavailable under ZDR, because retrieval sends your latest message to an embedding model.
The Batch API also returns zdr_unavailable under ZDR (at upload and at batch create), because a batch stores its input and output files.
What we do not store for ZDR requests
- Prompt and response logging is skipped, even when prompt logging is enabled on the key.
- The response cache is neither read nor written.
Idempotency-Key(non-streaming requests): the response body is not stored. A retry with the same key returns409instead of a replay, so the call is not run or billed a second time.
Usage metering (model, token counts, cost, latency) is still recorded so the call can be billed.
Per-end-user budgets
How the cap is applied
Pass your end user's id in the request:
| Endpoint | Field |
|---|---|
/v1/chat/completions, /v1/responses | user (a cap can only be set on an id of at most 256 characters; a longer id is accepted and uncapped) |
/v1/embeddings | user (at most 256 characters) |
/v1/messages | metadata.user_id (at most 512 characters; a cap can only be set on an id of at most 256) |
If that id has a cap and has already spent its monthly limit, the request is rejected before any model is called:
{
"error": {
"message": "Monthly budget for this end user (the `user` field) is exhausted. ...",
"type": "insufficient_quota",
"code": "end_user_budget_exceeded"
}
}The status is 402. On /v1/messages the error uses the Anthropic shape with type billing_error.
- Spend is counted per calendar month (UTC) and resets at the start of each month.
- The cap is checked before each call, so requests already in flight when the limit is reached can take the total slightly past it.
- A limit of
0blocks that end user. - Ids without a cap are not limited and not tracked.
- Ids are matched exactly and are case-sensitive.
Authentication
Authorization: Bearer cm_your_api_key| Operation | Scope |
|---|---|
| List | end_user_budgets:read |
| Create or update, delete | end_user_budgets:write |
GET /api/v1/gateway/end-user-budgets
| Parameter | Type | Required | Constraints |
|---|---|---|---|
end_user | string | No | Exact id, at most 256 characters |
limit | integer | No | 1 <= limit <= 200, default 100 |
offset | integer | No | 0 <= offset <= 100000, default 0 |
[
{
"id": "5c1d…",
"end_user": "customer-42",
"monthly_limit_credits": 50.0,
"period_start": "2026-10-01T00:00:00Z",
"period_used_credits": 12.5,
"remaining_credits": 37.5,
"created_at": "2026-09-14T08:00:00Z",
"updated_at": "2026-10-01T09:30:00Z"
}
]period_used_credits is this month's spend. It reads 0 if the id has not been charged yet this month.
PUT /api/v1/gateway/end-user-budgets
Creates the cap, or changes the limit if one exists. Spend already recorded this month is kept.
| Field | Type | Required | Constraints |
|---|---|---|---|
end_user | string | Yes | 1–256 printable characters, stored exactly as sent |
monthly_limit_credits | number | Yes | 0 <= value <= 10000000 |
curl -X PUT https://api.callmissed.com/api/v1/gateway/end-user-budgets \
-H "Authorization: Bearer cm_your_api_key" \
-H "Content-Type: application/json" \
-d '{"end_user": "customer-42", "monthly_limit_credits": 50}'The response is one budget object in the same shape as the list items.
DELETE /api/v1/gateway/end-user-budgets/{id}
Removes the cap. The id becomes uncapped and its spend counter is discarded. Returns 204, or 404 for an id that is not in your account.
Bring Your Own Key
Store your own model-provider credentials, verify them, rotate them, and deactivate them — the secret is never returned.
Contacts
The person object — create, search, update and delete contacts, the phone/email/WhatsApp uniqueness rule, consent flags, and the facts your agents remember about each contact.