application/problem+json.
Branch on the code field — it’s stable. The title and detail are for
humans and may be reworded over time.
Shape
| Field | Notes |
|---|---|
type | URI describing the error class. Stable, dereferenceable. |
title | Human-readable summary. Don’t pattern-match on this. |
status | HTTP status code (mirrors the response status). |
code | Stable machine identifier. Branch on this. |
detail | Human-readable explanation of this particular error. |
instance | Request id. Also returned as the X-Request-Id response header. |
errors | Field-level errors (only on 422). |
instance (request id) — when you open a support ticket, that’s
the first thing we’ll ask for.
Code catalog
| HTTP | code | Meaning | Retry? |
|---|---|---|---|
400 | bad_request | Malformed request — invalid JSON, missing required body field, wrong method. | No. Fix the request. |
401 | unauthorized | Missing, malformed, expired, or revoked API key. | No. Re-issue the key. |
402 | insufficient_credits | Project balance is too low to cover the operation. Top up or upgrade. | No, until credits are added. |
403 | forbidden | Key is valid but lacks the required scope. | No. Re-issue with the missing scope. |
404 | not_found | Resource does not exist (or your key can’t see it). | No. |
409 | conflict | Generic conflict — e.g. slug already taken, key limit reached. | Sometimes. Read detail. |
409 | slot_unavailable | Booking slot was taken between availability check and create. | Yes — refresh availability. |
409 | idempotency_conflict | Idempotency key reused with different parameters. | No. Use a fresh key or match params. |
409 | connect_not_ready | Stripe Connect not onboarded; checkout cannot be created. | No, until Connect is set up. |
422 | validation_failed | One or more fields failed validation. See errors map. | No. Fix and resend. |
429 | rate_limited | Per-project rate limit exceeded. Honour Retry-After. | Yes — exponential backoff. |
500 | internal_error | Server bug. We’re paged. | Yes — exponential backoff, alert. |
502 | upstream_unavailable | Upstream AI provider error (model, image gen, etc.). Often transient. | Yes — exponential backoff. |
503 | service_unavailable | Maintenance or transient outage. Honour Retry-After. | Yes — exponential backoff. |
insufficient_credits
The most common failure for active projects. Returned whenever a write or
generation costs more credits than the project has on hand. The body
includes cost (credits the operation needs) and balance (credits the
project had on hand) so clients can render an exact top-up prompt:
content_update, page_update, image_register) as
well as the larger generations (image, blog, video, website).
Watch the credits_remaining field on GET /v1/projects/me/usage (or
react to a 402 in your client) to alert your team or auto-top-up before
mutating writes start failing.
validation_failed
Field-level errors are returned as an errors map; arrays of strings keyed by
the offending field. Mirror the keys back to the user.
rate_limited
The response carries Retry-After (seconds) and the standard rate-limit
headers. See rate-limits for the full backoff strategy.
slot_unavailable
A booking-specific 409: the slot was taken between the time you ran an
availability check and the time you posted the booking. Refresh availability
and let the user pick again. Don’t auto-retry.
Retry strategy
For retryable error classes, use exponential backoff with full jitter. Cap retries at 5 attempts; cap any single delay at 30 seconds.- Never retry
400,401,403,404,409(exceptslot_unavailable, which means “refresh and let the user choose”), or422. They are deterministic. - Always honour
Retry-Afterif present; the platform sets it precisely. - Long-running jobs are submitted via
202 Acceptedand tracked via/jobs/{id}. The submit call is idempotent if you pass anIdempotency-Key; the job itself is the right place to handle failures, not the submit endpoint.
Idempotency
POST and other mutating endpoints accept an Idempotency-Key header (any
unique string up to 255 chars). Retries with the same key within 24 hours
return the original response without re-executing the side effect.
409 idempotency_conflict. Use one key per
logical operation; UUIDs are fine.
When to ask for help
Open a ticket withinfo@neuraldraft.io or
the dashboard support widget and
include:
- The
instance(request id) — alsoX-Request-Idon the response. - The exact request URL and method.
- The response status and
code. - The approximate timestamp (UTC).