Home /
Appendices /
Error Codes
Appendix 14.2 — Error Codes
1. Purpose
This appendix is the canonical catalog of error codes returned by the API and surfaced in logs, validation reports, and UI dialogs. Codes are stable identifiers; their human-readable messages may evolve, but the code is the consumer-stable contract.
2. Code naming convention
<DOMAIN>_<CONDITION> — uppercase, snake_case.
- Domain is the functional area:
BOOKING, INVOICE, JE, RECON, TAX, AUTH, etc.
- Condition is the precise failure.
Examples: BOOKING_HOLD_EXPIRED, JE_UNBALANCED, TAX_RULE_OVERLAP.
3. Severity & retry advice
Each code carries:
- HTTP status when surfaced via API.
- Severity: INFO / WARN / ERROR / CRITICAL.
- Retry advice: non_retryable / retryable_after_short / retryable_after_long / requires_user_action.
4. Authentication & authorization
| Code |
HTTP |
Severity |
Retry |
Description |
AUTH_INVALID_CREDENTIALS |
401 |
WARN |
requires_user_action |
Username/password failed |
AUTH_MFA_REQUIRED |
401 |
INFO |
requires_user_action |
MFA challenge needed |
AUTH_MFA_INVALID |
401 |
WARN |
requires_user_action |
Code didn't match |
AUTH_SESSION_EXPIRED |
401 |
INFO |
requires_user_action |
Re-authenticate |
AUTH_TOKEN_INVALID |
401 |
ERROR |
non_retryable |
Bearer token malformed / unknown |
AUTH_TOKEN_REVOKED |
401 |
ERROR |
non_retryable |
Token explicitly revoked |
AUTH_TOKEN_EXPIRED |
401 |
INFO |
requires_user_action |
Token aged out |
AUTH_ACCOUNT_LOCKED |
403 |
ERROR |
requires_user_action |
Excessive failed attempts |
AUTH_PASSWORD_EXPIRED |
403 |
INFO |
requires_user_action |
Rotation overdue |
PERM_DENIED |
403 |
ERROR |
non_retryable |
Caller lacks required permission |
PERM_SCOPE_INSUFFICIENT |
403 |
ERROR |
non_retryable |
Token's scope does not include this resource |
PARTNER_MISMATCH |
403 |
CRITICAL |
non_retryable |
Resource belongs to a different partner |
5. Validation (generic)
| Code |
HTTP |
Severity |
Description |
FIELD_REQUIRED |
400 |
WARN |
Required field missing |
FIELD_INVALID_FORMAT |
400 |
WARN |
Format failed (e.g., bad email) |
FIELD_OUT_OF_RANGE |
400 |
WARN |
Numeric/date out of valid range |
FIELD_TOO_LONG |
400 |
WARN |
String exceeds limit |
FIELD_UNKNOWN_VALUE |
400 |
WARN |
Enum value not recognised |
BODY_MALFORMED |
400 |
ERROR |
Could not parse |
IDEMPOTENCY_KEY_CONFLICT |
409 |
ERROR |
Same key, different body |
IDEMPOTENCY_KEY_MISSING |
400 |
WARN |
Required for this endpoint |
RATE_LIMITED |
429 |
INFO |
Retry after Retry-After |
MAINTENANCE_MODE |
503 |
INFO |
Platform in maintenance window |
6. Booking & ticketing
| Code |
HTTP |
Description |
BOOKING_OFFER_NOT_FOUND |
404 |
Offer ID unknown or expired |
BOOKING_OFFER_EXPIRED |
409 |
Offer cache TTL passed |
BOOKING_HOLD_EXPIRED |
409 |
Supplier hold lapsed |
BOOKING_CUSTOMER_REQUIRED |
400 |
No customer linked |
BOOKING_INSUFFICIENT_PASSENGERS |
400 |
Pax count mismatch |
BOOKING_PRICE_CHANGED |
409 |
Re-pricing returned a different amount |
BOOKING_SCHEDULE_CHANGED |
409 |
Underlying flight time changed |
BOOKING_NOT_IN_ISSUABLE_STATE |
409 |
State machine rejects issuance from current state |
BOOKING_CREDIT_DENIED |
402 |
Customer credit hold / over limit |
BOOKING_REQUIRES_APPROVAL |
202 |
Over threshold; pending approver |
BOOKING_PAYMENT_PENDING |
402 |
Payment not yet captured |
TICKETING_TIMELIMIT_EXPIRED |
409 |
Fare TL passed |
TICKETING_VOID_WINDOW_CLOSED |
409 |
Cannot void after window |
TICKETING_SUPPLIER_ERROR |
502 |
Supplier rejected issuance |
TICKETING_SUPPLIER_TIMEOUT |
504 |
Supplier did not respond |
TICKETING_DUPLICATE_ISSUANCE |
409 |
Already issued |
7. Refunds & cancellations
| Code |
HTTP |
Description |
REFUND_NOT_ELIGIBLE |
409 |
Fare rules prohibit |
REFUND_AMOUNT_EXCEEDS_PAID |
422 |
Cannot refund more than paid |
REFUND_QUOTE_STALE |
409 |
Quote older than allowed |
REFUND_SUPPLIER_REJECTED |
502 |
Supplier said no |
VOID_AFTER_HOURS_WINDOW |
409 |
Past void cutoff |
EXCHANGE_FARE_DIFFERENCE_UNPAID |
402 |
Customer must pay difference first |
EXCHANGE_NOT_REISSUABLE_FARE |
409 |
Fare type forbids reissue |
8. Customers & suppliers
| Code |
HTTP |
Description |
CUSTOMER_DUPLICATE |
409 |
Same identifying details exist |
CUSTOMER_INACTIVE |
409 |
Marked inactive |
CUSTOMER_CREDIT_HOLD |
402 |
Hold flag set |
CUSTOMER_CREDIT_LIMIT_EXCEEDED |
402 |
Booking would breach limit |
CUSTOMER_KYC_INCOMPLETE |
422 |
Required docs missing |
CUSTOMER_SANCTIONS_HIT |
451 |
Screening match — block & escalate |
SUPPLIER_INACTIVE |
409 |
Marked inactive |
SUPPLIER_BANK_HOLD |
409 |
Recent bank change; payout hold active |
SUPPLIER_COMMISSION_RULE_NONE |
422 |
No rule found at booking time |
SUPPLIER_CREDENTIALS_INVALID |
502 |
Auth with supplier failed |
9. Journal entries & ledger
| Code |
HTTP |
Severity |
Description |
JE_UNBALANCED |
422 |
CRITICAL |
Debit ≠ Credit |
JE_PERIOD_CLOSED |
409 |
ERROR |
Target period closed |
JE_ACCOUNT_DISABLED |
422 |
ERROR |
Account marked inactive |
JE_NEGATIVE_AMOUNT |
422 |
ERROR |
Lines must be positive (sign via DR/CR) |
JE_FX_RATE_MISSING |
422 |
ERROR |
No rate for foreign-currency line |
JE_DIMENSION_MISMATCH |
422 |
ERROR |
Account requires dimension not supplied |
JE_REVERSAL_TARGET_NOT_FOUND |
404 |
ERROR |
Trying to reverse non-existent JE |
JE_ALREADY_REVERSED |
409 |
ERROR |
Already reversed |
COA_ACCOUNT_NOT_FOUND |
404 |
ERROR |
Unknown account code |
COA_HIERARCHY_INVALID |
422 |
ERROR |
Parent/child structure broken |
10. Invoicing & payments
| Code |
HTTP |
Description |
INVOICE_ALREADY_GENERATED |
409 |
Booking already invoiced |
INVOICE_TEMPLATE_MISSING |
422 |
No template configured |
INVOICE_NUMBER_SEQUENCE_EXHAUSTED |
500 |
Internal — escalate |
PAYMENT_AMOUNT_MISMATCH |
422 |
Doesn't match invoice/booking |
PAYMENT_CURRENCY_MISMATCH |
422 |
Currency conflict |
PAYMENT_GATEWAY_DECLINED |
402 |
Card/method declined |
PAYMENT_GATEWAY_TIMEOUT |
504 |
Gateway no response |
PAYMENT_DUPLICATE |
409 |
Same gateway transaction id seen |
PAYMENT_INVOICE_OVERAPPLIED |
422 |
Application exceeds invoice |
11. Reconciliation
| Code |
HTTP |
Description |
RECON_FILE_DUPLICATE |
409 |
Same checksum imported |
RECON_FILE_FORMAT_INVALID |
422 |
Parse failed |
RECON_OPENING_BALANCE_MISMATCH |
422 |
Doesn't continue from prior closing |
RECON_MANUAL_MATCH_CONFLICT |
409 |
Chosen pair already matched |
RECON_VARIANCE_EXCEEDS_THRESHOLD |
422 |
Requires manual review |
RECON_PERIOD_CLOSED |
409 |
Adjustment hits closed period |
12. Tax
| Code |
HTTP |
Description |
TAX_RULE_OVERLAP |
422 |
Two rules of same priority match |
TAX_RATE_INVALID |
422 |
Negative or >100% |
TAX_JURISDICTION_NOT_SUPPORTED |
422 |
Partner not configured |
TAX_RULE_MISSING |
422 |
Required rule absent for booking |
TAX_RETURN_PERIOD_OVERLAP |
409 |
Already filed |
13. Period close
| Code |
HTTP |
Description |
PERIOD_PRECLOSE_GATE_FAILED |
422 |
Unsatisfied items |
PERIOD_TRIAL_BALANCE_UNBALANCED |
422 |
Critical — cannot close |
PERIOD_CLOSE_SAME_USER |
403 |
Maker = Checker |
PERIOD_REOPEN_LOCKED |
409 |
Locked; cannot reopen |
PERIOD_FUTURE_CLOSE |
422 |
Period not yet ended |
14. Memos (ADM/ACM)
| Code |
HTTP |
Description |
MEMO_NOT_FOUND |
404 |
Unknown memo |
MEMO_DISPUTE_WINDOW_CLOSED |
409 |
Cannot dispute now |
MEMO_ALREADY_SETTLED |
409 |
Lifecycle terminal |
MEMO_LINK_TARGET_INVALID |
422 |
Cannot link to that ticket |
15. Recognition & commission
| Code |
HTTP |
Description |
RECOGNITION_BOOKING_NO_SERVICE_DATE |
422 |
Missing service_date |
RECOGNITION_DOUBLE_ATTEMPT |
409 |
Idempotency stopped re-entry |
RECOGNITION_PERIOD_CLOSED |
409 |
Cannot recognise into closed period |
COMMISSION_RATE_INVALID |
422 |
Out of range |
COMMISSION_RECALL_AMOUNT_MISMATCH |
422 |
Recall ≠ accrual |
COMMISSION_VARIANCE_EXCESSIVE |
422 |
Above auto-resolve threshold |
16. BSP / ARC
| Code |
HTTP |
Description |
BSP_FILE_HEADER_INVALID |
422 |
Malformed header |
BSP_FILE_DUPLICATE |
409 |
Same checksum |
BSP_FILE_AGENT_CODE_MISMATCH |
422 |
Code ≠ partner accreditation |
BSP_FILE_TOTAL_MISMATCH |
422 |
Trailer total ≠ line sum |
BSP_ORPHAN_RATE_HIGH |
422 |
Operational concern |
17. Imports
| Code |
HTTP |
Description |
IMPORT_FILE_DUPLICATE |
409 |
Same checksum |
IMPORT_FILE_TOO_LARGE |
413 |
Exceeds limit |
IMPORT_ENCODING_INVALID |
422 |
Not UTF-8 |
IMPORT_SCHEMA_MISMATCH |
422 |
Headers don't match |
IMPORT_PARTNER_MISMATCH |
422 |
Wrong partner |
18. Integrations / supplier
| Code |
HTTP |
Description |
SUPPLIER_UNAVAILABLE |
503 |
Outage |
SUPPLIER_SCHEDULE_CHANGED |
409 |
Underlying flight changed |
SUPPLIER_RATE_LIMITED |
503 |
Supplier-side throttle |
SUPPLIER_RESPONSE_MALFORMED |
502 |
Couldn't parse |
WEBHOOK_URL_INVALID |
422 |
Non-HTTPS / unreachable |
WEBHOOK_SIGNATURE_MISMATCH |
401 |
Signature failed verification |
| Code |
HTTP |
Description |
INTERNAL_ERROR |
500 |
Unexpected; logged |
DB_DEADLOCK_RETRY |
503 |
Caller can retry |
CACHE_MISS_HARD |
503 |
Required cache entry missing |
WORKER_TIMEOUT |
504 |
Background job timed out |
FEATURE_NOT_ENABLED |
403 |
Feature flag off for partner |
20. Consumer guidance
For API consumers:
- Always switch on the code, not the message. Messages may localise.
- Tolerate unknown codes. New codes may be introduced; treat as INTERNAL_ERROR defensively.
- Respect retry_advice. Don't hammer.
- Surface the request_id in support tickets.