Skip to content

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.

TypeScript
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:

Terminal
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"
  }'
  • model is a model id in provider/model form. GET /v1/ai/models lists every id available to the app, with its context length and price per token.
  • user names 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.
  • metric is an optional label of your own, such as ai.summary, so you can see cost per feature.
  • Everything else (temperature, tools, response_format and 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.

JSON
{
  "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:

Terminal
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
    }
  }'
SettingEffect
modelsOnly these ids may be called, including as fallbacks. Others get 403 model_not_allowed.
monthly_cap_usdModel spend for the whole app this calendar month (UTC). Past it, calls get 402 spend_cap_reached.
user_monthly_cap_usdThe 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

Terminal
curl "https://api.dianthan.com/v1/ai/usage?user=usr_x81…" -H "Authorization: Bearer dn_live_…"
JSON
{ "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.

Terminal
curl "https://api.dianthan.com/v1/ai/margin?month=2026-11" -H "Authorization: Bearer dn_live_…"
JSON
{
  "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:

Terminal
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.

Terminal
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

StatuserrorMeaning
400bad_requestMissing model or messages, or an invalid user.
400upstream_errorThe provider rejected the request; message says why.
402spend_cap_reached / user_cap_reachedA monthly cap has been reached.
402insufficient_creditsCredits are on and this user's balance is spent.
403model_not_allowedThe model isn't in the app's allow-list.
429upstream_errorThe provider is rate-limiting; retry with backoff or add a fallback.
502upstream_errorThe 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.