Retrieving Transactions
Read transactions from your backend with the REST API, or inside your app with the SDK. Use the backend for reporting, reconciliation, and anything that spans merchants. Use the SDK for the signed-in merchant's own history screen.
Every method returns the same transaction object. The Transaction Result explains its fields.
Choose a Method
| You need | Use |
|---|---|
| A filtered, sorted, paged list | GET /v2/transactions |
| One transaction with its history | GET /v1/transactions/{transaction_id} |
| The result of one operation whose event ID you saved | GET /v1/transactions/event/{event_id} |
| The signed-in merchant's history inside your app | The SDK history methods in Show Transactions in Your App |
Call the REST endpoints from your backend with an API key that holds payments:read. A key sees its own account and the accounts beneath it.
List and Search
GET /v2/transactions returns one page of transactions, newest first unless you sort otherwise. Every filter is optional. Filters combine with AND, and a filter you repeat matches any of its values.
curl --get 'https://api.uat.koard.com/v2/transactions' \
-H 'X-Koard-apikey: YOUR_API_KEY' \
--data-urlencode 'account_id=YOUR_MERCHANT_ACCOUNT_ID' \
--data-urlencode 'status=captured' \
--data-urlencode 'status=settled' \
--data-urlencode 'start_date=1767225600000' \
--data-urlencode 'sort_by=created_at' \
--data-urlencode 'sort_order=desc' \
--data-urlencode 'limit=50' \
--data-urlencode 'offset=0'
The response wraps the page in a paging envelope. This example is abridged.
{
"transactions": [
{
"transaction_id": "YOUR_TRANSACTION_ID",
"event_id": "YOUR_EVENT_ID",
"status": "captured",
"status_reason": "approved",
"transaction_type": "sale",
"currency": "USD",
"subtotal": 1000,
"tax_amount": 85,
"tip_amount": 200,
"total_amount": 1285,
"card": "****1234",
"card_brand": "Visa",
"created_at": 1767229200000
}
],
"total": 132,
"limit": 50,
"offset": 0,
"page": 1
}
Filters
| Filter | Matches |
|---|---|
account_id |
Transactions of a merchant account, or of every merchant beneath a partner or PSP account. Repeatable. |
status |
One or more status values. Repeatable. |
transaction_type |
One or more operations: sale, auth, capture, refund, reverse, tip_adjust, incremental_auth, or manually_keyed_sale. Repeatable. |
location_id, terminal_id, batch_id |
The location, terminal, or settlement batch. |
start_date, end_date |
A created_at range in milliseconds since the Unix epoch. |
min_amount, max_amount |
A total_amount range in minor currency units. |
transaction_id, event_id, gateway_transaction_id |
An exact identifier. |
card_number |
Part of the masked card number, such as the last four digits. |
search_term |
Free text across common fields. |
device_type |
iphone, ipad, android_phone, android_tablet, other, or unknown for transactions recorded without a device family. |
payment_method, card_brand |
The exact label stored on the transaction. Labels differ by processor, so read the value from a transaction before you filter on it. |
processor_mid, processor_tid |
Part of the processor merchant ID or terminal ID. |
reader_identifier, owner_id |
The reader that took the tap, or the owner account, usually the parent PSP or partner. |
Sorting
| Parameter | Values | Default |
|---|---|---|
sort_by |
created_at, total_amount, transaction_id, status, card_brand |
created_at |
sort_order |
asc, desc |
desc |
Paging
| Parameter | Meaning | Default | Maximum |
|---|---|---|---|
limit |
Transactions per page | 50 | 200 |
offset |
Transactions to skip | 0 | None |
Read total from the first page, then raise offset by limit until offset + transactions.length reaches total. Keep the filters and sort the same for every page. Pagination lists the page sizes of the other list endpoints.
Get One Transaction
curl 'https://api.uat.koard.com/v1/transactions/YOUR_TRANSACTION_ID' \
-H 'X-Koard-apikey: YOUR_API_KEY'
The response is the transaction with its history of earlier events. A transaction that does not exist and one that your key cannot see both return 404.
When a payment response was lost, look the operation up by the event ID you saved before you sent it: GET /v1/transactions/event/{event_id}. A 404 there does not prove that the payment never reached the processor. Follow Idempotency and Outcome Recovery before you retry.
Show Transactions in Your App
For the signed-in merchant's own history, call the SDK from the app.
| Platform | Methods |
|---|---|
| Android | getTransactions(limit, offset) and getTransaction(transactionId). See Transaction history and receipts. |
| iOS | transactionHistory(), transactionsByStatus(...), searchTransactions(...), and searchTransactionsAdvanced(...). See Running Payments. |
For history that spans merchants, or filters the SDK does not offer, have your backend call GET /v2/transactions and return the rows your screen needs. Do not ship a backend key in the app.
What to Show
| In a list row | From |
|---|---|
| Amount | total_amount in minor units, formatted with currency. On a refund row this is the refund event's total, not the original payment. |
| Status | status, mapped to your own label |
| Card | card_brand and the last four digits of card |
| Time | created_at, converted from milliseconds to the device's local time |
In a detail view, add the breakdown (subtotal, tax_amount, tip_amount, surcharge_amount), the refunded and reversed totals, the processor response, and the history of events.
Refunded Payments
A list row is the latest event on a payment. After a refund, that event is the refund itself. Its transaction_type is refund, and its total_amount and breakdown describe that event, not the original payment. What total_amount holds on a refund event differs by processor, so never use it as the payment's amount.
refunded is the cumulative amount returned across every refund of the payment. A refunded total above zero means some of the payment was returned, whatever the current status.
To show whether a refund is partial or full, read the payment's own amount from the detail view. GET /v1/transactions/{transaction_id} returns the earlier events in history, newest first. Take total_amount from the newest event that is not a refund or a reversal, and compare the cumulative refunded total with it. Do not compare refunded with the refund event's own total_amount. Refund describes the fields of a refund event.
Map each status to a label the operator understands. A preauth that is authorized is a hold, not a completed sale.
| Status | Suggested label |
|---|---|
captured, settled |
Paid |
authorized |
On hold, not captured yet |
pending, surcharge_pending |
In progress |
declined |
Declined |
error |
Needs review |
refunded |
Refunded. See Refunded Payments to show a partial refund. |
reversed |
Reversed |
canceled |
Canceled |
This example turns one transaction from the API into a row for a history screen.
type TransactionRow = {
id: string;
amount: string;
status: string;
card: string;
time: string;
};
const STATUS_LABELS: Record<string, string> = {
captured: "Paid",
settled: "Paid",
authorized: "On hold",
pending: "In progress",
surcharge_pending: "In progress",
declined: "Declined",
error: "Needs review",
refunded: "Refunded",
reversed: "Reversed",
canceled: "Canceled",
};
// Amounts are integers in minor units. The number of decimal places
// depends on the currency, so do not divide by a fixed 100.
function formatMinor(amount: number, currency: string): string {
const formatter = new Intl.NumberFormat(undefined, { style: "currency", currency });
const digits = formatter.resolvedOptions().maximumFractionDigits ?? 2;
return formatter.format(amount / 10 ** digits);
}
function toRow(transaction: any): TransactionRow {
return {
id: transaction.transaction_id,
// On a refund row this is the refund event's total, not the original payment.
amount: formatMinor(transaction.total_amount, transaction.currency),
status: STATUS_LABELS[transaction.status] ?? "Unknown",
card: `${transaction.card_brand} ${transaction.card.slice(-4)}`,
time: new Date(transaction.created_at).toLocaleString(),
};
}
In the detail view, work out the refund state from the original payment, not from the refund event. Pass the result of GET /v1/transactions/{transaction_id}.
type RefundState = "none" | "partial" | "full" | "unknown";
function refundState(transaction: any): RefundState {
const refunded = transaction.refunded ?? 0;
if (refunded <= 0) return "none";
// The current event comes first, then history from newest to oldest.
const events = [transaction, ...(transaction.history ?? [])];
const payment = events.find(
(event: any) =>
!["refund", "reverse"].includes(event.transaction_type) &&
["authorized", "captured", "settled"].includes(event.status),
);
if (!payment) return "unknown";
return refunded < payment.total_amount ? "partial" : "full";
}
Keep transaction_id on each row so a tap opens the detail view, and so capture, tip adjust, reverse, and refund act on the right payment.

