Errors
Two error shapes, six status codes, and how to handle each.
Errors are the one place this API is not uniform, so it's worth being precise about it.
Two error shapes
Which body you get depends on which layer rejected the request. Authentication and routing failures are handled by the framework and return a detail field; everything else is returned by the view and uses the same status/message envelope as a success.
Framework errors — authentication, routing, method
{
"detail": "Invalid API key"
}View errors — validation, missing objects, business rules
{
"status": "failed",
"message": "Workspace not found"
}Read message first and fall back to detail. That one line covers every error this API produces.
Status codes
Six codes, and two you will not see:
| Code | Meaning | How to handle it |
|---|---|---|
400 | Bad request | Validation failed, or a required parameter is missing. The message names the field. Don't retry without changing the request. |
402 | Payment required | The account behind this API key has run out of credits. Every AI action spends them — uploading, chatting, retrying — so nothing that reaches the model will succeed until it is topped up. Retrying the same request does not help. |
403 | Forbidden | The API key is missing or invalid. Note this is 403, not the 401 most APIs return. Check the header before assuming a permissions problem. |
404 | Not found | The object doesn't exist, or belongs to another app. The API deliberately doesn't distinguish the two. |
405 | Method not allowed | The path exists but not for that verb. Usually a missing trailing slash turned a POST into a redirected GET. |
500 | Server error | Something failed on our side. Safe to retry once with backoff; if it persists, tell us the time and the request. |
There is no rate limiting on this API yet, so you will not see a 429 — and you should not rely on the absence of one. Put your own ceiling in front of it, especially if you proxy end-user traffic through it.
Nothing returns 409. Creates are never rejected as duplicates.
400
{
"status": "failed",
"message": "{'external_user_id': [ErrorDetail(string='This field is required.', code='required')]}"
}Handling errors
A minimal client that gets this right on every endpoint looks like this — note that fetch resolves on a 4xx, so checking the promise is not enough:
# -f makes curl exit non-zero on a 4xx/5xx, and -sS keeps the message.
curl -fsS "https://api.daneshyar.info/api/v1/workspaces/" \
-H "X-API-Key: dk_live_9f3aC2xQ7mB1vT8sE4nK6pR0jY5wZ2hL" \
|| echo "request failed"