Skip to content

Errors

Every LithosPOS API failure carries a stable machine-readable code alongside a human-readable message. Branch on code. Messages are translated server-side into the locale you send in Accept-Language and their wording changes without notice; codes do not.

The developer API and the partner API are different services and return different shapes.

403 Forbidden
{
"error": {
"code": "developer.grant_missing",
"message": "This app is not authorised for merchant 104271.",
"params": { "companyId": 104271 }
}
}
FieldTypeNotes
error.codestringStable identifier. Always present
error.messagestringLocalised, display-ready
error.detailsobject | arrayField-level validation detail when applicable
error.paramsobjectInterpolation values used to build the message
403 Forbidden
{
"status": "error",
"statusCode": 403,
"timestamp": "2026-07-29T09:14:22.481Z",
"path": "/v1/items",
"message": "Your app does not have the items.write scope.",
"code": "partner.scope_denied",
"params": { "scope": "items.write" },
"correlationId": "f2b1c8a4-9d13-4f77-8c0e-2a5b6e0d3417"
}
FieldTypeNotes
statusstringAlways the literal "error"
statusCodenumberMirrors the HTTP status
timestampstringISO 8601, server clock
pathstringRequest path
messagestringLocalised. 5xx messages are deliberately generic
codestringPresent for all documented failures; absent for unclassified errors
paramsobjectInterpolation values
correlationIdstringUUID. Log it — support lookups start here
CodeStatusMeaning
developer.email_not_verified403Sign-in attempted before the verification link was opened
developer.too_many_attempts429Repeated failed sign-ins — the account is locked for a short window
developer.account_suspended403The developer account is suspended; contact the program team
developer.refresh_reuse_detected401A refresh token was presented twice — the whole session family is revoked; sign in again
developer.nda_required403Account has not submitted a program application
developer.nda_already_submitted409A pending application already exists
developer.nda_not_approved403Program application is still pending or was declined
developer.agreement_required403Developer agreement not signed
developer.agreement_version_stale409Signature targets an agreement version that is no longer active — reload and sign the current one
CodeStatusMeaning
developer.not_org_member403The account is not a member of that organization
developer.insufficient_org_role403Your role in the organization cannot perform this action
developer.owner_immutable409The organization owner cannot be removed or downgraded
developer.invite_invalid400The invitation link is malformed or was already used
developer.invite_expired400The invitation link expired — ask for a new invite
CodeStatusMeaning
developer.product_immutable409An app’s product cannot change after creation
developer.app_archived403The app is archived; credentials no longer mint tokens
developer.app_not_approved403Production access attempted before review approval
developer.review_not_in_review409Review action attempted from the wrong state
developer.sandbox_limit409The organization already has three active sandbox merchants
developer.sandbox_country_unknown400The country code is not a supported ISO 3166-1 alpha-2 value
CodeStatusMeaning
developer.invalid_client401Unknown client id, wrong secret or revoked credential — one code for all three, by design
developer.merchant_required400merchant_id omitted and not inferable
developer.merchant_not_found404merchant_id does not exist
developer.merchant_inactive403The merchant exists but is not active
developer.grant_missing403No active grant links this app to that merchant
developer.env_mismatch403Sandbox credential against a live merchant, or a production credential against a demo one
developer.region_unconfigured503The merchant’s region has no API base URL configured — contact support

These tables cover the codes an integration will realistically encounter; console-only flows (email verification, password reset, profile) carry additional developer.* codes that follow the same envelope.

CodeStatusMeaningFix
partner.scope_denied403The route needs a scope your product bundle does not includeUse an app with the right product
partner.env_mismatch403Token environment does not match the merchant’s demo flagUse the matching credential
partner.ip_denied403Source address is not on the credential’s allowlistUpdate the allowlist, or call from the declared egress
partner.grant_missing403The grant was revoked after the token was mintedRe-mint; if it still fails, the merchant link is gone
partner.rate_limited429More than 300 requests in the current minuteBack off — see rate limits
partner.window_too_large400A report window exceeds 31 daysSplit the range
partner.invalid_window400from/to is missing, malformed, or to precedes fromSend both as YYYY-MM-DD
partner.order_not_found404The online-order id does not exist in this merchantCheck the id returned at creation
partner.acting_user_unavailable409The merchant has no employee record to attribute writes toContact the merchant — their account is not fully set up

These come from the shared data layer and can surface on any partner route.

CodeStatusMeaning
db.duplicate409A unique constraint rejected the write — usually a repeated barcode, code or name
db.not_found404The referenced row does not exist in this merchant
company.not_provisioned503The merchant’s database is not ready yet. Retry shortly

Request bodies are validated strictly. Unknown properties are rejected, not ignored, so a typo in a field name fails loudly instead of silently dropping data.

400 Bad Request
{
"status": "error",
"statusCode": 400,
"timestamp": "2026-07-29T09:20:03.117Z",
"path": "/v1/items",
"message": "groupId must be a number; property 'grupId' should not exist",
"correlationId": "0b7a5c11-84c2-4f0a-9f2f-7c1e40d6b8aa"
}

Validation failures have no code — the message names the offending fields.

SituationAction
429Wait until X-RateLimit-Reset, then retry. Do not retry immediately
503 company.not_provisionedRetry with exponential backoff, up to about a minute
Other 5xxRetry up to three times with jittered backoff
401 on a partner callMint a fresh token once, then retry. If it fails again, stop
Any other 4xxDo not retry — fix the request