Errors
The SpAItial Developer API uses conventional HTTP status codes and returns a stable JSON envelope on every error.
Error shape
All errors emitted by the API share the same shape:
{
"error": {
"code": "INVALID_INPUT",
"message": "Human-readable description of what went wrong.",
"request_id": "optional trace id",
"details": { "optional": "structured context" }
}
}
Always branch on error.code, not on the human-readable message. The code field is stable.
HTTP status codes
| Status | Meaning | Typical error.code |
|---|---|---|
400 | Malformed request — bad JSON, missing required field, wrong content type. | INVALID_INPUT |
401 | Missing, malformed, or revoked API key. | UNAUTHORIZED |
402 | Account does not have enough generation credits. details has required_credits and available_credits. | INSUFFICIENT_CREDITS |
403 | Authenticated but the key lacks the required scope, or moderation blocked the input. | FORBIDDEN, MODERATION_REJECTED |
404 | Resource does not exist (e.g. unknown request_id, file_id, or panorama_id). | REQUEST_NOT_FOUND, FILE_NOT_FOUND, PANORAMA_NOT_FOUND |
409 | Idempotency key was reused with a different payload, the requested world/artifact is not ready yet, or a resource is still processing and can't be deleted. | IDEMPOTENCY_KEY_REUSED, RESOURCE_NOT_READY, RESOURCE_NOT_DELETABLE |
410 | Short-lived resource has expired. | PANORAMA_EXPIRED |
422 | Payload parsed but semantically invalid (validation rule violated). | VALIDATION_FAILED |
429 | Rate limit exceeded, or too many of your worlds are already generating — see Rate Limits. | RATE_LIMIT_EXCEEDED, WORLD_USER_LIMIT |
503 | World generation intake is paused or temporarily unavailable. Retry later. | WORLD_INTAKE_PAUSED, WORLD_INTAKE_UNAVAILABLE |
5xx | Unexpected server error. Safe to retry with backoff. | INTERNAL_ERROR, EDIT_FAILED |
Retry strategy
4xxresponses are not retryable by themselves — fix the request first. The exceptions are409 RESOURCE_NOT_READY(poll again later),409 RESOURCE_NOT_DELETABLE(cancel the request, or wait for it to finish, then delete again),409 IDEMPOTENCY_KEY_REUSED(resolve the collision and pick a new key),429 RATE_LIMIT_EXCEEDED(wait until the window resets — the response includesRetry-After), and429 WORLD_USER_LIMIT(wait for one of your in-flight worlds to finish).5xxresponses are retryable. Use exponential backoff with jitter, capped at a reasonable ceiling (for example 30s). Pair retries with anIdempotency-Keyon mutating endpoints so retries do not produce duplicates.
Panorama edit errors
Panorama editing adds a few resource-specific codes:
| Code | HTTP | Meaning |
|---|---|---|
PANORAMA_NOT_FOUND | 404 | The pano_... does not exist or is not owned by the caller. |
PANORAMA_EXPIRED | 410 | The edited panorama has passed its 24-hour TTL. |
EDIT_FAILED | 503 | The upstream panorama edit failed after the request was accepted. Retry with a fresh or idempotent request. |
Validation errors
400 INVALID_INPUT responses for a malformed body, path parameter, or query parameter name the failing fields in message and list every issue in details.issues. Use it to surface actionable feedback to your users without re-implementing the schema check.
{
"error": {
"code": "INVALID_INPUT",
"message": "Validation failed: webhook.url: Invalid URL",
"details": {
"issues": [{ "path": "webhook.url", "message": "Invalid URL" }]
}
}
}