Accounts API
The Accounts API allows Third Party Providers (TPPs) to access account information on behalf of authorized users. This API requires the PSP_AI (Account Information) scope.
Capabilities
List Accounts
Retrieve a list of all accounts accessible to the authenticated user.
GET /accountsEach account includes:
| Field | Required | Description |
|---|---|---|
id | Yes | Unique account identifier used in other API calls |
name | Yes | Human-readable account name |
currency | Yes | Account currency (ISO 4217 alpha-3, e.g., DKK, SEK, NOK) |
bban | Yes | National Basic Bank Account Number |
iban | No | International Bank Account Number |
swift | No | SWIFT/BIC code |
balanceAmount | Yes | Current account balance |
balanceReservedAmount | Yes | Amount reserved for pending transactions |
balanceAvailableAmount | Yes | Available spending amount (balance + credit limit - reserved) |
creditLimitAmount | No | Authorized credit limit (if applicable) |
type | Yes | Account type (only CHECKING accounts are available via PSD2) |
supportsPayments | Yes | Whether payments can be initiated from this account |
supportsTransfers | Yes | Whether credit transfers can be initiated from this account |
ownerName | No | Full name of the account owner |
usage | No | Whether the account is owned by a private individual or an organisation. One of PRIVATE, ORGANISATION. Omitted if the owner type cannot be determined. |
Only checking accounts are exposed through the Open Banking API. Savings accounts and other account types are not accessible.
List Transactions
Retrieve transaction history for a specific account.
GET /accounts/{accountId}/transactionsA newer Transactions v2 endpoint
returns transactions split into separate pending and posted lists (nextgen
style) with opaque cursor pagination. Attachments a user added to a
transaction can be downloaded via the
Attachments endpoints.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
limit | number | Maximum transactions to return (max 500, default 100) |
offset | number | Pagination offset for retrieving additional pages |
from | datetime | Include only transactions after this ISO-8601 timestamp |
to | datetime | Include only transactions before this ISO-8601 timestamp |
If you send a date without a time component (e.g., 2025-01-01), it defaults
to 2025-01-01T12:00:00.000.
Response Structure
The response is a TransactionsResponse wrapper object:
| Field | Type | Description |
|---|---|---|
offset | number | The offset used in pagination |
limit | number | The limit requested |
from | datetime | The from date requested (if any) |
to | datetime | The to date requested (if any) |
transactions | Transaction[] | Array of transaction objects |
The offset is the amount of transactions “skipped”, if you have 10 transactions, and limit is 10 and you have offset 1 you get the last 9 transactions.
Transaction Properties
Each transaction includes:
| Field | Description |
|---|---|
id | Unique transaction identifier |
transactionTime | ISO-8601 timestamp when the transaction was initiated (e.g., the moment a card was swiped or a transfer was created). Present on all transactions including pending ones |
postingTime | ISO-8601 timestamp when the transaction was settled and funds were moved on the account. null for pending/authorized transactions that have not yet settled |
billingAmount | Amount object ({ amount, currency }) — the actual billed amount in the account’s currency |
transactionAmount | Amount object ({ amount, currency }) — the original transaction amount (may differ for foreign currency). Can be null in cases where the payment was never reserved on the account |
currencyExchange | CurrencyExchange object with currency (string), targetCurrency (string), and exchangeRate (number) |
title | Transaction description |
type | Transaction type: dkPaymentSlip, domesticCreditTransfer, card, direct-debit, or unknown |
domesticCreditTransferInfo | Object with recipientBBAN (string, BBAN of the recipient, 11-14 chars) and message (string, optional). Present for domestic credit transfer transactions |
cardTransactionInfo | Object describing the card used and the merchant — merchantName, merchantCategoryCode, cardEntryMode, walletProvider, isAtm, and merchantLocation. Present for card transactions. See Card transaction info. |
status | Status: future, financial, authorization, interim, failed_authorization, declined, initiated, or unknown |
accountId | Parent account identifier |
attachmentIds | string[] — IDs of the attachments a user has added to this transaction. Populated on the transaction list endpoints and the get-by-id endpoint; omitted when the transaction has no attachments. See Attachments for downloading them. |
message | Optional free-text payment message associated with the transaction — for example, the sender note on a transfer, the message on a direct debit, or the description on a Swedish giro payment. Present when available; omitted otherwise. |
debtor | TransactionParty object ({ name, address, account?: { scheme, identification }, bic? }) — the party funds are debited from. Populated for transfer-like transactions. |
creditor | TransactionParty object ({ name, address, account?: { scheme, identification }, bic? }) — the party funds are credited to. Populated for transfer-like transactions. |
accountBalanceAfterTransaction | Amount object — the booked account balance after this transaction settled. Populated for settled transactions; omitted otherwise. |
additionalInformation | object (string map) — free-form identifiers that don’t fit other fields. See below for currently emitted keys. |
Transactions are returned with negative amounts for debits and positive amounts for credits.
Eventual consistency
Transaction data is served from a view that is updated over time as payments move through our transaction pipeline — not as a single atomic snapshot at creation.
- Transactions can appear in the API as soon as they are known, while optional fields are added or updated asynchronously as the payment progresses.
- A common lifecycle is
authorizationorinitiated(reserved or in flight) →financialwhen posted on the ledger.postingTimeis empty until the transaction is posted, then set (this corresponds tobookingDatein NextGenPSD2). - Same
id, different payloads on repeat fetches is normal. For example, a first response may havestatusauthorizationand nopostingTimeoraccountBalanceAfterTransaction; a later response for the same transaction may includepostingTime,accountBalanceAfterTransaction, andstatusfinancial. That reflects posting and enrichment — not duplicate transactions. - This applies to same-day activity, not only historical data. Enrichment can lag by seconds or minutes depending on product and posting path.
- Treat enrichment fields (
message,debtor,creditor,accountBalanceAfterTransaction,additionalInformation, and others) as optional until present. Absence on the first fetch does not mean the field will never appear. - For reconciliation and balance reconstruction, prefer transactions with
postingTimeandstatusfinancial. Re-fetch or poll recent activity when required fields are missing; do not assume one list call is complete for “today.” - Use stable
idfor deduplication when merging polls; merge updates into stored records rather than treating missing optional fields as removed.
This behaviour is expected platform behaviour, not an error in your integration.
Title and payment message
title and message serve different purposes:
title— the primary short transaction description (for example merchant name or counterparty name). It is what we show as the main label in the Lunar app. It is not the payment reference.message— optional payment or remittance text when available (sender note on a transfer, direct-debit message, international receiver message, and similar). Use this for unstructured reference or note text.- Structured Swedish payment references (OCR, bankgiro/plusgiro numbers, Swish ID) belong in
additionalInformation, not intitle. - For domestic credit transfers,
domesticCreditTransferInfo.messagemay still be present for backward compatibility. When both are available, prefer the top-levelmessage— it can contain the full multi-line text.
Billing amount vs. transaction amount
A transaction carries two amount fields:
transactionAmount— the amount in the original currency of the payment (e.g., EUR 20 for a purchase at a European shop). Can benullin some special cases for example when the payment was never reserved on the account, such as certain direct settlements.billingAmount— the amount that was actually posted to the account, in the currency of the posting. For foreign-currency transactions this is the converted amount after exchange.
When both currencies are the same, the two values will match. When they differ, the currencyExchange object provides the conversion details.
Always prefer billingAmount — it is the authoritative figure for balance
calculations and represents what actually entered or left the account.
Example: A card payment made for 20 EUR:
{
"billingAmount": { "amount": -149.0, "currency": "DKK" },
"transactionAmount": { "amount": -20.0, "currency": "EUR" },
"currencyExchange": {
"currency": "EUR",
"targetCurrency": "DKK",
"exchangeRate": 7.45
}
}Mapping properties for transaction fields
Here is a mapping of our statuses, to codes of the ISO 20022 data table:
| Lunar Status | Closest ISO 20022 | Rationale |
|---|---|---|
initiated | RCVD / PDNG | Transaction received/pending |
authorization | ACTC / PDNG | Awaiting authorization |
interim | ACSP | In process, not yet settled |
financial | ACSC / ACCC | Settled/completed |
future | PDNG | Scheduled for a future date |
failed_authorization | RJCT | Authorization failed |
declined | RJCT | Rejected |
unknown | — | A fault that isn’t described above, contact Lunar |
Mapping to NextGenPSD2 (Berlin Group) fields:
Lunar uses transactionTime and postingTime as full ISO-8601 timestamps. The NextGenPSD2 spec uses bookingDate, valueDate, and transactionDate as date-only (ISODate) fields. The mapping between them depends on the transaction status:
bookingDate — “The date when an entry is posted to an account on the ASPSP’s books.”
- Maps to
postingTime. Both represent when the transaction was recorded on the account. nullfor pending transactions, just asbookingDateis absent for pending entries in NextGen.
valueDate — “The date at which assets become available to the account owner in case of a credit, or cease to be available in case of a debit. If entry status is pending, refers to an expected/requested value date.”
- For settled transactions (
financialstatus): maps topostingTime. Lunar does not distinguish between the booking date and the value date — settlement, booking, and funds availability happen at the same time. - For future/scheduled transactions (
futurestatus): maps totransactionTime, which holds the scheduled execution date — equivalent to the “expected/requested value date” in NextGen. - For other pending transactions (
authorization,initiated): Lunar does not expose an expected value date. There is no direct equivalent.
transactionDate (NextGen card transactions only) — “Date of the actual card transaction.”
-
Maps to
transactionTime. Both represent when the transaction was initiated (e.g., the moment a card was swiped or a transfer was created).Reconciliation guidance:
-
Use
postingTimefor matching against bank statements and cash-flow reconciliation — this is when the account balance actually changed. It corresponds tobookingDatein NextGenPSD2. -
Use
transactionTimeto determine when the transaction was initiated, e.g., for dispute timelines or user-facing activity logs. -
For pending transactions (
authorization,initiated), onlytransactionTimeis available.postingTimeis populated once the transaction reachesfinancialstatus. See Eventual consistency for why fields can appear or change between fetches. -
For future/scheduled transactions,
transactionTimerepresents the expected execution date and can be used as an anticipated settlement date.
Debtor and creditor parties
Transfer-like transactions (domestic credit transfers, international payments, direct debits, bank giro, plus giro, Swish, e-faktura) carry structured debtor and creditor objects. The direction follows the sign of billingAmount:
- Incoming (positive
billingAmount) — the counterparty is the debtor (the sender); the account holder is the creditor. - Outgoing (negative
billingAmount) — the account holder is the debtor; the counterparty is the creditor.
All fields on the party object are optional. Consumers must not rely on any particular field being present.
| Field | Required | Description |
|---|---|---|
name | No | Name of the party. |
address | No | Address of the party (see below). |
account | No | Structured account identifier (see below). |
bic | No | SWIFT/BIC of the counterparty bank. |
address is a single free-text string, not a structured street/city/postcode model. When multiple lines are available they may be joined with newline characters. Format varies depending on source (bank-supplied data, payer-entered text, and similar) — do not parse into structured address fields without your own heuristics. The field is often omitted when only a counterparty name is known.
When account is present, both scheme and identification are guaranteed:
account.scheme | Description |
|---|---|
IBAN | International Bank Account Number (ISO 13616). |
BBAN | National Basic Bank Account Number, used for domestic transfers in DK, NO, and SE. |
ACCOUNT_NUMBER | Fallback for account identifiers that are neither an IBAN nor a BBAN. Treat as opaque. |
Additional schemes may be introduced in the future; consumers should treat unknown values defensively.
When the counterparty’s account identifier is not a bank account (e.g. bank giro, plusgiro, Swish), the account field is omitted and the relevant identifier is surfaced via additionalInformation instead.
Example: An incoming domestic credit transfer of DKK 500:
{
"billingAmount": { "amount": 500.0, "currency": "DKK" },
"debtor": {
"name": "Jane Doe",
"account": { "scheme": "BBAN", "identification": "12345678901" }
},
"creditor": {
"name": "John Smith",
"account": { "scheme": "BBAN", "identification": "98765432109" }
}
}Example: An outgoing international payment to a Danish IBAN:
{
"billingAmount": { "amount": -1000.0, "currency": "DKK" },
"creditor": {
"name": "Foreign Recipient",
"account": { "scheme": "IBAN", "identification": "DK1212345678901234" },
"bic": "BANKDKKKXXX"
}
}Transaction identifiers (id)
The id field is an opaque, stable identifier. Use it only for lookup and deduplication (for example GET /accounts/{accountId}/transactions/{transactionId} and merging results across polls).
Do not use id for business logic. Do not infer transaction type,
direction, payment reference, or product from substrings in the identifier.
IDs reflect historical naming conventions across many internal flows; a
substring such as payout is not a reliable signal of incoming vs outgoing or
of a specific product. Use structured fields instead (type, status,
billingAmount, message, additionalInformation, debtor / creditor,
and others).
Account balance after transaction
accountBalanceAfterTransaction carries the booked account balance immediately after a transaction settled, as an Amount object ({ amount, currency }). It is populated when that booked balance is available and omitted otherwise — including for same-day transactions that have not yet been enriched.
This field aligns with Berlin Group’s balanceAfterTransaction and is useful for reconstructing a running balance from a transaction list without a separate balance call.
See Eventual consistency for why the same transaction may lack this field on an earlier request and gain it after posting.
Card transaction info
Card transactions (type card) carry a cardTransactionInfo object describing
the card used and the merchant. All fields are optional and each is omitted
when the source data is unavailable — there are no empty placeholders.
| Field | Type | Description |
|---|---|---|
merchantName | string | Merchant name. |
merchantCategoryCode | number | Merchant Category Code (MCC). |
cardEntryMode | string | How the card credentials were captured. One of chip, contactless, magneticStripe, manual, merchantInitiated. Omitted when not specified. |
walletProvider | string | Digital wallet used when the card was tokenized for the payment. One of applePay, googlePay. Omitted when the payment was not made via a wallet. |
isAtm | boolean | true when the card transaction took place at an ATM. Omitted when the source data does not indicate an ATM transaction. |
merchantLocation | object | Structured merchant address (see below). Populated alongside merchantName/merchantCategoryCode when available. |
merchantLocation — structured merchant address. Every sub-field is optional.
| Field | Type | Description |
|---|---|---|
city | string | City of the merchant. |
country | string | ISO 3166-1 alpha-2 country code of the merchant. |
These are enrichment fields: they may be absent on an early fetch and appear on a later one as the transaction is enriched. See Eventual consistency and treat them as optional until present.
Example: A contactless Apple Pay card payment at a merchant:
{
"type": "card",
"billingAmount": { "amount": -149.0, "currency": "DKK" },
"cardTransactionInfo": {
"merchantName": "Coffee Shop",
"merchantCategoryCode": 5814,
"cardEntryMode": "contactless",
"walletProvider": "applePay",
"isAtm": false,
"merchantLocation": {
"city": "Copenhagen",
"country": "DK"
}
}
}Additional information map
additionalInformation is a free-form string-to-string map for identifiers that don’t fit other structured fields. It is omitted when empty. Consumers should treat unrecognized keys as opaque — new keys may be added without a version bump.
Structured OCR references (Swedish bankgiro, plusgiro, e-faktura) are exposed under the ocr key when available. The map or key is omitted when there is no OCR. OCR is not guaranteed on all transaction types (for example ordinary card payments or domestic transfers may have no ocr entry). Do not use title or message as the structured OCR channel — use additionalInformation.ocr when present. Reference-like text may still appear in message as free text.
Currently emitted keys:
| Key | Description |
|---|---|
bgNumber | Swedish bank giro recipient number |
pgNumber | Swedish plusgiro recipient number |
giroNumber | Generic giro identifier from e-faktura SE (may be a BG or PG number; not distinguishable at source) |
bgIssuer | Bank giro issuer identifier (BGS number) from e-faktura SE |
ocr | OCR reference for SE bank giro / plusgiro / e-faktura |
swishId | Swish payment identifier |
Get Transaction by ID
Retrieve a single transaction by its identifier.
GET /accounts/{accountId}/transactions/{transactionId}Path Parameters
| Parameter | Type | Description |
|---|---|---|
accountId | string | The account identifier |
transactionId | string | The transaction identifier |
Headers
| Header | Required | Description |
|---|---|---|
X-Language | No | Language preference |
Response Structure
Returns a single TransactionDetail object. This is a full
Transaction — every field described above,
including attachmentIds — plus one extra
field that is only returned by this endpoint (the list endpoints keep
returning the plain Transaction):
| Field | Type | Description |
|---|---|---|
deleted | boolean | Always present. true when the transaction has been deleted and no longer appears in the transaction lists; false otherwise. See Detecting deleted transactions. |
attachmentIds is populated on this endpoint as well (with the same rules as
the list endpoints — omitted when the transaction has no attachments). See
Attachments for downloading the files.
Detecting deleted transactions
A transaction can appear while it is still pending (status authorization)
and later fail — for example a declined or reversed authorization. When that
happens it simply stops appearing in the transaction lists (neither the flat
list nor the Transactions v2
pending/posted lists) without ever settling.
Because the list endpoints only tell you a transaction is absent, they cannot
distinguish “failed and removed” from “not yet fetched”. The get-by-id endpoint
resolves this: it keeps returning the transaction by its stable id and sets
deleted: true once it has been removed. Use it to confirm the outcome of a
pending transaction you previously stored instead of waiting for it to (possibly
never) reappear. See Eventual consistency for the wider
lifecycle behaviour.
Example: a previously pending transaction that later failed:
{
"id": "txn-a1",
"status": "authorization",
"transactionTime": "2025-01-31T18:22:00.000Z",
"billingAmount": { "amount": -149.0, "currency": "DKK" },
"title": "Coffee Shop",
"type": "card",
"accountId": "account-uuid",
"deleted": true
}Funds Confirmation
Check whether a specific amount is available in an account. This is useful before initiating a payment to ensure it won’t fail due to insufficient funds.
GET /funds-confirmationRequest Body
{
"accountId": "account-uuid",
"amount": 100.0,
"currency": "DKK"
}Response
{
"fundsAvailable": true
}Funds confirmation requires the PSP_PI (Payment Initiation) scope, not
PSP_AI.
Required Scopes
| Endpoint | Required Scope |
|---|---|
GET /accounts | PSP_AI |
GET /accounts/{accountId}/transactions | PSP_AI |
GET /accounts/{accountId}/transactions/{transactionId} | PSP_AI |
GET /funds-confirmation | PSP_PI |
Error Handling
Each endpoint defines its own set of error responses:
| Endpoint | 400 | 404 | 500 |
|---|---|---|---|
GET /accounts | ✓ | ||
GET /accounts/{accountId}/transactions | ✓ | ||
GET /accounts/{accountId}/transactions/{transactionId} | ✓ | ✓ | ✓ |
GET /funds-confirmation | ✓ | ✓ |
| Status | Description |
|---|---|
400 | Invalid request parameters |
404 | Resource not found |
500 | Internal server error |
Error responses include an errorCode and message for debugging.