Physical POS
API Reference
Bank Rows

Bank Rows

Who calls these endpoints?External API (s2s)
Base path
/api/v1/physical-pos
Identity
Ed25519 request signature on every request — X-Key-Id, X-Date, X-Nonce, X-Signature, plus Digest on requests with a body. ±5 min clock window, single-use nonce. A Bearer token or a panel session is not accepted here.
Authorization
The key must carry the Physical POS product (PHYSICAL_POS) and the scope for the endpoints on this page: physical_pos:view · physical_pos:manage. Without the product the call gets 403 PRODUCT_NOT_ENABLED; without the scope 403 API_INSUFFICIENT_SCOPE. See Authentication and Scopes.
Cross-surface
These endpoints are the s2s surface — call them from your own server with a signed request. The same records have screens in the panel, but panel endpoints cannot be called with an API key, and these endpoints cannot be called with a panel session.

A logical terminal can work with several banks. For each bank the terminal has a bank row: the merchant number (MID) and terminal number (TID) the bank gave you, and the currencies accepted with this row. Bank rows live in the terminal's environment; a test key can only read bank rows, and write requests get 403 physical_pos.environment.test_key_read_only.

MethodPathScope
GET/api/v1/physical-pos/terminals/{terminalId}/acquirersphysical_pos:view
POST/api/v1/physical-pos/terminals/{terminalId}/acquirersphysical_pos:manage
PATCH/api/v1/physical-pos/terminal-acquirers/{terminalAcquirerId}physical_pos:manage

The cURL examples compute the signature with lunixi-sign.sh: Authentication › Signing helper.

Bank row object

FieldTypeDescription
idstring (UUID)Row ID; used as terminalAcquirerId on update.
terminalIdstring (UUID)The row's terminal.
environmentstringTEST or LIVE; the terminal's environment.
vendorAcquirerRefstringThe code the POS vendor gives this bank, as you sent it.
acquirerCodestring | nullThe four-digit code of the mapped bank (acquirerCode in the catalog). null if not mapped.
acquirerDisplayNamestring | nullName of the mapped bank. null if not mapped.
mappingStatusstringMAPPED: vendorAcquirerRef was mapped to a bank in the catalog. UNMAPPED: no mapping.
merchantNumberstringThe merchant number (MID) from the bank.
terminalNumberstringThe terminal number (TID) from the bank.
labelstring | nullYour own label for the row.
currenciesstring[]Currencies accepted with this row (ISO 4217), in alphabetical order.
verificationStatusstringUNVERIFIED: entered manually, not confirmed by the vendor. VENDOR_CONFIRMED: confirmed by the vendor.
sourcestringWhere the row came from: MANUAL (added in the panel or via the API), VENDOR_ACCOUNT or DEVICE_REPORTED.
acquirerStatusstringACTIVE or DISABLED.
lastSyncedAtstring | nullWhen the row was last updated from the vendor; null for manually added rows.
rowVersionintegerThe row's concurrency version. → rowVersion
createdAtstringCreation time.
updatedAtstringLast change time.

List bank rows

GET /api/v1/physical-pos/terminals/{terminalId}/acquirers

Query parameterTypeDescription
limitintegerRows per page, 1–100. Default 25.
cursorstringpageInfo.nextCursor from the previous response.
includeTotalbooleanWhen true, pageInfo.totalCount is filled.

The terminal detail already includes the first 100 rows: Retrieve a terminal.

source lunixi-sign.sh
REQ_PATH="/api/v1/physical-pos/terminals/00000000-0000-0000-0000-000000000301/acquirers"
lunixi_sign GET "$REQ_PATH"
 
curl "https://api-gateway.lunixi.com$REQ_PATH" \
  -H "X-Key-Id: $LUNIXI_KEY_ID" \
  -H "X-Date: $DATE" \
  -H "X-Nonce: $NONCE" \
  -H "X-Signature: $SIGNATURE"
{
  "status": "success",
  "code": "SUCCESS",
  "data": {
    "items": [
      {
        "id": "00000000-0000-0000-0000-000000000601",
        "terminalId": "00000000-0000-0000-0000-000000000301",
        "environment": "LIVE",
        "vendorAcquirerRef": "B01",
        "acquirerCode": "0000",
        "acquirerDisplayName": "Example Bank",
        "mappingStatus": "MAPPED",
        "merchantNumber": "000000000000001",
        "terminalNumber": "PS000001",
        "label": "Example Bank TRY",
        "currencies": ["TRY"],
        "verificationStatus": "UNVERIFIED",
        "source": "MANUAL",
        "acquirerStatus": "ACTIVE",
        "lastSyncedAt": null,
        "rowVersion": 1,
        "createdAt": "2026-01-01T07:30:00.000Z",
        "updatedAt": "2026-01-01T07:30:00.000Z"
      }
    ],
    "pageInfo": { "hasMore": false, "nextCursor": null, "totalCount": null }
  },
  "pageInfo": { "hasMore": false, "nextCursor": null, "totalCount": null },
  "statusCode": 200,
  "requestId": "00000000-0000-0000-0000-000000000000"
}

Add a bank row

POST /api/v1/physical-pos/terminals/{terminalId}/acquirers · Idempotency-Key required

