Errors
Every error tells you whether it is your fault, ours, or worth retrying.
Envelope
Endpoints under /api/v1 always return one of two shapes.
{ "success": true, "data": { … }, "message": "Bundle created successfully" }{ "success": false, "error": { "code": "VALIDATION_ERROR", "message": "Validation failed" } }error.code is stable and safe to branch on. error.message is for humans and may change.
Every response, success or error, carries an X-Request-Id header. Include it when you contact support.
Codes
| HTTP | code | Meaning | Retry? |
|---|---|---|---|
| 400 | VALIDATION_ERROR | Request body failed schema validation. | No. Fix the body. |
| 400 | QUERY_VALIDATION_ERROR | A query parameter is out of range or unknown. | No. |
| 400 | INVALID_BUNDLE_ID | Path id is not a positive integer. | No. |
| 400 | INVALID_ACCOUNT_ID | A stripe-account header was sent but does not start with acct_. | No. |
| 400 | UNSUPPORTED_API_VERSION | An api-version header was sent that is not v1. | No. |
| 400 | CARD_ERROR | The payment method was declined. | Only with a different card. |
| 400 | INVALID_REQUEST | Stripe rejected the request. | No. |
| 401 | AUTHENTICATION_ERROR | The connected Stripe account could not be authenticated. | No. |
| 403 | (gateway) | Missing, unknown, frozen or deleted API key. Body is { "message": "Forbidden" }. | No. |
| 404 | NOT_FOUND | No resource with that id. | No. |
| 404 | ROUTE_NOT_FOUND | The path does not exist. Check the base URL and version. | No. |
| 429 | RATE_LIMIT_ERROR | Too many requests. | Yes, after Retry-After. |
| 500 | INTERNAL_ERROR | Something broke on our side. | Yes, with backoff. |
| 500 | API_ERROR | Stripe returned an error. | Yes, with backoff. |
| 503 | CONNECTION_ERROR | Stripe was unreachable. | Yes, with backoff. |
Quote and certificate endpoints
The action-oriented endpoints under /public-quote and /coverport-agency-certs predate the envelope. They return 200 with a JSON body on success. On failure they return a 500 or 502 whose body is the underlying error serialised as JSON. Treat any non-200 from these endpoints as a failure, log the body, and retry idempotent reads. Do not retry a processUserDirectStripePayment call without first checking whether the payment went through.
Idempotency
POST /api/v1/products and POST /api/v1/bundles are not idempotent. If a create call times out, list the resource before retrying so you do not create a duplicate.