API reference
Error codes
The uniform error envelope, every machine-readable code Jobby.dev returns, and which are safe to retry.
Last updated
Every non-2xx response carries the same JSON envelope. The code field is the stable, machine-readable identifier — branch on it, not on the human-readable error string.
The envelope
{
"error": "Off-session charges require one-time consent. ...",
"code": "OFF_SESSION_CONSENT_REQUIRED",
"details": { "target_plan": "pro", "interval": "monthly" }
}error— always present, human- readable, may include guidance for the user.code— present on every error v1 generates. Stable identifier; the v1 contract guarantees we don't rename codes once shipped.details— optional, code-specific. Validation errors carry a flattened zod-style report; off-session consent errors carry a fresh checkout URL; SCA errors carry the payment-intent ID. Document per-code, but always JSON-serializable.
Codes
The full list, with HTTP status and whether retry is safe:
| Code | HTTP | Retryable? | Meaning |
|---|---|---|---|
VALIDATION | 400 | no | Request body / query parameters failed schema validation. |
INVALID_TOKEN | 401 | no | The bearer token is missing, malformed, or revoked. |
INSUFFICIENT_SCOPE | 403 | no | The token is valid but doesn't carry the required scope. |
PLAN_GATED | 403 | no | The endpoint is gated to a higher plan tier than the caller has. |
NOT_FOUND | 404 | no | Resource doesn't exist OR caller isn't the owner. Existence isn't leaked across tenants. |
ALREADY_ON_PLAN | 409 | no | Off-session upgrade attempted on the plan/interval the user is already on. |
SUBSCRIPTION_NOT_ELIGIBLE | 409 | no | Subscription is in a state (incomplete, past_due, canceled) that doesn't allow the requested action. |
OFF_SESSION_CONSENT_REQUIRED | 402 | no | Off-session charge requires prior consent. The error details carry a one-shot hosted-checkout URL. |
ACTION_REQUIRED_NO_URL | 402 | no | Bank requires 3DS / SCA but Stripe didn't return a redirect URL. Use the billing portal as a fallback. |
RATE_LIMITED | 429 | yes (with back-off) | Per-token rate limit exceeded. Retry-After header carries the wait. |
DEPENDENCY_UNAVAILABLE | 503 | yes (with back-off) | Upstream provider (Stripe, Daily, Anthropic, Supabase) is temporarily unreachable. Safe to retry with back-off. |
INTERNAL | 500 | no | Unexpected server error. Already logged to Sentry; please open a support ticket if persistent. |
Retryability
Codes marked yes (with back-off) are safe to retry with exponential back-off bounded by the retry-after / rate-limit signal. Codes marked no represent application-level decisions; retrying without changing the request will get the same error.
Idempotency
Mutating endpoints accept an optional Idempotency-Key header (UUID v4). If the same key is replayed within 24 hours, the original response is returned without re-applying the side effect. Use this for any retry loop on a non-idempotent endpoint (off-session charge, match decline, event create).
Error mapping in SDKs
Both the JS and Python SDKs throw a typed JobbydevError on non-2xx responses, carrying code, status, details, and retryAfterSeconds. Branch on error.code, never on error.message (the human string can change).