Errors
Every failed request returns the same JSON body.
{
"trace_id": "3f2504e0-4f89-11d3-9a0c-0305e82c3301",
"error_code": "devapi_not_found_error",
"message": "Not Found",
"details": "The customer does not belong to this partner"
}
| Field | What it is |
|---|---|
trace_id | Identifier of this request. Keep it: support asks for it first |
error_code | Machine-readable code, stable across releases. Branch on it |
message | Short explanation for a human. The wording changes without notice, do not match on it |
details | Extra context: a string, a list of field errors, or no key at all when there is nothing to add |
Status codes
| Status | Meaning | What to do |
|---|---|---|
400 | The request is well-formed, but these values cannot be used | Read message and fix the request |
401 | The API key is missing or unknown | Send a valid X-Api-Key |
403 | The key is valid, the action is not allowed for it | Do not retry; the action needs a different account |
404 | The object does not exist or belongs to another partner | Check the UUID against your own customers |
405 | The method is not allowed on this path | Fix the request |
409 | The object is in a state that does not allow the action | Read the object, act on its current state |
422 | The body or a header failed validation | Read details; the list names every invalid field |
500 | A failure on our side | Retry with backoff; if it repeats, send support the trace_id |
503 | The service is temporarily unavailable | Retry with backoff |
Codes
error_code | HTTP | When |
|---|---|---|
devapi_internal_error | 500 | Unexpected failure inside Stablegate |
devapi_request_validation_error | 422 | The body or a header failed validation |
devapi_iban_parse_error | 400 | The IBAN cannot be parsed |
devapi_bank_code_required_error | 400 | The IBAN does not carry a BIC; pass bank_code explicitly |
devapi_bad_request_error | 400 | The values contradict each other, for example both client_account and client_account_uuid |
devapi_email_occupied_error | 400 | The email or username belongs to an existing customer |
devapi_not_found_error | 404 | The object does not exist or is not yours |
devapi_unauthorized_error | 401 | The API key is missing or unknown |
devapi_forbidden_error | 403 | The action is not allowed for this key |
devapi_method_not_allowed_error | 405 | The method is not allowed on this path |
devapi_customer_close_error | 409 | A customer can be closed only in the new state with no verification submitted |
devapi_download_aol_error | 409 | The account opening letter is not available in the current state |
devapi_kyc_application_link_error | 400, 409 | Verification by link is not configured, or the application is in a state that does not allow the action |
devapi_instant_verification_error | 400, 409 | Instant verification is not configured, not available for this customer type, or the application is in a state that does not allow the action |
devapi_idempotency_invalid_key | 422 | Idempotency-Key has non-printable characters or is longer than 255 |
devapi_idempotency_key_misuse | 422 | The key was already used with a different body or path |
devapi_idempotency_request_in_progress | 409 | A request with this key is still running; retry when it finishes |
devapi_service_unavailable_error | 503 | A service Stablegate depends on is down |
Validation errors
A 422 lists every invalid field in details:
{
"trace_id": "3f2504e0-4f89-11d3-9a0c-0305e82c3301",
"error_code": "devapi_request_validation_error",
"message": "Unprocessable Entity",
"details": [
{
"loc": ["body", "source", "amount"],
"msg": "Input should be a valid decimal",
"type": "decimal_parsing"
},
{
"loc": ["body", "destination", "client_account_uuid"],
"msg": "Field required",
"type": "missing"
}
]
}
loc is the path to the field: the first item is where the value came from (body, query, header), the rest is the path inside it.
Codes raised by other services
Requests that touch orders, limits, accounts, customers or verification go through internal services, and their codes reach you through the Partner API: oa_ for operations, ca_ for customers. An example is oa_limit_exceeded_error, which check-limits and order creation return when a rule is broken, with the reason in details.
Treat these codes as opaque. Log the code and the trace_id, show the customer message, and send both to support when the problem needs us.
Retries
- Retry
500,503and timeouts with exponential backoff. Three attempts is enough for an interactive flow; a background job can keep going longer. - Send the same
Idempotency-Keyon every retry of the same request: a repeated create returns the first result instead of a second order. A retry with a different body answers422withdevapi_idempotency_key_misuse. - Do not retry
400,401,403,404and422unchanged: the same request fails the same way. 409may go away when the object changes state, so re-read the object before you decide.- There is no rate limit today. If one appears, it will answer
429withRetry-After.
The request id header
Every response carries X-Request-Id. Send your own value in the request header and Stablegate returns it unchanged; without it the platform generates one. The trace_id of an error body is the same value, so the header alone identifies the call even when the body never reaches you — for example a gateway timeout.
curl https://api.stablegate.com/v1/customers/ \
-H "X-Api-Key: YOUR_API_KEY" \
-H "X-Request-Id: order-c1d2e3f4-42"
Before you contact support
Send the trace_id, the method and path of the request, and the time with the time zone. The trace_id finds the request in our logs, everything else narrows the search.
Ready to integrate?
For LLM Agents
Machine-readable Stablegate resources for LLM coding agents: llms.txt, llms-full.txt, the OpenAPI specification and Markdown copies of pages.
Quickstart
Seven requests take a new customer from nothing to a paid-out on-ramp order. Each step links to the page that explains the details; the bodies here are the shortest working form.