Error Codes
Errors come back in the common error envelope. Branch on code, not on message; envelope fields and general rules: Error Handling.
{
"status": "failure",
"code": "physical_pos.input.text_invalid",
"message": "The text cannot be empty or contain invisible or formatting characters.",
"statusCode": 400,
"requestId": "00000000-0000-0000-0000-000000000000",
"details": { "field": "name", "reason": "forbidden_character" },
"docsUrl": "https://docs.lunixi.com/physical-pos/errors#physical_pos.input.text_invalid"
}messageis returned in Turkish or English depending on the request language; the descriptions in the tables below are these texts. → Error Handling › Choosing a languagedetailsis not present on every error. When present it may carry the field name (field) and the cause (reason); forphysical_pos.validation.invalidthe details are in thedetails.errorsarray, each item{ field, errors: [<reason>] }. Values are never written todetails.- For the codes below the response carries a
docsUrlfield that links to the code's row on this page. 4xxerrors are not fixed by retrying. The retryable class is503and the other5xxerrors. → Common Rules › Retries
physical_pos.* codes
Request and common rules
| HTTP | code | Description | What to do |
|---|---|---|---|
400 | physical_pos.validation.invalid | The physical POS request contains invalid fields — check the submitted fields. | Fix the field named in details.errors. |
400 | physical_pos.cursor.invalid | The pagination cursor is invalid; request the list again from the first page. | Start again from the first page. |
400 | physical_pos.idempotency.key_required | The Idempotency-Key header is required for this operation. | Add the Idempotency-Key header. |
400 | physical_pos.idempotency.key_invalid | Idempotency-Key must be 8-200 printable ASCII characters without spaces. | Use an 8–200 character value without spaces (e.g. a UUID). |
409 | physical_pos.idempotency_mismatch | This Idempotency-Key was already used with a different request. | Use a new key for a new operation. |
409 | physical_pos.version_conflict | The record changed while you were editing it; reload the latest version and apply your change again. | Read the record again and resend with the current rowVersion. |
400 | physical_pos.input.code_invalid | The code format is invalid. | Write the code with A-Z, 0-9, ., _, -; for fields with a fixed set, send a value from the list. |
400 | physical_pos.input.text_invalid | The text cannot be empty or contain invisible or formatting characters. | Check details.field and details.reason; fix the text. |
400 | physical_pos.input.ref_invalid | The reference value is invalid: it must be printable ASCII without spaces. | Use only the allowed characters. |
400 | physical_pos.input.reason_code_invalid | The reason code is invalid. | Send a reason code that fits the operation. |
400 | physical_pos.input.vendor_code_invalid | The vendor code is invalid. | Use a vendor code from the catalog. |
400 | physical_pos.input.virtual_serial_invalid | The virtual serial number is invalid. | Use only A-Z and 0-9, at most 64 characters. |
400 | physical_pos.input.bank_terminal_number_invalid | The bank merchant or terminal number is invalid (uppercase letters and digits only). | Write the MID and TID with A-Z and 0-9, at most 32 characters. |
404 | physical_pos.record.reference_not_found | The referenced record was not found. | Check the IDs you sent. |
409 | physical_pos.record.already_exists | This record already exists. | Read the record; do not repeat the request with a new key. |
409 | physical_pos.record.field_immutable | This field cannot be changed after creation. | Do not change this field; create a new record if needed. |
409 | physical_pos.record.interval_conflict | The record was changed by another request at the same time; reload it and try again. | Read the record again and retry. |
400 | physical_pos.input.empty_patch | There is no field to update. | Send at least one field to change. |
400 | physical_pos.input.filter_invalid | A list filter value is invalid; correct the value of the field in details.field. | Fix the filter named in details.field. |
400 | physical_pos.input.serial_invalid | The device serial number is invalid. | Write the serial number in the vendor's format. |
400 | physical_pos.input.channel_variant_invalid | The integration channel variant is invalid. | Use only A-Z, 0-9 and _, at most 32 characters. |
Identity and environment
| HTTP | code | Description | What to do |
|---|---|---|---|
403 | physical_pos.environment.test_key_read_only | A TEST API key can only read physical POS records; use a LIVE key or the panel to make changes. | Use a live key or the panel for every physical POS write request. |
400 | physical_pos.input.environment_invalid | Invalid environment: do not send an environment with an API key; the panel works only with the live environment. | Do not send environment in the request; your key determines the environment. |
403 | physical_pos.auth.scope_missing | You do not have the permission required for this physical POS operation. | Add the required scope to the key. |
403 | physical_pos.auth.organization_missing | The organization for this request could not be verified. | Check that the key is valid; contact support if it persists. |
403 | physical_pos.auth.environment_missing | The environment for this request could not be verified. | Check that the key is valid; contact support if it persists. |
403 | physical_pos.auth.actor_missing | The user performing this operation could not be verified. | Check that the key is valid; contact support if it persists. |
Stores and registers
| HTTP | code | Description | What to do |
|---|---|---|---|
404 | physical_pos.store.not_found | Store not found. | Check the store ID. |
404 | physical_pos.register.not_found | Register not found. | Check the register ID. |
409 | physical_pos.store.code_taken | A store with this code already exists (archived stores keep their codes). | Choose another code or restore the archived store. |
409 | physical_pos.store.external_ref_taken | A store with this external reference already exists. | Use another externalRef. |
409 | physical_pos.store.archived | The store is archived; reopen the store first. | Restore the store first. |
409 | physical_pos.store.has_devices | A store with assigned devices cannot be archived; move the devices to another store, move them to storage, or retire them. | Move the devices to another store or register, move them to storage or retire them. |
409 | physical_pos.store.too_many_active_registers | The store has too many active registers to archive at once; archive the registers first. | Archive the registers one by one first. |
409 | physical_pos.store.invalid_transition | The store status cannot be changed this way. | Read the current status of the store. |
409 | physical_pos.store.has_active_registers | A store with active registers cannot be archived; archive the registers first. | Archive the registers first. |
409 | physical_pos.store.has_open_assignments | A store with assigned devices cannot be archived; move the devices to another store, move them to storage, or retire them. | Move the devices to another store or register, move them to storage or retire them. |
409 | physical_pos.register.code_taken | A register with this code already exists in this store. | Choose another register code in this store. |
409 | physical_pos.register.archived | The register is archived; reopen the register first. | Restore the register first. |
409 | physical_pos.register.has_devices | A register with assigned devices cannot be archived; move the devices to another store or register, move them to storage, or retire them. | Move the devices to another store or register, move them to storage or retire them. |
409 | physical_pos.register.invalid_transition | The register status cannot be changed this way. | Read the current status of the register. |
409 | physical_pos.register.has_open_assignments | A register with assigned devices cannot be archived; move the devices to another store or register, move them to storage, or retire them. | Move the devices to another store or register, move them to storage or retire them. |
409 | physical_pos.register.store_archived | A register of an archived store cannot be opened; reopen the store first. | Restore the store first. |
409 | physical_pos.device_assignment.target_archived | A device cannot be assigned to an archived store or register. | Assign to an active store or register. |
Logical terminals, bank rows and device link
| HTTP | code | Description | What to do |
|---|---|---|---|
404 | physical_pos.terminal.not_found | Terminal not found. | Check the terminal ID and your key's environment. |
409 | physical_pos.terminal.code_taken | A terminal with this code already exists. | Choose another terminal code in this environment. |
409 | physical_pos.terminal.vendor_ref_taken | An open terminal with this vendor terminal reference already exists. | Check the terminal that uses this value. |
409 | physical_pos.terminal.virtual_serial_taken | An open terminal with this virtual serial already exists. | Check the terminal that uses this value. |
409 | physical_pos.terminal.closed | The terminal is closed and can no longer be changed. | A closed terminal cannot be changed; create a new terminal if needed. |
400 | physical_pos.terminal.vendor_required | The terminal must have a vendor for this operation. | Assign a vendor to the terminal first, or send vendorCode. |
400 | physical_pos.device.vendor_unknown | The device vendor is not recognised. | Use the code of an active vendor in the catalog. |
400 | physical_pos.terminal.device_vendor_mismatch | The terminal and the device belong to different vendors and cannot be linked. | Use a device of the terminal's vendor, or assign the device's vendor. |
409 | physical_pos.logical_terminal.invalid_transition | The terminal status cannot be changed this way. | Read the current status of the terminal. |
404 | physical_pos.acquirer.not_found | Bank record not found. | Check the bank row ID and the environment. |
409 | physical_pos.acquirer.duplicate | A record with this bank and terminal number already exists. | Use the existing row or reactivate it. |
400 | physical_pos.acquirer.currency_invalid | Invalid currency list: ISO 4217 codes, no duplicates, at most 16. | Send unique ISO 4217 codes, at most 16. |
409 | physical_pos.terminal.link_conflict | The device is linked to another terminal; that link must be closed first. | Unlink the device from the other terminal first. |
409 | physical_pos.terminal.device_already_linked | The device is already linked to this terminal. | Nothing to do; read the terminal. |
409 | physical_pos.terminal.device_not_linked | No device is linked to this terminal. | Nothing to do; read the terminal. |
409 | physical_pos.terminal.environment_mismatch | The terminal and the device must be in the same environment (test / live). | Use a device in the same environment as the terminal. |
Devices
| HTTP | code | Description | What to do |
|---|---|---|---|
404 | physical_pos.device.not_found | Device not found. | Check the device ID and its environment. |
404 | physical_pos.device_model.not_found | Device model not found. | Use a model ID from the catalog. |
400 | physical_pos.device.model_vendor_mismatch | The device model does not belong to the selected vendor. | Choose a model of the same vendor as vendorCode. |
409 | physical_pos.device.model_deprecated | This device model is deprecated; new devices cannot be added with it. | Choose an active model. |
400 | physical_pos.device.app_mode_invalid | An app mode can only be set for app-based integration channels (APP2APP, LOCAL_REST). | Send appMode only with the APP2APP or LOCAL_REST channel. |
409 | physical_pos.device.serial_taken | You already have a device registered with this serial number. | Use the device already registered with this serial. |
409 | physical_pos.device.external_ref_taken | This external reference is already used by another of your devices. | Use another externalRef. |
409 | physical_pos.device.already_assigned | The device is already assigned to this store and register. | Nothing to do; read the device. |
409 | physical_pos.device.not_assigned | The device is not assigned to any store. | Nothing to do; read the device. |
409 | physical_pos.device.invalid_transition | The device status cannot be changed this way. | Read the device's current status. |
409 | physical_pos.device.ownership_unverified | A device cannot be activated before its ownership is verified. | The status of a device with unverified ownership cannot be changed. |
409 | physical_pos.device.identity_active | The device has an active identity key or an open activation code; revoke the identity first. | A device with an active identity cannot be retired. |
409 | physical_pos.device.retired | The device has been retired; this operation is not possible. | A retired device cannot be changed; use another device. |
Server
| HTTP | code | Description | What to do |
|---|---|---|---|
503 | physical_pos.service.busy | The record is busy with another operation; please retry shortly. | Retry the same request after a short wait. |
503 | physical_pos.dependency.unavailable | A dependent service is not responding right now; please retry shortly. | Retry with backoff. |
503 | physical_pos.service.not_configured | The physical POS service is currently unavailable. Please try again later. | Retry with backoff. |
500 | physical_pos.internal.integrity | The operation cannot be completed right now. Please try again later. | Retry with backoff; if it persists, contact support with the requestId. |
500 | physical_pos.gateway.response_invalid | An unexpected response was received from the physical POS service. Please try again later. | Retry with backoff; if it persists, contact support with the requestId. |
A record that belongs to another organization gets the same 404 as a record that does not exist; malformed IDs get the same answer too. This keeps the existence of records from being inferred from outside.
Platform codes
These codes come from the signature, product, scope, validation and rate-limit layers and are the same across products:
| HTTP | code | When |
|---|---|---|
400 | VALIDATION_ERROR | Undefined field or query parameter; filter outside its value set; wrong type. |
401 | AUTH_HEADERS_MISSING, INVALID_KID, INVALID_SIGNATURE, NONCE_REPLAY, INVALID_DATE, DIGEST_MISSING, INVALID_DIGEST | The request signature is missing or invalid. → Authentication and Scopes |
403 | IP_NOT_ALLOWED, MERCHANT_SUSPENDED | The request came from outside the key's IP allowlist, or your account is not active. |
403 | PRODUCT_NOT_ENABLED | The key lacks the Physical POS product (PHYSICAL_POS). |
403 | API_INSUFFICIENT_SCOPE | The key lacks the endpoint's scope. → Scopes |
429 | RATE_LIMITED | Rate limit exceeded; wait for Retry-After. → Rate limits |
503 | PRODUCT_ENTITLEMENT_UNAVAILABLE | Your product entitlement could not be verified right now; retry with backoff. |
503 | gateway.downstream_unavailable | The service cannot be reached right now; retry with backoff. |
504 | gateway.downstream_timeout | The service did not respond in time; retry with backoff. For create requests, reuse the same Idempotency-Key. |
Code details: Error Handling › Code dictionary.