Error Codes
All Sangho API errors return a flat structured JSON object (no nested keys) with a business error code, a readable message, and a direct link to the section of this page that explains it. Clicking a doc_url link from your console or logs brings you here and automatically highlights the relevant row.
Error Structure
| Champ | Type | Requis | Description |
|---|---|---|---|
type | string | Requis | Error category — one of the 7 values listed below. Useful for generic handling (displaying a message per category). |
code | string | Requis | Precise Sangho business code — e.g. AMOUNT_TOO_SMALL. This is the source of truth for fine-grained error handling, to be preferred over type and the HTTP status code. |
message | string | Requis | Human-readable error description |
status | integer | Requis | Associated HTTP code — 400, 401, 403, 404, 409, 422, 429, 500 |
doc_url | string | Optionnel | Direct link to the row of this page matching the code — e.g. https://docs.sangho.ga/errors#AMOUNT_TOO_SMALL |
param | string | Optionnel | Field at fault for validation errors (422) |
errors | object | Optionnel | Per-field detail for multi-field validation errors — {`{ "amount": ["..."] }`} |
request_id | string | Optionnel | Request identifier, to provide to support if needed |
The 7 categories (type)
Every error belongs to one of these 7 categories. The JavaScript SDK exposes the same taxonomy on err.type — handy for generic branch handling (retry, login redirect, etc.) before drilling down to the precise code if needed.
| type | Typical HTTP status | Meaning |
|---|---|---|
AUTHENTICATION_ERROR | 401 | API key missing, invalid, or expired |
PERMISSION_ERROR | 403 | Identity recognized, but the action is not authorized (public key, restricted account, insufficient plan…) |
NOT_FOUND_ERROR | 404 | Resource not found |
CONFLICT_ERROR | 409 | Conflict — reused idempotency key, duplicate |
VALIDATION_ERROR | 422 | Syntactically correct request, but invalid at the business level |
RATE_LIMIT_ERROR | 429 | Call quota exceeded |
API_ERROR | 500 | Error on Sangho's side |
Standard HTTP Codes
The HTTP code indicates the error category. Use it to guide your retry and display logic.
| Code | Description |
|---|---|
200 | Success — request processed |
201 | Resource created successfully |
400 | Invalid request — malformed JSON or incorrect headers |
401 | API key missing, expired, or invalid |
403 | Insufficient permission — the App does not have access to this resource or action |
404 | Resource not found — incorrect ID or deleted resource |
409 | Conflict — idempotency key reused with a different request body |
422 | Business validation failed — business rule not respected |
429 | Too many requests — rate limit reached, apply backoff |
500 | Internal Sangho error — retry with exponential backoff |
Business Error Codes
All codes follow the SCREAMING_SNAKE_CASE standard, with no prefix. Hook your error handlers on code rather than the HTTP status — the code is the source of truth.
Authentication — 401
| Code | Description |
|---|---|
MISSING_API_KEY | API key missing — provide it via Authorization: Bearer <key> |
INVALID_API_KEY | API key invalid, revoked, or malformed |
EXPIRED_API_KEY | API key expired — generate a new one from your dashboard |
INVALID_TOKEN | Invalid or expired JWT token |
AUTHENTICATION_FAILED | Generic authentication failure — check the Authorization header |
Authorization — 403
These errors are returned when the identity is recognized but the operation is not authorized for this key, account, or plan.
| Code | Description |
|---|---|
PERMISSION_DENIED | Permission denied — your key is not authorized to perform this action on this resource |
PUBLIC_KEY_NOT_ALLOWED | This route requires a secret key (sk_prod_ or sk_test_). Never expose your secret key client-side |
APP_DISABLED | The application is disabled — re-enable it from your dashboard |
APP_NOT_VERIFIED | Application not verified — KYC pending approval |
INSUFFICIENT_PERMISSIONS | Insufficient permissions for this resource or action |
SANDBOX_ONLY | This route is restricted to the sandbox environment — use a sk_test_ key |
CURRENCY_NOT_IN_PLAN | The requested currency is active on Sangho but not included in your current plan — upgrade to enable it |
COMPANY_BLACKLISTED | This account has been blacklisted and no longer has access to the platform — contact support |
COMPANY_PAYMENTS_DISABLED | Incoming payments are currently disabled for this account |
COMPANY_PAYOUTS_DISABLED | Outgoing payouts are currently disabled for this account |
COMPANY_SANDBOX_ONLY | This account is restricted to sandbox (test) mode for now |
COMPANY_KYC_REQUIRED | KYC verification is required before payments can be processed |
The PERMISSION_DENIED code is notably returned by POST /v1/reset when called with a production key (sk_prod_). This route wipes all of the application’s data and is only accessible in sandbox. Use a sk_test_ key to access it.
Resource Not Found — 404
| Code | Description |
|---|---|
NOT_FOUND | Resource not found — incorrect ID, deleted resource, or access denied |
CUSTOMER_NOT_FOUND | No Customer with this ID for this application |
PRODUCT_NOT_FOUND | No Product with this ID |
PAYMENT_INTENT_NOT_FOUND | No PaymentIntent with this ID |
TRANSACTION_NOT_FOUND | No Transaction with this ID |
REFUND_NOT_FOUND | No Refund with this ID |
INVOICE_NOT_FOUND | No Invoice with this ID |
SUBSCRIPTION_NOT_FOUND | No Subscription with this ID |
CHECKOUT_SESSION_NOT_FOUND | No CheckoutSession with this ID |
PAYMENT_METHOD_NOT_FOUND | No PaymentMethod with this ID |
PAYMENT_LINK_NOT_FOUND | No PaymentLink with this ID |
RECEIPT_NOT_FOUND | No Receipt with this ID |
WEBHOOK_NOT_FOUND | No Webhook with this ID |
SEARCH_NO_RESULTS | No results for the search — check the ?search= parameter |
Conflict — 409
| Code | Description |
|---|---|
IDEMPOTENCY_CONFLICT | A request with the same |
DUPLICATE_EMAIL | A Customer with this email already exists for this application |
DUPLICATE_SKU | A Product with this SKU already exists |
Business Validation — 422
| Code | Description |
|---|---|
VALIDATION_ERROR | The provided data is invalid — check the errors field for per-field details |
MISSING_FIELD | A required field is missing from the request |
INVALID_FIELD | A field value is incorrect or outside accepted values |
INVALID_FILTER_PARAM | A filter parameter (?param=) does not match any model field |
INVALID_CURRENCY | The provided currency is not a recognized ISO 4217 code, or is not enabled on Sangho — see |
AMOUNT_TOO_SMALL | Amount below the minimum allowed (100 XAF by default) |
AMOUNT_TOO_LARGE | Amount above the maximum allowed |
INVALID_STATUS_TRANSITION | The requested status transition is not allowed (e.g. reopening a canceled Invoice) |
PAYMENT_INTENT_UNCANCELABLE | Cannot cancel this PaymentIntent in its current status |
PAYMENT_INTENT_NOT_CONFIRMABLE | Cannot confirm this PaymentIntent in its current status |
PAYMENT_INTENT_NOT_CAPTURABLE | Cannot capture — status must be requires_capture |
CAPTURE_AMOUNT_EXCEEDS_ORIGINAL | Capture amount exceeds the original PaymentIntent amount |
CHECKOUT_SESSION_EXPIRED | The checkout session has expired — ask the customer to start a new one |
SUBSCRIPTION_ALREADY_CANCELED | This Subscription is already canceled and cannot be canceled again |
INVOICE_NOT_DRAFTABLE | This Invoice is not in a state that allows reverting to draft |
Rate Limiting — 429
| Code | Description |
|---|---|
RATE_LIMIT_EXCEEDED | API call quota exceeded — check the Retry-After header for wait time |
TOO_MANY_REQUESTS | Too many concurrent requests from this API key |
Payments & Mobile Money
| Code | Description |
|---|---|
INSUFFICIENT_FUNDS | Insufficient funds on the buyer side (Mobile Money or card) |
PAYMENT_METHOD_NOT_SUPPORTED | Payment method not enabled for this merchant account |
MOBILE_MONEY_TIMEOUT | The buyer did not confirm the payment within the allotted time (usually 3 min) |
MOBILE_MONEY_REJECTED | The buyer rejected the payment request on their phone |
MOBILE_MONEY_NUMBER_INVALID | Incorrect Mobile Money number or not registered with the operator |
MOBILE_MONEY_LIMIT_REACHED | Daily or monthly operator limit reached for this number |
CUSTOMER_BLACKLISTED | The Customer is blacklisted — no payments allowed |
Webhooks & Integrations
| Code | Description |
|---|---|
WEBHOOK_SIGNATURE_INVALID | Invalid HMAC-SHA256 signature — verify your webhook secret |
WEBHOOK_ENDPOINT_UNREACHABLE | The webhook URL is not responding — Sangho will retry according to the retry policy |
TERMINAL_OFFLINE | The Sangho terminal is unreachable — check the network connection |
PDF_NOT_AVAILABLE | The PDF for this document has not been generated yet — try again in a moment |
METHOD_NOT_ALLOWED | HTTP method not allowed on this endpoint |
Server Error — 500
| Code | Description |
|---|---|
INTERNAL_ERROR | Internal Sangho error — our team has been notified. Retry with exponential backoff |
DATABASE_ERROR | Database error — temporary, retry in a moment |
UPSTREAM_ERROR | Third-party service error (operator, gateway) — retry or contact support |
Recommended Retry Strategy
Not all errors are equal. The JavaScript SDK already applies this logic automatically (see Node.js SDK) — useful to know if you call the API directly.
| HTTP Code | Recommended action |
|---|---|
| 400 / 422 | Do not retry — fix the request parameters |
| 401 / 403 | Do not retry — verify the API key, permissions, or plan |
| 404 | Do not retry — verify the resource ID |
| 409 | Retrieve the already-produced idempotent response — do not create a duplicate |
| 429 | Wait the duration indicated in the Retry-After header, then retry |
| 500 | Retry with exponential backoff (1s → 2s → 4s → 8s, max 3 attempts) |
The Retry-After header is included in all 429 responses. Its value is expressed in seconds.