Bank Accounts
API Reference
Transactions

Transactions

Who calls these endpoints?External API (s2s)
Base path
/api/v1/bank-connect
Identity
Ed25519 request signature on every request — X-Key-Id, X-Date, X-Nonce, X-Signature. ±5 min clock window, single-use nonce. A Bearer token or a panel session is not accepted here (401 AUTH_HEADERS_MISSING).
Authorization
The key must carry the Bank Connections product (BANK_CONNECT) and the permission for the endpoints on this page: bank-connect:transactions:view. Without the product the call gets 403 PRODUCT_NOT_ENABLED; without the permission 403 API_INSUFFICIENT_SCOPE. See API Introduction › Permissions.
Cross-surface
These endpoints are the s2s surface — call them from your own server with a signed request. The same data has screens in the panel, but panel endpoints cannot be called with an API key, and these endpoints cannot be called with a panel session. Connecting and disconnecting a bank is done only in the panel.

A transaction is a single line of the account statement. These endpoints return the transactions of your connected accounts. Transaction direction and types: Concepts › Transaction direction and type.

List transactions

GET /api/v1/bank-connect/transactions

Query parameterTypeDescription
accountIdstringComma-separated account IDs (UUID), at most 100. If omitted, all accounts in your key's environment.
bookedFromstring (ISO 8601)Booking date (bookedAt) from this moment — inclusive. Send it with a time zone.
bookedTostring (ISO 8601)Booking date before this moment — exclusive. Can only be sent together with bookedFrom.
directionstringCREDIT or DEBIT.
minAmountMinorintegerMinimum amount in minor units — inclusive.
maxAmountMinorintegerMaximum amount in minor units — inclusive.
canonicalTypestringComma-separated transaction types, e.g. TRANSFER_IN,POS_SETTLEMENT.
limitintegerRecords per page, 1–100. Default 25.
cursorstringpageInfo.nextCursor from the previous response.

Date range rules

  • The range between bookedFrom and bookedTo can be at most 92 days in a single request.
  • If you send only bookedFrom, the upper bound is the time of the request; the range is still at most 92 days.
  • bookedTo cannot be sent alone; bookedFrom is also required.
  • If you send neither, no date filter is applied and all transactions are paginated.

A request that breaks these rules returns 400 bank_connect.validation.invalid; the reason is in details.errors[].reason:

reasonCause
booked_from_requiredbookedTo is present, bookedFrom is not.
after_booked_tobookedFrom is after bookedTo.
range_too_largeThe range is longer than 92 days.
invalidThe date could not be read.
min_greater_than_maxminAmountMinor is greater than maxAmountMinor.

Filter examples

What you wantQuery
Transactions of one account?accountId=00000000-0000-0000-0000-000000000100
Transactions of two accounts?accountId=00000000-0000-0000-0000-000000000100,00000000-0000-0000-0000-000000000101
Transactions in January 2026?bookedFrom=2026-01-01T00:00:00%2B03:00&bookedTo=2026-02-01T00:00:00%2B03:00
Incoming money only?direction=CREDIT
Between 1,000 and 5,000 TRY?minAmountMinor=100000&maxAmountMinor=500000
Incoming transfers and POS settlements?canonicalType=TRANSFER_IN,POS_SETTLEMENT
⚠️

Encode the + character as %2B in the query string; an unencoded + is read as a space and the date is rejected. The path you sign must be exactly the encoded path you send.

There is no filter that searches the description text; any undeclared parameter returns 400 VALIDATION_ERROR.

source lunixi-sign.sh
REQ_PATH="/api/v1/bank-connect/transactions?direction=CREDIT&canonicalType=TRANSFER_IN&limit=50"
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"

$bankConnect in the PHP example: API Introduction › PHP SDK.

Ordering

Transactions are ordered by the time they were recorded (receivedAt), newest first. receivedAt is the moment the transaction was read from the bank and recorded at Lunixi, and it never changes. The booking date (bookedAt) is only a filter, not the sort key.

So if a bank reports a transaction late, it appears at the top of the list even though its booking date is older.

Response

{
  "status": "success",
  "code": "SUCCESS",
  "data": {
    "items": [
      {
        "transactionId": "00000000-0000-0000-0000-000000001000",
        "accountId": "00000000-0000-0000-0000-000000000100",
        "direction": "CREDIT",
        "amountMinor": 150000,
        "currency": "TRY",
        "bookedAt": "2026-01-01T07:00:00.000Z",
        "canonicalType": "TRANSFER_IN",
        "description": "ORNEK ACIKLAMA",
        "counterpartyNameMasked": "Ö**** A****",
        "counterpartyIbanMasked": "TR00 **** **** **** **** **00 00",
        "receivedAt": "2026-01-01T07:05:00.000Z"
      }
    ]
  },
  "pageInfo": { "hasMore": true, "nextCursor": "eyJ0IjoiMjAyNi0wMS0wMVQwNzowNTowMFoifQ", "totalCount": null },
  "statusCode": 200,
  "requestId": "00000000-0000-0000-0000-000000000000"
}

Single transaction

