Skip to main content

Errors

Error Response​

Every error from Mekari POS uses the same JSON envelope:

{
"status": "error",
"code": "VALIDATION_FAILED",
"message": "validation failed",
"data": {
"fields": [
{ "field": "name", "code": "REQUIRED", "message": "this field is required" }
]
},
"meta": { "request_id": "<REQUEST_ID>" }
}
  • code is stable and in UPPER_SNAKE case. Build your error handling on code, not on message.
  • message is English text for people. It can change.
  • data.fields appears on validation errors that name a field. A few business errors carry other data values (for example data.unlock_at); they are named where the error is explained.
  • meta.request_id identifies the request (usually the same value as the X-Request-Id response header). Include it, or the X-Request-Id header, when you contact support.
  • meta.company_cid (the merchant's Mekari POS company number) and meta.user_id (the acting user) are added when the caller is known. A 401 has only request_id.

Errors from Mekari API Gateway​

Mekari API Gateway checks every request before Mekari POS sees it. Its errors do not use the envelope above.

HTTPWhen
401The signature is wrong, the Date header is more than 5 minutes off, or the endpoint is not in your application's scopes
429Too many requests; see Rate Limits
5xxMekari POS could not be reached (for example 520 or 522). Retry later with backoff.

Error Codes​

HTTPCodeMeaning
400VALIDATION_FAILEDThe body, a query parameter or a header value is invalid. See data.fields when present.
400INVALID_CURSORThe cursor is invalid, or was used with a different limit or different filters
400USER_ID_REQUIREDA write without X-User-ID, or with the header sent more than once
400IDEMPOTENCY_KEY_REQUIREDA POST without Idempotency-Key, or an empty or longer-than-128-character key
401COMPANY_NOT_FOUNDThe company of your application does not use Mekari POS
403PLAN_FEATURE_REQUIREDThe merchant's plan lacks the Public API add-on, or the feature an endpoint needs. See Plan Feature Errors
403SUBSCRIPTION_EXPIREDThe merchant's Mekari POS subscription has expired
403OUTLET_FORBIDDENX-Outlet-ID is missing or not an active outlet of the merchant, or the acting user cannot access it
403PERMISSION_DENIEDThe acting user's role does not allow this action
403APPROVAL_REQUIREDA return, refund or exchange needs a manager approval and no approval_token was sent. See Approvals
403APPROVAL_TOKEN_INVALIDThe approval_token is unknown, used, expired, or issued for another order, user or type. data.reason tells which
403APPROVER_NOT_ELIGIBLEThe approver is not allowed to approve at this outlet
403PIN_INVALIDThe manager PIN is wrong. data.attempts_remaining tells how many tries are left
403OTP_REQUIREDThe approval needs a one-time code. Request a code first
403OTP_NOT_ENABLEDThe outlet does not use approval by one-time code
403OTP_INVALIDThe one-time code is wrong. data.attempts_remaining
403OTP_EXPIREDThe one-time code expired, or its request_id is unknown
403CUSTOMER_SEAT_LIMITThe merchant's plan cannot hold more customers
403FORBIDDENThe request is not allowed; read message
404NOT_FOUNDThe resource does not exist, belongs to another merchant, or is not available at X-Outlet-ID
409DUPLICATE_PHONEAnother customer already uses this phone number
409DUPLICATE_CARD_NUMBERAnother customer already uses this card number
409DUPLICATE_EXTERNAL_ORDER_IDPOST /orders with an external_order_id your order source already sent. data.order_id is the first order
409DATA_CONFLICTThe change conflicts with existing data (for example a category name already used); read message
409SHIFT_NOT_OPENThe acting user has no open shift at the outlet (approval PIN and code calls, returns, refunds and exchanges, and orders created with X-User-ID)
409PIN_LOCKEDToo many wrong PINs or codes. data.unlock_at tells when to try again
409OTP_RESEND_COOLDOWNA code was sent a moment ago. data.resend_after tells when to ask again
409OTP_UNAVAILABLENo code can be sent now. A PIN fallback is open until data.fallback_expires_at
409INSUFFICIENT_PETTY_CASHA cash refund is more than the petty cash of the user's shift
409DINE_IN_ROUND_UNPAIDA return, refund or exchange of a Dine In round whose bill is not paid yet
409SETTLEMENT_SALE_NOT_REVERSIBLEA return, refund or exchange of the payment record of a whole Dine In table; reverse its rounds instead
409KITCHEN_NOT_FULFILLEDA return, refund or exchange (or its quote) of an order of a kitchen sales type whose kitchen ticket is not served or completed yet, or that has no ticket; data.kitchen_status has its status (null without a ticket). Retry with the same Idempotency-Key once the kitchen is done
409CART_REPRICEDexpected_payment_fee of an exchange differs from the fee the server priced (quote again), or expected_total of an order differs from the total POS priced (data has the POS amounts). See Creating Orders
409ORDER_SOURCE_NOT_REGISTEREDPOST /orders before PUT /order-source. Register your order source first
409SOURCE_CODE_TAKENAnother application of the merchant already uses this source_code
409SOURCE_CODE_LOCKEDThe order source already has an order, so its source_code cannot change
409SALES_TYPE_DISABLEDThe outlet does not sell the sales_type of the order
409IDEMPOTENCY_KEY_REUSEDThe Idempotency-Key was already used for a different request
409IDEMPOTENCY_IN_PROGRESSA request with the same Idempotency-Key is still running; retry shortly
422INVALID_USERX-User-ID is not an active user of the merchant
422UNPROCESSABLEA business rule of the endpoint failed; read message
422KITCHEN_ACCEPT_STOCK_INSUFFICIENTAn order with accepted_type auto was refused because stock is short. data.shortages names the items
422POST_SALE_DISABLEDThe merchant turned this operation (returns, refunds or exchanges) off in Pengaturan pesanan. See Returns, Refunds and Exchanges
422POST_SALE_WINDOW_EXPIREDThe order is older than the day limit the merchant set for the operation. data.max_days is the limit in days
422PARTNER_REFUND_ORIGINAL_TENDERA partner order (created with POST /orders) is refunded to the original tender. Cash is refused above the cash it was paid
422REFUND_TENDER_INVALID_AMOUNTA refund tender amount is not above 0
422REFUND_TENDER_DUPLICATEA payment method appears more than once in refund_tenders
422REFUND_TENDER_NOT_ELIGIBLEA refund tender is not a refundable tender of the order, and not the cash method. A Mekari Pay QRIS payment, points, deposit, customer_balance and a legacy tender are always refused
422REFUND_MULTI_TENDER_REQUIRES_FULLMore than one refund tender, when the refund is not a full refund of an untouched order
422REFUND_TENDER_SUM_MISMATCHThe refund_tenders amounts do not add up to the refund
422REFUND_TENDER_EXCEEDS_REMAININGA Dine In round's share of a table settlement tender cannot take that much more; refund the rest in cash
500INTERNALUnexpected error. Retry with backoff; contact support with meta.request_id if it continues.

Other codes can appear for business rules of a specific endpoint; the endpoint's description in the API reference names them, and message explains them.

A request to an unknown path, with an unsupported method, or with a body larger than 64 KB gets 404, 405 or 413 with an envelope that has no code.

Endpoints That Need a Plan Feature​

On top of the Public API add-on, these endpoints need another feature in the merchant's plan. Without it they answer 403 PLAN_FEATURE_REQUIRED.

EndpointsPlan feature
GET /promotions, GET /promotions/{id}Product discounts
GET /vouchers/{code}Order discount vouchers
/raw-materials, /recipes, stock reads and counts of raw materials (GET /inventory/stock/raw_material/{id}, PUT /inventory/snapshots/raw_material/{id}), and stock adjustments of raw materialsRaw materials and recipes
GET /returnsRefund order list
GET /refundsRefund order list
POST /returns/quote, POST /returnsReturn orders
POST /refundsRefund order list
POST /ordersKitchen order management
GET /exchangesExchange orders
POST /exchanges/quote, POST /exchangesExchange orders
POST /approvals/otpApproval by one-time code

GET /vouchers/{code} never answers "not found" for an unknown code: it answers 200 with "valid": false and "reason": "not_found".

Plan Feature Errors​

PLAN_FEATURE_REQUIRED has two kinds of answer:

  • A missing Public API add-on answers 403 PLAN_FEATURE_REQUIRED with no data.
  • A missing plan feature of an endpoint (the table above) answers 403 PLAN_FEATURE_REQUIRED with data.feature, the feature key, and data.reason:
data.reasonMeaning
not_in_planThe merchant's plan does not include the feature. The merchant must change the plan.
disabledThe plan includes the feature but it is switched off. The merchant can turn it on in Settings > Features.
{
"status": "error",
"code": "PLAN_FEATURE_REQUIRED",
"message": "<MESSAGE>",
"data": { "reason": "disabled", "feature": "<FEATURE_KEY>" },
"meta": { "request_id": "<REQUEST_ID>" }
}

Build your handling on code and data.reason, not on message.

Returns, Refunds and Exchanges Switched Off​

Two 422 codes come from the merchant's own settings for returns, refunds and exchanges (Pengaturan pesanan), not from the plan:

  • POST_SALE_DISABLED: the merchant has not turned this operation on. It is off for new merchants.
  • POST_SALE_WINDOW_EXPIRED: the order is older than the day limit of the operation. data.max_days is the limit in days.

See Returns, Refunds and Exchanges. Another request will not help: the merchant must change the setting.