Transactions
- 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 gets403 PRODUCT_NOT_ENABLED; without the permission403 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 parameter | Type | Description |
|---|---|---|
accountId | string | Comma-separated account IDs (UUID), at most 100. If omitted, all accounts in your key's environment. |
bookedFrom | string (ISO 8601) | Booking date (bookedAt) from this moment — inclusive. Send it with a time zone. |
bookedTo | string (ISO 8601) | Booking date before this moment — exclusive. Can only be sent together with bookedFrom. |
direction | string | CREDIT or DEBIT. |
minAmountMinor | integer | Minimum amount in minor units — inclusive. |
maxAmountMinor | integer | Maximum amount in minor units — inclusive. |
canonicalType | string | Comma-separated transaction types, e.g. TRANSFER_IN,POS_SETTLEMENT. |
limit | integer | Records per page, 1–100. Default 25. |
cursor | string | pageInfo.nextCursor from the previous response. |
Date range rules
- The range between
bookedFromandbookedTocan 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. bookedTocannot be sent alone;bookedFromis 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:
reason | Cause |
|---|---|
booked_from_required | bookedTo is present, bookedFrom is not. |
after_booked_to | bookedFrom is after bookedTo. |
range_too_large | The range is longer than 92 days. |
invalid | The date could not be read. |
min_greater_than_max | minAmountMinor is greater than maxAmountMinor. |
Filter examples
| What you want | Query |
|---|---|
| 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
| Field | Type | Description |
|---|---|---|
transactionId | string (UUID) | The transaction's ID. Always the same for the same transaction; use it for deduplication. |
accountId | string (UUID) | The account the transaction belongs to. |
direction | string | CREDIT (in) or DEBIT (out). |
amountMinor | integer | Amount in minor units. Always zero or positive; the direction is in direction. |
currency | string | Currency (ISO 4217). |
bookedAt | string | Booking date. Absent when there is no value. |
valueAt | string | Value date. Absent if the bank does not report it. |
canonicalType | string | Bank-independent transaction type. → Transaction types |
description | string | The description as the bank wrote it, unmasked. Absent when there is no value. |
counterpartyNameMasked | string | The counterparty's masked name. Absent when there is no value. |
counterpartyIbanMasked | string | The counterparty's masked IBAN. Absent when there is no value. |
receiptNo | string | Receipt number. Absent when there is no value. |
receivedAt | string | The moment the transaction was recorded at Lunixi; the sort key. |
isReversal | boolean | Present only when true: this row reverses an earlier transaction (a cancellation or refund). Absent when the row is not a reversal. |
reversalOfTransactionId | string (UUID) | The original transaction the reversal belongs to. Absent when the bank does not report the link. |
counterpartyBankCode | string | EFT code of the counterparty's bank. Absent when the bank does not report it. |
counterpartyBankName | string | Name of the counterparty's bank. Absent when the bank does not report it. |
balanceAfterMinor | integer | Single-transaction endpoint only. Balance after the transaction. Absent if the bank does not report it. |
documentNo | string | Single-transaction endpoint only. The bank's document number. Absent when there is no value. |
bank | object | Single-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:
receivedAt, never by bookedAt.- Initial load. Request the first page without filters (or only with
accountId) and paginate withnextCursoruntilpageInfo.hasMoreisfalse. Store each transaction bytransactionIdand keep the newestreceivedAtyou have seen. - 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
receivedAtyou stored, then stop. - Deduplicate. Put the stop point a little behind the
receivedAtyou stored and deduplicate every incoming transaction bytransactionId; 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
| HTTP | code | What to do |
|---|---|---|
400 | VALIDATION_ERROR | An 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. |
400 | bank_connect.validation.invalid | The date range or amount range breaks a rule; check details.errors[].reason. |
400 | bank_connect.cursor.invalid | Send the cursor unchanged or start from the first page. |
403 | API_INSUFFICIENT_SCOPE | Add bank-connect:transactions:view to your key; bank-connect:view is not enough. |
404 | bank_connect.transaction.not_found | Check the ID and your key's environment. |