Skip to main content

Error Response Format

Upwell API errors are flat JSON objects — a message string, sometimes with a machine code alongside it:
There is no nested error envelope, no details object, and no timestamp field. Parse error as the human-readable message and treat code (when present, e.g. constraint-violation, bad-request) as the machine-readable category.
One exception: a failed authentication (401) returns an empty body — don’t assume every error response parses as JSON.

HTTP Status Codes

The codes you will actually encounter: Two codes conspicuously absent, on purpose:
  • 201 is not used — successful creates return 200.
  • 429 is not used — see Rate limiting below.

Business errors inside a 200

The approval action endpoints report failure in the response body, not the HTTP status. A POST /api/rest/invoices/{id}/approve that can’t proceed still returns 200:
Always check the nested success / error fields on action endpoints — checking only the HTTP status will miss these.

Real error messages and their fixes

The messages you’ll actually see in 400 responses, and what each one means:

Not-found semantics

A lookup by an id that doesn’t exist — deleted, mistyped, or belonging to a different organization — is not an HTTP error. The single-resource GETs declare a nullable resource and return it inside a 200:
List endpoints likewise return empty arrays. So:
  • Check the body, not just the status, when verifying a record exists.
  • A real 404 means the path is wrong, not that the record is missing.
  • Remember Upwell ids are prefixed strings (cus_…, inv_…, cari_…) — a truncated or unprefixed id is a common cause of “exists in the dashboard but null over the API.”
  • To find records by your own keys instead, use the POST /<resource>/search endpoints (see Integration patterns).

Rate limiting

The Upwell API does not currently enforce rate limits. There are no 429 responses, no X-RateLimit-* headers, and no Retry-After header — don’t build handling machinery for them. If you do observe a 429, it originated from infrastructure on your side (a proxy or gateway), not from Upwell. That’s a description of today’s behavior, not an invitation: keep client concurrency bounded and batch with the bulk-* endpoints where they exist. If limits are introduced, they’ll be announced in the changelog.

What is safe to retry

  1. Network failures and 5xx — retry with exponential backoff and jitter.
  2. POST retries need per-resource care. A retried create is not uniformly safe: customers/shipments/invoices reject duplicates with 400 Uniqueness violation, carriers silently no-op, and carrier invoices deduplicate asynchronously — read idempotent upserts before writing retry logic.
  3. 400 is never retryable as-is — the request itself is wrong; fix the payload.
  4. 401 is not retryable — fix the credential.

Getting Help

If an error doesn’t match anything on this page:
  1. Capture the full exchange — endpoint, HTTP method, request body (redact the API key), the complete response body, and the timestamp.
  2. Note the last 4 characters of the API key you used (never send the full key).
  3. Contact support at support@upwell.com.
For platform-level troubleshooting beyond API errors, see the Troubleshooting guide.