Models
One endpoint for every major model. Models speaks the chat completions format that most libraries already use, so adopting it is a change of base URL. Each call is checked against your caps, priced, and logged against the customer who caused it.
Making a call
Point any OpenAI-compatible client at https://api.dianthan.com/v1/ai and use an app API key.
import OpenAI from "openai";
const ai = new OpenAI({
baseURL: "https://api.dianthan.com/v1/ai",
apiKey: process.env.DIANTHAN_KEY,
});
const reply = await ai.chat.completions.create({
model: "anthropic/claude-sonnet-5.5",
messages: [{ role: "user", content: "Summarise this note in one line." }],
user: session.userId,
});Or with plain HTTP:
curl https://api.dianthan.com/v1/ai/chat/completions \
-H "Authorization: Bearer dn_live_…" \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-sonnet-5.5",
"messages": [{ "role": "user", "content": "Hello" }],
"user": "usr_x81…",
"metric": "ai.summary"
}'modelis a model id inprovider/modelform.GET /v1/ai/modelslists every id available to the app, with its context length and price per token.usernames the customer the call is for. It is used for per-customer caps and cost, and it is never sent on to the model provider.metricis an optional label of your own, such asai.summary, so you can see cost per feature.- Everything else (
temperature,tools,response_formatand so on) is passed through unchanged.
The response is the standard completion object. The price of the call in US dollars is in the X-Dianthan-Cost-Usd header and in usage.cost.
Streaming
Set "stream": true. Server-sent events arrive exactly as the provider sends them, and the last event before [DONE] carries usage, including the cost. Usage is recorded when the stream ends.
Fallbacks
List alternatives in models, in order of preference. If the first model is unavailable, rate-limited or refuses the request, the next one answers it.
{
"model": "anthropic/claude-sonnet-5.5",
"models": ["openai/gpt-5", "google/gemini-2.5-pro"],
"messages": [{ "role": "user", "content": "Hello" }]
}The model field of the response tells you which one answered, and that is the one you are billed for.
Allow-lists and caps
Set them in the app's settings, from the dashboard or the API:
curl -X PATCH https://api.dianthan.com/v1/apps/app_…/settings \
-H "Authorization: Bearer <session>" -H "Content-Type: application/json" \
-d '{
"ai": {
"models": ["anthropic/claude-sonnet-5.5", "openai/gpt-5"],
"monthly_cap_usd": 200,
"user_monthly_cap_usd": 2
}
}'| Setting | Effect |
|---|---|
models | Only these ids may be called, including as fallbacks. Others get 403 model_not_allowed. |
monthly_cap_usd | Model spend for the whole app this calendar month (UTC). Past it, calls get 402 spend_cap_reached. |
user_monthly_cap_usd | The same, per user. Past it, that customer's calls get 402 user_cap_reached. |
Caps are checked before each call, so a single call can finish slightly over a cap; the next one is refused.
Usage and cost
curl "https://api.dianthan.com/v1/ai/usage?user=usr_x81…" -H "Authorization: Bearer dn_live_…"{ "month": "2026-11", "user": "usr_x81…", "spent_usd": 0.4182, "cap_usd": 2 }Leave out user for the whole app. Every call is also written to the app's event log as ai.call with the model, tokens, cost, latency, user and metric, so it can be read with GET /v1/logs?name=ai.call and appears in Observe.
Margin per customer
Dianthan takes your customers' payments through Billing and routes their model calls, so it can set one against the other. Send the customer's id as user on each call: a Dianthan user id, a billing customer id, or the customer's email all match.
curl "https://api.dianthan.com/v1/ai/margin?month=2026-11" -H "Authorization: Bearer dn_live_…"{
"month": "2026-11",
"totals": { "revenue_usd": 346, "model_cost_usd": 141.55, "margin_usd": 204.45,
"margin_percent": 59.1, "customers": 5, "unprofitable": 2, "unattributed_cost_usd": 0.27 },
"customers": [
{ "user": "usr_lumen…", "email": "ops@lumen.ai", "plan": "Pro", "revenue_usd": 49,
"credits_used_usd": 0, "model_cost_usd": 71.2, "margin_usd": -22.2, "margin_percent": -45.3 }
]
}Customers come least profitable first. Revenue is what each customer paid in the month: paid invoices where there are any, otherwise the monthly price of their live subscription, plus credits they used. Cost is the provider price of their calls. Calls made without a user are counted in unattributed_cost_usd. The same view is in the console under Customers, Margin.
Credits
Credits let customers prepay for model use, so heavy use pays for itself. Turn them on for an app with a markup:
curl -X PATCH https://api.dianthan.com/v1/apps/app_…/settings \
-H "Authorization: Bearer dn_live_…" -H "Content-Type: application/json" \
-d '{ "ai": { "credits": { "enabled": true, "markup_percent": 20 } } }'Then grant credits from your own payment flow, for example the webhook your checkout sends when a top-up succeeds. Negative amounts correct a balance.
curl -X POST https://api.dianthan.com/v1/ai/credits \
-H "Authorization: Bearer dn_live_…" -H "Content-Type: application/json" \
-d '{ "user": "usr_x81…", "amount_usd": 10, "note": "Top-up" }'With credits on, every call must name a user. A call is refused with 402 insufficient_credits once that user's balance is spent; otherwise it is charged at provider cost plus your markup, returned in the x-dianthan-charged-usd header, and counted as revenue in the margin view. Read a balance with GET /v1/ai/credits?user=….
Errors
| Status | error | Meaning |
|---|---|---|
| 400 | bad_request | Missing model or messages, or an invalid user. |
| 400 | upstream_error | The provider rejected the request; message says why. |
| 402 | spend_cap_reached / user_cap_reached | A monthly cap has been reached. |
| 402 | insufficient_credits | Credits are on and this user's balance is spent. |
| 403 | model_not_allowed | The model isn't in the app's allow-list. |
| 429 | upstream_error | The provider is rate-limiting; retry with backoff or add a fallback. |
| 502 | upstream_error | The provider failed. Add a fallback to absorb this. |
Pricing
Each call costs the provider's published price for the tokens it used, plus a fee stated on the pricing page. There is no separate subscription for Models.
In preview
- Your own provider keys. Use existing provider contracts while keeping caps, logs and fallbacks here.