Transaction feed
Use the transaction feed to reconcile a Hubpay wallet against your own ledger. It returns every entry on the wallet in the order it happened, and each page carries totals you can check against your running totals, so you know whether you have missed anything.
What you need
- If you integrate through a connected app, it needs the
TRANSACTION_FEED_READscope, requested through the connection flow. If you integrate your own Hubpay account with your own credentials, use Banking and money movement and no app connection is needed. - The wallet to read. List wallets and balances returns the wallets the connection can see; a wallet it cannot see returns
404.
Routes
| Route | Use |
|---|---|
GET /v1/wallets | List wallets. |
GET /v1/wallets/{walletId} | Read one wallet's balances and historyStart. |
GET /v1/wallets/{walletId}/entries | Page through the wallet's entries. |
The entries route takes three query parameters:
status:POSTED(the default) orALL. Any other value returns400.cursor: thenextCursorfrom the previous page. Omit it to start from the beginning of the wallet's history. A cursor that cannot be parsed returns400.limit: the maximum number of entries to return, from 1 to 500.
All timestamps are in UTC and use ISO 8601 format with a Z suffix, for example 2026-10-06T09:30:00Z. Amounts are exact decimal strings.
The wallet
| Field | Meaning |
|---|---|
accountId | The wallet's identifier. |
currency | The wallet's currency. |
postedBalance | The balance of posted entries, including funds that are reserved for payments in progress. |
reservedBalance | Funds set aside for payments in progress. |
pendingHolds | Card and other holds that have not posted yet. |
availableBalance | postedBalance minus reservedBalance minus pendingHolds. |
visibleThrough | How far the feed is complete. See Entries appear after a short delay. |
historyStart | The time of the wallet's first visible entry. |
The balances on the wallet are current. They are not the balance at visibleThrough, so they can include postings that the feed does not show yet. To compare balances at a known point, use balanceAfter on a page.
Entries
Each entry has these fields and no others.
| Field | Meaning |
|---|---|
entryId | A stable identifier of the form <prefix>-<journal id>. See Entry identifiers. |
status | POSTED, RESERVED, RELEASED or CONSUMED. See Posted entries and reservation events. |
direction | CREDIT or DEBIT, from the wallet's point of view. |
amount | The amount, as an exact decimal string. |
currency | The currency of the entry. |
occurredAt | When the event happened. |
bookingDate | The ledger entry date in the Asia/Dubai time zone. Present on posted entries only. |
description | The wording the customer statement uses for this entry, or null when the entry has no statement wording. |
category | What kind of entry this is. See Categories. |
hubpayReference | The Hubpay transaction reference. Entries of one transaction share it. |
relatedEntryIds | The other entries of the same transaction. See Related entries. |
counterparty | name, iban and paymentReference of the other party, or null. |
Entry identifiers
An entryId never changes once the entry has been returned. Use it as your key when you store entries. Do not read anything into the journal id inside it.
| Prefix | Entry |
|---|---|
PFU | Funding |
PCO | Collection |
PPY | Payment |
PFE | Fee |
PFX | FX |
PRT | Return |
PTR | Transfer |
PAD | Adjustment |
POB | Opening balance |
POT | Other |
RPL | Reservation placed |
RRL | Reservation released |
RCN | Reservation consumed |
RCF | Reservation consumed, fee |
The P prefixes are posted entries. The R prefixes are reservation events, which you only receive with status=ALL.
Posted entries and reservation events
With status=POSTED, you receive posted entries only, and every entry has status POSTED.
With status=ALL, you also receive reservation events. A reservation is funds set aside for a payment while it is in progress:
RESERVED: funds were set aside.RELEASED: the funds were given back.CONSUMED: the payment completed and used the funds. A completion gives oneCONSUMEDevent for each reserved amount it uses, so a payment with a fee gives a principal event (RCN) and a fee event (RCF).
Reservation events have no bookingDate.
Categories
category is one of FUNDING, COLLECTION, PAYMENT, FEE, FX, RETURN, TRANSFER, ADJUSTMENT, OPENING_BALANCE or OTHER. The category tells you whether an entry is principal or a fee. Reservation events are PAYMENT, or FEE for a fee.
Categorise entries by category, not by parsing description.
Descriptions
description carries the customer statement wording where one exists. It is null for an entry that has none; there are no substitute labels.
Related entries
relatedEntryIds lists the other entries of the same transaction:
- on a posted entry, the other posted entries of the transaction, for example the fee of a payment, or the original of a return;
- on
RESERVEDandRELEASED, the posted entries of the same transaction; - on
CONSUMED, the posted entries of the completion that consumed it.
Counterparty
counterparty holds name, iban and paymentReference, or is null when the other party is not known.
Pages
Each response is a page with these fields.
| Field | Meaning |
|---|---|
entries | The entries on this page, oldest first. |
nextCursor | The cursor for the next request. When the page is empty it equals the cursor you sent. |
balanceAfter | The wallet balance from the start of history to nextCursor. |
entryCountThrough | The number of posted entries from the start of history to nextCursor. Reservation events are not counted. |
openItemsTotal | The running net of reservation events from the start of history to nextCursor: placed, minus released, minus consumed. |
visibleThrough | How far the feed is complete. |
balanceAfter, entryCountThrough and openItemsTotal always cover the whole history up to nextCursor, whichever status you ask for. openItemsTotal counts reservation events whether or not the page lists them.
A page holds whole journals, so it can be larger than limit when one journal has more entries than the page has room for. Cursors are opaque and never expire. Do not try to read their contents.
Sync protocol
There is currently no webhook for the feed, but this will be implemented soon. In the meantime, poll on your own schedule; every 1 to 5 minutes is typical.
- Start without a
cursorand read pages until one comes back empty. - Store each entry by
entryId. Entries never change once returned, so a repeat of an entry you already hold is a no-op. - Keep the
nextCursorof the last page. Next time, send it ascursorand carry on from there. - After each page, check your running totals against the page, as described below.
Check every page
With status=POSTED, keep two running totals of posted entries: the sum of credits minus debits, and the count. After each page, both must match the page:
- the sum equals
balanceAfter; - the count equals
entryCountThrough.
You need both checks. If you miss one entry and read another twice, or miss two entries whose amounts offset, the sum can still match while the count does not. The count catches these skips.
If either check fails, you have missed an entry. Re-read from the last cursor at which both checks matched, and upsert by entryId.
With status=ALL, the posted checks are unchanged. In addition, keep the running net of the reservation events you read: RESERVED amounts, minus RELEASED amounts, minus CONSUMED amounts. After each page it must equal openItemsTotal. A mismatch means you missed a reservation event. Re-read in the same way.
balanceAfter minus openItemsTotal is the available balance at that point in the history, before card holds.
Entries appear after a short delay
An entry appears in the feed about two minutes after it happens. This delay keeps the order of the feed consistent, so that an entry never turns up behind a cursor you have already passed.
visibleThrough tells you how far the feed is complete. A page never contains anything after it. postedBalance on the wallet is current, so it can be ahead of what you have read; balanceAfter on a page is the balance at the cursor.
Differences from the customer statement
The feed and the customer statement show the same wording where an entry has statement wording. They differ in these ways:
- Scope. The statement covers the wallet's posted entries. The feed also covers the reservations that set funds aside for payments.
- Reservations. The statement shows a payment when its funds are reserved. With
status=POSTEDthe feed shows it when it completes; withstatus=ALLthe feed also lists the reservation event at the time funds were set aside. - Entries without wording. The statement leaves out entries that have no statement wording. The feed includes them, with
descriptionset tonulland acategory.