FieldTypeRequiredDescription
vendorAcquirerRefstringYesThe code the POS vendor gives the bank: A-Z, a-z, 0-9; at most 16 characters. Case is kept.
merchantNumberstringYesMerchant number (MID): A-Z, 0-9; at most 32 characters. Upper-cased.
terminalNumberstringYesTerminal number (TID): A-Z, 0-9; at most 32 characters. Upper-cased.
labelstringNoYour own label, at most 64 characters. Follows the text rules.
currenciesstring[]YesISO 4217 currency codes (e.g. ["TRY"]); no duplicates, at most 16. Upper-cased and stored in alphabetical order.

Conditions:

  • The terminal must have a vendor; a row cannot be added to a vendor-less terminal: 400 physical_pos.terminal.vendor_required. Assign the vendor with a terminal update.
  • A row cannot be added to a closed terminal: 409 physical_pos.terminal.closed.
  • If the same terminal already has a row with the same vendorAcquirerRef and terminalNumber, or one of your terminals in this environment has an active row with that pair: 409 physical_pos.acquirer.duplicate.

Lunixi maps vendorAcquirerRef to a bank in the catalog using the vendor's bank code mapping. With a mapping you get mappingStatus: "MAPPED" and acquirerCode set; without one, mappingStatus: "UNMAPPED" and acquirerCode: null. A manually added row starts with source: "MANUAL" and verificationStatus: "UNVERIFIED".

source lunixi-sign.sh
REQ_PATH="/api/v1/physical-pos/terminals/00000000-0000-0000-0000-000000000301/acquirers"
BODY='{"vendorAcquirerRef":"B01","merchantNumber":"000000000000001","terminalNumber":"ps000001","label":"Example Bank TRY","currencies":["try"]}'
lunixi_sign POST "$REQ_PATH" "$BODY"
 
curl -X POST "https://api-gateway.lunixi.com$REQ_PATH" \
  -H "Content-Type: application/json" \
  -H "X-Key-Id: $LUNIXI_KEY_ID" \
  -H "X-Date: $DATE" \
  -H "X-Nonce: $NONCE" \
  -H "Digest: $DIGEST" \
  -H "X-Signature: $SIGNATURE" \
  -H "Idempotency-Key: 4f9b2c1e-0000-4000-8000-000000000004" \
  --data-raw "$BODY"

A new row returns 201; a repeat with the same key and the same body returns 200. data: { "acquirer": { … }, "replayed": false }; the acquirer fields are as in the list.

The MID, TID and bank code do not change after a row is created. If your bank gave you a new terminal number, add a new row and set the old row to DISABLED.

Update a bank row

PATCH /api/v1/physical-pos/terminal-acquirers/{terminalAcquirerId}

FieldTypeRequiredDescription
rowVersionintegerYesThe row's rowVersion you last read (not the terminal's).
labelstring | nullNoNew label; null removes it.
currenciesstring[]NoNew currency list; replaces the old one.
statusstringNoACTIVE or DISABLED. Any other value gets 400 physical_pos.input.code_invalid.
  • Send at least one of label, currencies and status; with none the request gets 400 physical_pos.input.empty_patch. If the values you send equal the current ones, the row does not change, rowVersion does not increase and 200 is returned.
  • A row of a closed terminal cannot be updated: 409 physical_pos.terminal.closed.
  • Setting a DISABLED row back to ACTIVE gets 409 physical_pos.acquirer.duplicate if another active row with the same bank code and TID exists in the same environment.
source lunixi-sign.sh
REQ_PATH="/api/v1/physical-pos/terminal-acquirers/00000000-0000-0000-0000-000000000601"
BODY='{"rowVersion":1,"currencies":["EUR","TRY","USD"]}'
lunixi_sign PATCH "$REQ_PATH" "$BODY"
 
curl -X PATCH "https://api-gateway.lunixi.com$REQ_PATH" \
  -H "Content-Type: application/json" \
  -H "X-Key-Id: $LUNIXI_KEY_ID" \
  -H "X-Date: $DATE" \
  -H "X-Nonce: $NONCE" \
  -H "Digest: $DIGEST" \
  -H "X-Signature: $SIGNATURE" \
  --data-raw "$BODY"

Response: { "acquirer": { … } } with the new rowVersion.

Errors

HTTPcodeWhen
400physical_pos.idempotency.key_required / physical_pos.idempotency.key_invalidIdempotency-Key missing or malformed on add.
400physical_pos.input.ref_invalidvendorAcquirerRef is malformed.
400physical_pos.input.bank_terminal_number_invalidmerchantNumber or terminalNumber is malformed.
400physical_pos.input.text_invalidlabel breaks the rules.
400physical_pos.acquirer.currency_invalidInvalid currency list: unknown code, duplicate or more than 16.
400physical_pos.input.code_invalidInvalid status.
400physical_pos.terminal.vendor_requiredThe terminal has no vendor.
400physical_pos.input.empty_patchEmpty update.
400physical_pos.cursor.invalidBroken cursor.
403physical_pos.environment.test_key_read_onlyWrite request made with a test key.
404physical_pos.terminal.not_foundThe terminal does not exist, is not yours or is in the other environment.
404physical_pos.acquirer.not_foundThe row does not exist, is not yours or is in the other environment.
409physical_pos.terminal.closedThe terminal is closed.
409physical_pos.acquirer.duplicateA row with the same bank code and TID exists.
409physical_pos.version_conflictrowVersion is not current.
409physical_pos.idempotency_mismatchThe same Idempotency-Key was used with a different request.
429RATE_LIMITEDTerminal family write limit exceeded. → Rate limits

All codes: Error Codes.