GET /api/v1/bank-connect/transactions/{transactionId} returns a single transaction. In addition to the list fields, it contains the balance after the transaction, the document number and the bank. A transaction that is unknown, belongs to another organization or to the other environment returns 404 bank_connect.transaction.not_found.

{
  "status": "success",
  "code": "SUCCESS",
  "data": {
    "transactionId": "00000000-0000-0000-0000-000000001000",
    "accountId": "00000000-0000-0000-0000-000000000100",
    "direction": "CREDIT",
    "amountMinor": 150000,
    "currency": "TRY",
    "bookedAt": "2026-01-01T07:00:00.000Z",
    "canonicalType": "TRANSFER_IN",
    "description": "ORNEK ACIKLAMA",
    "counterpartyNameMasked": "Ö**** A****",
    "counterpartyIbanMasked": "TR00 **** **** **** **** **00 00",
    "receivedAt": "2026-01-01T07:05:00.000Z",
    "counterpartyBankCode": "0000",
    "counterpartyBankName": "ÖRNEK BANKA",
    "balanceAfterMinor": 990000,
    "documentNo": "0000000",
    "bank": { "bankId": "00000000-0000-0000-0000-000000000001", "eftCode": "0000", "name": "ÖRNEK BANKA" }
  },
  "statusCode": 200,
  "requestId": "00000000-0000-0000-0000-000000000000"
}

The transaction object

FieldTypeDescription
transactionIdstring (UUID)The transaction's ID. Always the same for the same transaction; use it for deduplication.
accountIdstring (UUID)The account the transaction belongs to.
directionstringCREDIT (in) or DEBIT (out).
amountMinorintegerAmount in minor units. Always zero or positive; the direction is in direction.
currencystringCurrency (ISO 4217).
bookedAtstringBooking date. Absent when there is no value.
valueAtstringValue date. Absent if the bank does not report it.
canonicalTypestringBank-independent transaction type. → Transaction types
descriptionstringThe description as the bank wrote it, unmasked. Absent when there is no value.
counterpartyNameMaskedstringThe counterparty's masked name. Absent when there is no value.
counterpartyIbanMaskedstringThe counterparty's masked IBAN. Absent when there is no value.
receiptNostringReceipt number. Absent when there is no value.
receivedAtstringThe moment the transaction was recorded at Lunixi; the sort key.
isReversalbooleanPresent only when true: this row reverses an earlier transaction (a cancellation or refund). Absent when the row is not a reversal.
reversalOfTransactionIdstring (UUID)The original transaction the reversal belongs to. Absent when the bank does not report the link.
counterpartyBankCodestringEFT code of the counterparty's bank. Absent when the bank does not report it.
counterpartyBankNamestringName of the counterparty's bank. Absent when the bank does not report it.
balanceAfterMinorintegerSingle-transaction endpoint only. Balance after the transaction. Absent if the bank does not report it.
documentNostringSingle-transaction endpoint only. The bank's document number. Absent when there is no value.
bankobjectSingle-transaction endpoint only. bankId, eftCode and, when available, name.

Keeping transactions in sync

The primary way to learn about new transactions is the webhook event. The list endpoint is the reliable source when you missed a webhook or pull data for the first time. Use them together like this:

Keeping transactions in syncData flow: transactions arrive from the webhook (fast) and from regular catch-up reads of the list endpoint; both are deduplicated by transactionId before they are stored, and the newest stored receivedAt is the point where the next catch-up stops.FASTCATCH-UPSTORE ONCESTOP POINTWebhookbank_account.transaction.receivedList endpointGET …/transactionsDeduplicateby transactionIdYour recordsnewest receivedAtLEGENDSOURCEDEDUPLICATIONYOUR SYSTEMNEXT CATCH-UP STOPS HERE
Neither source is enough on its own: a webhook can be missed, and the list is read at intervals. Order and stop by receivedAt, never by bookedAt.
  1. Initial load. Request the first page without filters (or only with accountId) and paginate with nextCursor until pageInfo.hasMore is false. Store each transaction by transactionId and keep the newest receivedAt you have seen.
  2. Regular catch-up. At intervals, start from the first page (no cursor) with the same filters. Because the list is newest first, paginate until you pass behind the newest receivedAt you stored, then stop.
  3. Deduplicate. Put the stop point a little behind the receivedAt you stored and deduplicate every incoming transaction by transactionId; receiving the same transaction from both the webhook and the list is then harmless.
⚠️

Do not build your sync on the booking date (bookedAt). A bank may report a transaction late; a transaction with an old booking date can enter the list after your last check. That is why receivedAt is the right field for ordering and for the stop point.

When you change filters, reset the cursor and start from the first page.

Errors

HTTPcodeWhat to do
400VALIDATION_ERRORAn undeclared parameter (for example a description search), an unknown canonicalType or direction, a non-UUID accountId, more than 100 accounts, or limit outside 1–100.
400bank_connect.validation.invalidThe date range or amount range breaks a rule; check details.errors[].reason.
400bank_connect.cursor.invalidSend the cursor unchanged or start from the first page.
403API_INSUFFICIENT_SCOPEAdd bank-connect:transactions:view to your key; bank-connect:view is not enough.
404bank_connect.transaction.not_foundCheck the ID and your key's environment.