Errors
HTTP status codes, the error envelope, and every error code the Open Network API returns.
The API uses standard HTTP status codes. When a request fails, the body has success: false, a
message for people and, for most failures, a machine-readable code.
{
"success": false,
"message": "This session quoted 90000 (minor units, BDT); you reported 9000. The order has NOT been marked paid.",
"code": "payment_amount_mismatch",
"details": { "expectedAmountMinor": 90000, "reportedAmountMinor": 9000 },
"requestId": "3f0c9f8e-6a1d-4f5e-9b1a-2f1c0d9e7a41"
}Status codes#
| Status | Meaning |
|---|---|
200 | Success. |
201 | A catalogue item was created. |
400 | The request is invalid — a missing or malformed field, or malformed JSON. |
401 | The API key is missing, malformed, unknown, revoked or expired. |
403 | The key is valid but lacks the scope the endpoint needs. |
404 | The resource does not exist, or it is not yours. |
409 | The request conflicts with the resource's current state. |
413 | The request body is larger than 1 MB. |
429 | Too many requests. See Rate limits. |
500 | An unexpected error on Localoy's side. Retry with backoff. |
503 | A dependency is unavailable (returned by GET /health). |
Not yours looks like not found
A request for another partner's resource answers exactly like one for a resource that does not exist. The API never confirms that an ID belongs to someone else.
Error codes#
Authentication#
| Code | Status | Meaning |
|---|---|---|
open_network_key_invalid | 401 | The key is missing, malformed, unknown, has the wrong secret, is revoked or has expired. |
open_network_scope_required | 403 | The key lacks the endpoint's scope. details.requiredScope and details.grantedScopes say which. |
Catalogue#
| Code | Status | Meaning |
|---|---|---|
open_network_item_not_found | 404 | No catalogue item of yours has that externalId. |
open_network_bookable_not_found | 404 | localoyRef points at an ID that is not one of your bookables. |
open_network_bookable_already_linked | 409 | Another of your items is already linked to that bookable. details.externalId names it. Unlink it first. |
external_id_immutable | 400 | A PATCH tried to change externalId. Delete the item and create it under the new ID. |
Payments#
| Code | Status | Meaning |
|---|---|---|
payment_session_not_found | 404 | No session of yours, in this key's environment, has that ID. |
payment_unreportable_status | 400 | status is not one of SUCCEEDED, FAILED, CANCELLED, PROCESSING. |
payment_amount_required | 400 | A SUCCEEDED result was sent without amountMinor. |
payment_invalid_transition | 409 | The session cannot move from its current status to the one reported. details.from and details.to say which. |
payment_amount_mismatch | 409 | The amount you charged differs from the session's. The order was not marked paid. |
payment_currency_mismatch | 409 | The currency differs from the session's. The order was not marked paid. |
payment_subject_not_chargeable | 409 | The order was already paid or refunded outside this session. details.paymentStatus says how. |
payment_session_conflict | 409 | Another request changed the session at the same moment. Read it back, then decide whether to retry. |
Validation errors without a code#
Invalid input on a single field is answered 400 with a message that names the field and the rule,
and no code — for example:
{
"success": false,
"message": "priceCents must be between 0 and 2147483647",
"requestId": "…"
}These are bugs in the calling code, not conditions to handle at run time. Log the message and fix the
request. The same applies to Malformed JSON body (400) and Request body is too large (413).
Handling errors#
| Status | Retry? |
|---|---|
400, 401, 403, 404 | No. Fix the request, the key or the ID first. |
409 | Not as is. Read the resource's current state, then decide. |
429 | Yes, after the Retry-After header's number of seconds. |
500, 503, network errors | Yes, with exponential backoff. For payment writes, send the same Idempotency-Key; see Idempotency. |