Transactions
Search payments across every application you are scoped to, export the filtered set as CSV, and read per-app dashboard metrics and daily trends.
Operators with payment:read can search payments across every application their
grants cover, and export the same filtered set as CSV with
payment:export. These are admin, session-authenticated endpoints under
/api/v1/admin/payments — distinct from the app-API-key
payments API, whose reads are confined to the one
authenticating app and mode.
Every admin endpoint on this page is session-authenticated: send your admin
session token as Authorization: Bearer SESSION_TOKEN, not an app API key.
Results are always narrowed to the apps your RBAC grants cover — you never see
another app's transactions (app isolation).
Every response uses the standard envelope —
{ "data": …, "meta": { … }, "pagination": … }. The examples below show the
data payload; see responses and errors
for the full shape.
Search
GET /admin/payments (gated by payment:read) returns a
paginated list of payment summaries,
automatically narrowed to the apps your grants cover.
Query parameters
| Field | Type | Required | Notes |
|---|---|---|---|
applicationId | integer | No | A specific app within your scope. |
mode | enum (TEST | LIVE) | No | Filter by mode; an invalid value is rejected with 400. |
status | enum | No | One of CREATED, PENDING, SUCCEEDED, FAILED, CANCELLED, EXPIRED, PARTIALLY_REFUNDED, REFUNDED. |
reference | string | No | Exact payment reference. |
customerRefId | integer | No | The app's internal customer-reference id. |
from | date (YYYY-MM-DD) | No | Inclusive start of the createdAt window. |
to | date (YYYY-MM-DD) | No | Inclusive end of the createdAt window. |
page | integer | No | Zero-based page index. Default 0. |
size | integer | No | Page size. Default 20. |
sortBy | string | No | Sort field. Default createdAt. |
order | enum (ASC | DESC) | No | Sort direction. Default DESC. |
paginate | boolean | No | When false, returns the full unpaged set. Default true. |
curl -G "http://localhost:8080/api/v1/admin/payments" \
-H "Authorization: Bearer SESSION_TOKEN" \
--data-urlencode "applicationId=42" \
--data-urlencode "mode=LIVE" \
--data-urlencode "status=SUCCEEDED" \
--data-urlencode "from=2026-06-01" \
--data-urlencode "to=2026-06-30"{
"data": [
{
"id": 90142,
"reference": "PAY-7H2K9Q8M",
"applicationId": 42,
"applicationName": "Acme Storefront",
"mode": "LIVE",
"status": "SUCCEEDED",
"amountMinor": 150000,
"currency": "BDT",
"customerRefId": 5567,
"customerExternalId": "cust_8842",
"customerName": "Rahim Uddin",
"invoiceId": 3310,
"gateway": "SSLCOMMERZ",
"gatewayTxnId": "2406301230ACME",
"createdAt": "2026-06-28T09:14:05"
}
],
"meta": { "success": true, "message": null, "errorCode": null, "timestamp": "2026-06-30T12:00:00Z" },
"pagination": { "page": 0, "size": 20, "totalElements": 42, "totalPages": 3 }
}Response row — PaymentSummaryDto
Null fields are omitted from the JSON. Fields marked CSV are also written by the export; the customer-contact fields are enrichment for the dashboard and are JSON-only.
| Field | Type | Required | Notes |
|---|---|---|---|
id | integer | Yes | Payment id. CSV |
reference | string | Yes | Opaque payment reference. CSV |
applicationId | integer | Yes | Owning app. CSV |
applicationName | string | No | Resolved for display; omitted when the owning app can no longer be found. CSV |
mode | string | Yes | TEST or LIVE. CSV |
status | string | Yes | Payment status. CSV |
amountMinor | integer | Yes | Amount in integer minor units (150000 = 1,500.00 BDT). CSV |
currency | string | Yes | Always BDT in v1. CSV |
customerRefId | integer | No | The app's internal customer-reference id. CSV |
customerExternalId | string | No | The app's external customer id (enriched). |
customerName | string | No | Customer display name (enriched). |
customerEmail | string | No | Customer email (enriched). |
customerPhone | string | No | Customer phone (enriched). |
invoiceId | integer | No | Settling invoice, when the payment settles one. CSV |
gateway | string | No | Payment gateway (e.g. SSLCOMMERZ). CSV |
gatewayTxnId | string | No | Gateway transaction handle. CSV |
createdAt | datetime | Yes | Creation timestamp. CSV |
updatedAt | datetime | No | Last-update timestamp (JSON only). |
Export
GET /admin/payments/export (gated by payment:export) streams the same
filtered set as the search — identical query parameters, same app-scope
narrowing — as a text/csv attachment named transactions.csv. The export is
sorted by id descending and is not paginated: every matching row is written,
so you export exactly what you're looking at — handy for finance hand-offs.
The header row is fixed, in this exact column order:
id,reference,applicationId,applicationName,mode,status,amountMinor,currency,customerRefId,invoiceId,gateway,gatewayTxnId,createdAtOnly those 13 columns are exported; the customer-contact fields the search JSON
enriches (customerName, customerEmail, customerPhone, customerExternalId)
and updatedAt are not in the CSV. amountMinor is in
integer minor units (150000 = 1,500.00 BDT).
curl -G "http://localhost:8080/api/v1/admin/payments/export" \
-H "Authorization: Bearer SESSION_TOKEN" \
--data-urlencode "applicationId=42" \
--data-urlencode "mode=LIVE" \
--data-urlencode "from=2026-06-01" \
--data-urlencode "to=2026-06-30" \
-o transactions.csvid,reference,applicationId,applicationName,mode,status,amountMinor,currency,customerRefId,invoiceId,gateway,gatewayTxnId,createdAt
90142,PAY-7H2K9Q8M,42,Acme Storefront,LIVE,SUCCEEDED,150000,BDT,5567,3310,SSLCOMMERZ,2406301230ACME,2026-06-28T09:14:05Dashboard metrics
GET /admin/payments/metrics (gated by payment:read) returns the per-app,
mode-aware key metrics for the dashboard metrics row. Unlike search and export,
both applicationId and mode are required query parameters.
Admin sessions are not bound to a single mode the way an app API key is, so the
dashboard reads (/metrics and /timeseries) take mode as an explicit,
required query parameter. This is the same exception documented on
dashboard analytics — it does not contradict
the modes rule that an app-API request's mode is derived
from its credential, because an admin session carries no app credential.
Query parameters
| Field | Type | Required | Notes |
|---|---|---|---|
applicationId | integer | Yes | The app to aggregate. Outside your scope → all-zero metrics. |
mode | enum (TEST | LIVE) | Yes | The mode to aggregate. |
from | date (YYYY-MM-DD) | No | Inclusive start of the createdAt window. Defaults to all time. |
to | date (YYYY-MM-DD) | No | Inclusive end of the createdAt window. Defaults to all time. |
curl -G "http://localhost:8080/api/v1/admin/payments/metrics" \
-H "Authorization: Bearer SESSION_TOKEN" \
--data-urlencode "applicationId=42" \
--data-urlencode "mode=LIVE" \
--data-urlencode "from=2026-06-01" \
--data-urlencode "to=2026-06-30"{
"data": {
"totalCount": 318,
"succeededCount": 297,
"failedCount": 21,
"grossSucceededMinor": 44550000,
"successRate": 0.9339622641509434
},
"meta": { "success": true, "message": null, "errorCode": null, "timestamp": "2026-06-30T12:00:00Z" },
"pagination": null
}Response — PaymentMetricsDto
| Field | Type | Required | Notes |
|---|---|---|---|
totalCount | integer | Yes | Payments created in the window. |
succeededCount | integer | Yes | How many reached SUCCEEDED. |
failedCount | integer | Yes | How many reached FAILED. |
grossSucceededMinor | integer | Yes | Gross succeeded amount in minor units. |
successRate | number | Yes | succeededCount / totalCount as a 0–1 fraction (0 when there are no payments). |
The endpoint always returns concrete zeros (never null) for an app with no
payments in the window — and the same all-zero object when you are not scoped
to the requested app, so callers can't probe app existence.
Daily time-series
GET /admin/payments/timeseries (gated by payment:read) returns one point per
calendar day for the dashboard revenue/volume trend. It takes the same required
applicationId + mode and optional from/to window as /metrics. Days
with no payments are omitted (the dashboard fills the gaps client-side); an app
outside your scope yields an empty list.
curl -G "http://localhost:8080/api/v1/admin/payments/timeseries?applicationId=42&mode=LIVE" \
-H "Authorization: Bearer SESSION_TOKEN"{
"data": [
{ "date": "2026-06-28", "totalCount": 12, "succeededCount": 11, "grossSucceededMinor": 1650000 },
{ "date": "2026-06-29", "totalCount": 9, "succeededCount": 9, "grossSucceededMinor": 1350000 }
],
"meta": { "success": true, "message": null, "errorCode": null, "timestamp": "2026-06-30T12:00:00Z" },
"pagination": null
}Response row — PaymentDayPointDto
| Field | Type | Required | Notes |
|---|---|---|---|
date | date (YYYY-MM-DD) | Yes | The calendar day. |
totalCount | integer | Yes | Payments created that day. |
succeededCount | integer | Yes | How many succeeded that day. |
grossSucceededMinor | integer | Yes | Gross succeeded amount that day, in minor units. |
No single-transaction endpoint
There is no GET /admin/payments/{id}. The admin transaction surface is the
cross-app search list, export, metrics and time-series above — the
only /{id} path under /admin/payments is POST /admin/payments/{id}/refunds,
which opens a dashboard-initiated refund (see refunds and
refund approval). To inspect one payment, filter
the search by its reference or customerRefId. App integrators read a single
payment by id through the app-API payments endpoint,
not this admin surface.