Error Codes
Complete reference for error envelope shape, HTTP status codes, and API-specific error codes returned by the Starship Rewards API
Error Codes
Every Starship API error is returned in a consistent envelope so your integration can route, log, and retry programmatically — without string-matching on messages.
Error Envelope
All non-2xx responses follow this shape:
{
"error": {
"name": "ValidationException",
"code": "E_VALIDATION_FAILURE",
"message": "Input validation failed.",
"messages": {
"amount": [
{
"rule": "required",
"field": "amount",
"message": "The amount field is required."
}
]
}
}
}| Field | Type | Description |
|---|---|---|
error.name | string | Stable machine identifier for the error class (e.g., ValidationException, NotFoundError). Safe to branch on. |
error.code | string | Short, UPPER_SNAKE_CASE code. The recommended primary field to branch on — see code table below. |
error.message | string | Human-readable summary. Suitable for logs; not guaranteed to be stable. Do not branch on this. |
error.messages | object (optional) | Present only on validation failures. Maps field name → array of { rule, field, message }. |
Never match on error.message. Messages are subject to wording changes. Always branch on error.code (or error.name). The API guarantees codes are stable; messages are not.
Code Table
Error codes grouped by HTTP status. Your integration should handle every code in a status class consistently (e.g., all 4xx are non-retriable; all 5xx may be retried with backoff).
400 Bad Request
| Code | Name | When it occurs |
|---|---|---|
E_VALIDATION_FAILURE | ValidationException | Request body failed schema validation. error.messages contains per-field details. |
VALIDATION_FAILURE | ValidationException | Single-field validation failure (legacy code; prefer E_VALIDATION_FAILURE). |
INVALID_INPUT | InvalidInputError | Input is syntactically valid but semantically rejected (e.g., unsupported currency). |
INVALID_STATUS | InvalidStatusError | The resource cannot transition to the requested status. |
INVALID_FEATURE | InvalidFeatureError | The feature is not enabled for your client account. Contact support. |
NOT_FOUND | ProductNotFoundException / DenominationNotFoundException | Referenced product or denomination doesn't exist. (Yes, 400 on these is historical — see note below.) |
BAD_REQUEST | BadRequestError | Generic malformed request. |
SYNTAX_ERROR | SyntaxError | Request body is not valid JSON. |
IDEMPOTENCY_KEY_REQUIRED | (see note) | Idempotency-Key header missing on a write endpoint that requires it. |
IDEMPOTENCY_KEY_TOO_SHORT | (see note) | Idempotency-Key shorter than 8 characters. |
IDEMPOTENCY_KEY_TOO_LONG | (see note) | Idempotency-Key longer than 256 characters. |
Idempotency errors use a slightly different envelope. The three IDEMPOTENCY_KEY_* errors currently return { error: { code, message, details } } (no name, no messages). Branch on error.code — it is stable across both envelope shapes. A future release will unify these on the standard envelope.
401 Unauthorized
| Code | Name | When it occurs |
|---|---|---|
E_UNAUTHORIZED_ACCESS | AuthenticationException | Credentials are missing, malformed, or revoked. Check X-API-Key / X-API-Secret or JWT. |
UNAUTHORIZED | UnauthorizedError | Generic authentication failure. |
INVALID_CREDENTIALS | InvalidCredentialsError | Credentials were supplied but were wrong. |
402 Payment Required
| Code | Name | When it occurs |
|---|---|---|
PAYMENT_REQUIRED | PaymentRequiredError | Wallet balance is insufficient to complete the order. |
403 Forbidden
| Code | Name | When it occurs |
|---|---|---|
FORBIDDEN_ACTION | ForbiddenActionError | Authenticated, but your API key does not have permission for this operation. |
FORBIDDEN | ForbiddenError | Generic authorization failure. |
404 Not Found
| Code | Name | When it occurs |
|---|---|---|
NOT_FOUND | NotFoundError | Generic resource not found. |
WALLET_NOT_FOUND | WalletNotFoundException | The wallet referenced does not exist for this client. |
USER_NOT_FOUND | UserNotFoundError | The client user referenced does not exist. |
RESOURCE_NOT_AVAILABLE | ResourceNotAvailableError | Resource exists but is unavailable for the requested operation. |
405 / 408 / 409 / 410
| Code | HTTP | Name | When it occurs |
|---|---|---|---|
METHOD_NOT_ALLOWED | 405 | MethodNotAllowedError | HTTP verb is not supported for this endpoint. |
OPERATION_TIMEOUT | 408 | OperationTimeoutError | Operation exceeded its internal timeout budget. Retriable with backoff. |
RESOURCE_CONFLICT | 409 | ResourceConflictError | State conflict (e.g., duplicate Idempotency-Key with different body). |
CONFLICT | 409 | ConflictError | Generic state conflict. |
GONE | 410 | GoneError | Resource existed but has been permanently removed. Do not retry. |
429 Too Many Requests
Throttling responses do not use the code / name envelope the other errors on this page use. The rate limiter returns its own flat shape:
{
"error": "rate_limit_exceeded",
"message": "Rate limit exceeded. Please retry after 30 minutes or contact support to increase your limit.",
"retry_after": 1800
}| Field | Value |
|---|---|
error | Always the literal string rate_limit_exceeded — branch on this |
message | Human-readable, varies by rule, rounds to whole minutes. Do not parse. |
retry_after | Seconds until the window reopens. Mirrors the Retry-After header. |
Honour Retry-After. See Rate Limits for every enforced limit.
500 / 503
| Code | HTTP | Name | When it occurs |
|---|---|---|---|
INTERNAL_SERVER_ERROR | 500 | InternalServerError | Unhandled server error. Safe to retry idempotent reads; use an Idempotency-Key for writes. |
INTERNAL_DATABASE_ERROR | 500 | InternalDatabaseError | Database layer failure. Retriable. |
SERVICE_UNAVAILABLE | 503 | ServiceUnavailableError | A downstream dependency is unavailable (typically a vendor API during fulfillment). Retriable. |
Retry Guidance
| Status | Retry? | Strategy |
|---|---|---|
| 4xx (except 408/429) | No | Fix the request before retrying. |
| 408 Operation Timeout | Yes | Exponential backoff, start at 2 s, max 5 attempts. |
| 429 Too Many Requests | Yes | Honor Retry-After header; if absent, exponential backoff from 1 s. |
| 500 / 503 | Yes | Exponential backoff starting at 1 s, max 3 attempts. Writes must carry the same Idempotency-Key. |
| Network timeout / connection reset | Yes | Same as 5xx. Treat as "request possibly succeeded" for writes — use idempotency. |
Idempotency is your friend. For any POST/PUT/PATCH retry, reuse the original Idempotency-Key. The server will return the cached response instead of processing the request twice. See the Idempotency guide.
Validation Error Details
For E_VALIDATION_FAILURE, the error.messages object gives you per-field reasons. Each entry is an array (a field can fail multiple rules):
{
"error": {
"name": "ValidationException",
"code": "E_VALIDATION_FAILURE",
"message": "Input validation failed.",
"messages": {
"email": [
{ "rule": "required", "field": "email", "message": "The email field is required." }
],
"amount": [
{ "rule": "gt", "field": "amount", "message": "The amount field must be greater than 0." },
{ "rule": "numeric", "field": "amount", "message": "The amount field must contain only numeric values." }
]
}
}
}Common validation rules you'll see in rule:
| Rule | Meaning |
|---|---|
required | Field is missing or empty. |
min / max | String length out of bounds. |
len | String length not exactly N. |
gt / gte / lt / lte | Numeric comparison failure. |
numeric | Value is not numeric. |
email | Value is not a valid email. |
Security & Information-Disclosure
Starship sanitizes error messages before returning them to clients — SQL errors, stack traces, credential fragments, and internal connection details are never leaked. If you ever see one, report it to hello@rocketincentive.com immediately.
Next Steps
- Request Headers — what to send on every request
- Authentication — API key and HMAC setup
- Idempotency Guide — safe retries for writes