API reference
Errors
Every Dhanu API error uses one envelope with a machine-readable code. Here is the full list and what to do about each.
Every failed request returns the same shape:
{
"error": {
"code": "invalid_api_key",
"message": "Missing or unknown API key"
}
}
Branch on code, not on message, messages are written for humans and may be
reworded. The tables below are rendered from the same catalogue the server
checks its responses against, so a code that can reach you is listed here.
Authentication and access
| Code | Status | What happened | What to do |
|---|---|---|---|
invalid_api_key | 401 | The bearer key is missing or unknown. | Check the Authorization header. |
api_key_revoked | 401 | The key was revoked. | Create a new key. |
api_key_expired | 401 | The key passed its expiry date. | Create a new key. |
api_key_ip_not_allowed | 403 | The caller's IP is outside the key's allowlist. | Call from an allowed address, or widen the key's allowlist. |
developer_access_revoked | 403 | The key's creator lost developer access in the workspace. | Restore their access, or issue a new key. |
api_key_project_scope | 403 | The key covers no active projects, or projectId is outside its scope. | Fix the key's project scope. |
api_key_operation_scope | 403 | The key's capability scope does not cover this operation. | Widen the key's scope, or use a broader key. |
Request problems
| Code | Status | What happened | What to do |
|---|---|---|---|
not_found | 404 | No such resource in the key's workspace. | Check the id and that the key belongs to the same workspace. |
channel_route_not_found | 404 | No staff member in the key's workspace has that trigger address. | Copy the address from the staff member's Channels tab. |
invalid_request_body | 422 | The body did not match the schema; error.issues names each field. | Fix the listed fields. |
invalid_cursor | 422 | The cursor was not one this endpoint issued. | Pass nextCursor back verbatim. |
invalid_idempotency_key | 400 | Idempotency-Key must be 1 to 255 characters. | - |
idempotency_key_reused | 409 | The same Idempotency-Key was sent with a different body. | Use a new key for each distinct request. |
idempotency_request_in_progress | 409 | The first request with this Idempotency-Key is still running. | Retry shortly. |
project_id_required | 422 | A multi-project key called without projectId. | Pass projectId, or use a single-project key. |
invalid_generation_prompt | 422 | The prompt was empty or longer than 2000 characters. | - |
invalid_image_aspect_ratio | 422 | The aspect ratio is outside the five supported values. | - |
invalid_workflow_values | 422 | The workflow values do not satisfy the workflow's fields; error.issues names each one. | Fix the listed fields. |
file_too_large | 413 | The file is larger than the direct-upload limit. | Use the presigned multipart upload for large files. |
unsupported_media_type | 415 | The file type is not accepted. | - |
unsupported_export_format | 422 | That file kind cannot be exported in the requested format. | Pages export as html, sheets as csv or xlsx, decks as pdf or pptx. |
export_not_rendered | 409 | The deck's PDF has not been rendered yet. | POST /files/{fileId}/render, wait for the job, then export again. |
video_not_ready | 409 | The video is still rendering, or failed. | Poll the job or wait for the video.ready webhook. |
Billing and limits
| Code | Status | What happened | What to do |
|---|---|---|---|
key_spend_limit_reached | 402 | The key reached its monthly spend cap. | Raise the cap in Developers → API keys, or wait for next month. |
storage_quota_exceeded | 507 | The workspace's storage quota is full. | Free space or raise the quota in Settings → Storage. |
insufficient_credits | 402 | The workspace credit balance is zero or below. | Top up in Settings → Billing. |
rate_limited | 429 | Too many requests for this workspace. | Back off for Retry-After seconds. |
Server
| Code | Status | What happened | What to do |
|---|---|---|---|
internal_error | 500 | Something failed on our side. | Retry with backoff and quote the requestId to support. |
Request IDs
Every response carries a request id, and it is included in the error envelope.
You can supply your own with the x-request-id header, it must match
[A-Za-z0-9._~-]{8,128}, otherwise Dhanu generates one, and it is echoed back on
the response.
Include the request id when you contact support. It is the fastest way for us to find what happened. You can also look it up yourself: Developers → Requests in the console keeps every call made with your keys for 30 days, with its status, latency, cost and redacted request and response bodies.
Handling errors with the SDK
The SDK throws DhanuError, carrying the same code plus the request id:
import { DhanuError } from "dhanu";
try {
await dhanu.images.generate({ prompt: "…" });
} catch (err) {
if (err instanceof DhanuError) {
console.error(err.code, err.status, err.message, err.requestId);
}
}