Every error the Samsa API returns — for any endpoint, at any status — uses one
consistent JSON envelope. Parse the machine-readable code, not the human
message (messages may change; codes are stable).
The error envelope
{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "The provided API key is invalid, expired, or revoked.",
"request_id": "req_8f14e45fceea167a"
}
}
| Field | Description |
|---|
error.type | Broad category, e.g. authentication_error, credit_error, rate_limit_error. |
error.code | Stable, specific code you branch on (the table below). |
error.message | Human-readable explanation. For display and logs — do not parse it. |
error.request_id | Correlation id for this request, also returned as the X-Request-ID response header. Quote it when contacting support. |
error.param | Present on validation and scope errors — names the offending field or "scope". |
Successful responses never contain an error object. Branch on the HTTP status
first, then on error.code.
Error codes
| HTTP | code | type | Meaning |
|---|
| 401 | invalid_api_key | authentication_error | Missing, malformed, unknown, expired, or revoked key. |
| 403 | missing_scope | permission_error | Valid key lacks the endpoint’s scope. |
| 402 | insufficient_credits | credit_error | Organization credit pool is below the action cost. |
| 402 | subscription_inactive | credit_error | No usable organization subscription. |
| 404 | not_found | invalid_request_error | Unknown id, or a resource owned by another organization. |
| 422 | validation_error | invalid_request_error | Request body or parameters failed validation. |
| 429 | rate_limited | rate_limit_error | Per-key request-rate window exceeded. |
| 429 | too_many_active_jobs | rate_limit_error | Organization’s concurrent-job cap reached. |
| 500 | internal_error | api_error | Unexpected server error. |
invalid_api_key
HTTP 401. The Authorization header is missing or malformed, or the key is
unknown, expired, or revoked — or the key creator is no longer a member of the
organization. Expired and unknown keys are intentionally indistinguishable.
{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "The provided API key is invalid, expired, or revoked.",
"request_id": "req_8f14e45fceea167a"
}
}
How to handle. Check the Authorization: Bearer samsa_sk_… header. If the key
was revoked or expired, create a new one.
Do not retry — the outcome will not change.
missing_scope
HTTP 403. The key is valid but lacks the scope
the endpoint requires. param is "scope" and the message names the missing scope.
{
"error": {
"type": "permission_error",
"code": "missing_scope",
"message": "The API key is missing the required scope: images.generate.",
"request_id": "req_1c9a3b7d2e5f4a80",
"param": "scope"
}
}
How to handle. An admin edits the key’s scopes (or issues a new key) to grant the
named scope. Do not retry without changing the key.
insufficient_credits
HTTP 402. The organization’s spendable credit pool (subscription plus valid
top-ups) is below the action’s cost. See Pricing.
{
"error": {
"type": "credit_error",
"code": "insufficient_credits",
"message": "Insufficient credits available.",
"request_id": "req_2d5f4a801c9a3b7d"
}
}
How to handle. Top up or upgrade the organization’s plan in the app, then retry.
Nothing was charged and no job was created.
subscription_inactive
HTTP 402. The organization has no usable subscription (none active, in grace, or
holding spendable top-ups). Distinct from insufficient_credits, where a subscription
exists but the pool is too low.
{
"error": {
"type": "credit_error",
"code": "subscription_inactive",
"message": "No active subscription for this organization.",
"request_id": "req_4a801c9a3b7d2d5f"
}
}
How to handle. Reactivate billing for the organization in the app, then retry.
not_found
HTTP 404. The id is unknown, or it belongs to another organization. The two cases
are indistinguishable by design, so existence is never disclosed across organizations.
{
"error": {
"type": "invalid_request_error",
"code": "not_found",
"message": "The requested resource was not found.",
"request_id": "req_7d2d5f4a801c9a3b"
}
}
How to handle. Verify the id, and that the key’s organization owns the resource.
Do not retry.
validation_error
HTTP 422. The request body or a parameter failed validation. param names the
offending field; message explains the constraint.
{
"error": {
"type": "invalid_request_error",
"code": "validation_error",
"message": "aspect_ratio must be one of: 1:1, 4:3, 4:5, 16:9, 21:9, 9:16",
"request_id": "req_3b7d2d5f4a801c9a",
"param": "aspect_ratio"
}
}
How to handle. Fix the request per message and param, then resubmit. This is a
client error — retrying the same request will fail identically.
rate_limited
HTTP 429. The key exceeded its per-key request-rate window. The response carries
Retry-After and X-RateLimit-* headers. See Rate limits.
{
"error": {
"type": "rate_limit_error",
"code": "rate_limited",
"message": "API rate limit exceeded. Slow down and retry after the rate-limit window resets.",
"request_id": "req_5f4a801c9a3b7d2d"
}
}
How to handle. Back off and retry after the Retry-After interval. Use
exponential backoff for repeated 429s.
too_many_active_jobs
HTTP 429. The organization has reached its cap on concurrent in-flight jobs. The
message includes the current count and the limit, and a Retry-After header is set.
{
"error": {
"type": "rate_limit_error",
"code": "too_many_active_jobs",
"message": "Too many active jobs for this organization (5/5). Wait for in-flight jobs to finish before submitting more.",
"request_id": "req_801c9a3b7d2d5f4a"
}
}
How to handle. Wait for in-flight jobs to reach a terminal status (poll their
GET endpoint or use a webhook) before submitting more, then
retry after Retry-After.
internal_error
HTTP 500. An unexpected error on Samsa’s side. The response never leaks internal
details — the request_id is your handle for support.
{
"error": {
"type": "api_error",
"code": "internal_error",
"message": "An internal error occurred. Contact support with the request_id.",
"request_id": "req_9a3b7d2d5f4a801c"
}
}
How to handle. Retry with backoff — 500s are often transient. If it persists,
contact support@samsa.ai with the request_id.
Using request_id for support
Every error (and every success) carries a request_id, also returned as the
X-Request-ID response header. It ties your client-side error, the response header,
and Samsa’s server logs to one request. Log it, and quote it when you contact
support@samsa.ai — it is the fastest way for us to find
exactly what happened.
New error codes may be added over time (for example, additive per-endpoint codes).
Treat an unrecognized code the same as its HTTP status class, and always branch on
code rather than matching message text.