Z Zise Developers 简体中文

Member transactions (cursor pagination)

GET /v1/transactions scope: balances:read
On behalf of a member · x-on-behalf-of required

Member-facing transactions across all business lines: deposits, withdrawals, remittances, exchanges, transfers, card issuing, wealth products, and QR payments. These are not ledger entries. A business transaction appears as at most one row here, even if its ledger journal has four legs.

There is one pagination method: pass back the previous page's next_cursor unchanged. The cursor is opaque; do not parse or construct it yourself. Otherwise changing our sort key would break your integration. An undecodable cursor restarts from the beginning without an error.

⚠ Always use amount_scale. Amounts are decimal representations of fixed-point integers scaled for each asset; precision differs by asset, such as 6 for USDT and 8 for BTC. Hardcoding 6 produces a factor-of-100 error on the first non-6-decimal asset, without an error response.

⚠ type and status are open enums with no database CHECK constraint. New business lines add values without sending you a release notification. Display unknown values unchanged; never default to processing or success, which could present Awaiting manual review as completed. For grouping, maintain an allowlist of known values and a fallback branch for the rest.

direction has only two values: in for incoming funds, out for outgoing funds.

For end-of-day reconciliation, use incremental from / to queries rather than repaging all history

from / to and the cursor use the same column, created_at, so from = 上一轮见过的最大 created_at is a consistent rule for incremental synchronization. to is exclusive (< to); an inclusive bound would retrieve the boundary row in both intervals, causing it to be counted twice during reconciliation.

⚠ Times must be ISO-8601, such as 2026-08-01T00:00:00Z. Unparseable values return 400 invalid_fields; they are not silently ignored. Ignoring them would produce plausible-looking data with an incorrect range because the filter did not apply.

⚠ Do not use occurred_at as the incremental watermark. Both timestamps are returned: occurred_at is when the event happened, using detection time for deposits, while created_at is when we recorded it. They may differ greatly for backfilled historical transactions, which an occurred_at watermark would skip permanently.

Query Parameters

FieldTypeRequiredDescription
limit integer Optional Items per page. Defaults to 20, maximum 100. Nonnumeric values or values ≤ 0 use 20 without an error, so do not rely on validation to detect incorrect input.
cursor string Optional Pass the previous page's next_cursor unchanged. Omit for the first page.
id string Optional Retrieve only one transaction. Accepts an ID with or without the txn_ prefix. Nonnumeric values return 400 invalid_fields immediately.
type string Optional Filter by business type using an exact, case-sensitive match. ⚠ This is an open enum; we deliberately do not validate values. New business lines add values without notifying you of a release. An allowlist here would make your filter miss orders as soon as we launch a new line. Unknown values therefore return an empty list, not an error; misspellings are not reported.
status string Optional Filter by status, likewise an open enum with no validation; misspellings return an empty list.
direction string Optional in or out. ⚠ Unlike the preceding filters, this is a closed enum, with exactly two values enforced by the database, so any other value immediately returns 400 invalid_fields.
asset string Optional Filter by asset, automatically converted to uppercase. Existence is not validated: this is a filter, not an order parameter. Querying an asset the member does not hold returns an empty list, not asset_not_allowed.
from string Optional Start time, inclusive, in ISO-8601. Compared against created_at.
to string Optional End time, exclusive, in ISO-8601. Compared against created_at.
FieldTypeRequiredDescription
x-on-behalf-of string Required Whose transactions to query.

Response

200OK
{
  "data": [
    {
      "id": "txn_80231",
      "type": "withdrawal",
      "status": "locked",
      "status_version": 1,
      "direction": "out",
      "asset": "USDT",
      "amount": "200000000",
      "amount_scale": 6,
      "occurred_at": "2026-08-12T09:31:02.881Z",
      "created_at": "2026-08-12T09:31:02.881Z"
    },
    {
      "id": "txn_80198",
      "type": "deposit",
      "status": "completed",
      "status_version": 2,
      "direction": "in",
      "asset": "USDT",
      "amount": "1500000000",
      "amount_scale": 6,
      "occurred_at": "2026-08-12T08:04:55.010Z",
      "created_at": "2026-08-12T08:04:57.204Z"
    }
  ],
  "next_cursor": "MjAyNi0wOC0xMlQwODowNDo1N1p8ODAxOTg",
  "has_more": true
}
400member_context_required: missing x-on-behalf-of. invalid_fields: id is not numeric, direction is not in|out, or from / to is not valid ISO-8601; inspect the fields array.
404member_not_found: member does not exist, belongs to another merchant, or is suspended.
Request
curl -X GET 'https://api.zinfra.vip/v1/transactions' \
  -H 'x-auth-token: Bearer $TOKEN' \
  -H 'x-on-behalf-of: $MEMBER_ID'
const res = await fetch("https://api.zinfra.vip/v1/transactions", {
  method: "GET",
  headers: {
    "x-auth-token": "Bearer $TOKEN",
    "x-on-behalf-of": "$MEMBER_ID",
  },
});
// Keep monetary amounts as strings, never numbers.
const data = await res.json();
import requests

res = requests.get(
    "https://api.zinfra.vip/v1/transactions",
    headers={
        "x-auth-token": "Bearer $TOKEN",
        "x-on-behalf-of": "$MEMBER_ID",
    },
)
# Use Decimal(str(...)) for amounts, not float.
data = res.json()
req, _ := http.NewRequest("GET", "https://api.zinfra.vip/v1/transactions",
    nil)
req.Header.Set("x-auth-token", "Bearer $TOKEN")
req.Header.Set("x-on-behalf-of", "$MEMBER_ID")
res, err := http.DefaultClient.Do(req)
// Decode amount fields as string, not float64.
HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://api.zinfra.vip/v1/transactions"))
    .header("x-auth-token", "Bearer $TOKEN")
    .header("x-on-behalf-of", "$MEMBER_ID")
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
// Use String / BigDecimal for amounts, not double.
$ch = curl_init('https://api.zinfra.vip/v1/transactions');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    'x-auth-token: Bearer $TOKEN',
    'x-on-behalf-of: $MEMBER_ID',
  ],
]);
$res = curl_exec($ch);
// Use bcmath / strings for amounts, not floatval.
200
{
  "data": [
    {
      "id": "txn_80231",
      "type": "withdrawal",
      "status": "locked",
      "status_version": 1,
      "direction": "out",
      "asset": "USDT",
      "amount": "200000000",
      "amount_scale": 6,
      "occurred_at": "2026-08-12T09:31:02.881Z",
      "created_at": "2026-08-12T09:31:02.881Z"
    },
    {
      "id": "txn_80198",
      "type": "deposit",
      "status": "completed",
      "status_version": 2,
      "direction": "in",
      "asset": "USDT",
      "amount": "1500000000",
      "amount_scale": 6,
      "occurred_at": "2026-08-12T08:04:55.010Z",
      "created_at": "2026-08-12T08:04:57.204Z"
    }
  ],
  "next_cursor": "MjAyNi0wOC0xMlQwODowNDo1N1p8ODAxOTg",
  "has_more": true
}