Swagger

Transactions

12 endpoints.

12 endpoints

Transactions

POST/transaction/preparePrepare (generate user operation)

Build an unsigned operation for the given calls on the UserOperation rail. transferredAssets are the asset(s) the gasless fee is charged against and they drive its percentage part. Sign the returned userOperationHash client-side, then call POST /transaction/submit.

  • ugtpFee is { tokenAmount, expectedTokenAmount, feeToken } in base units: tokenAmount is the worst case to reserve against, expectedTokenAmount what the operation is expected to bill.
  • authorizationRequired and authorizationNonce say when an EIP-7702 authorization must be attached on submit — the first operation of an account that has not adopted the delegate yet, and never a later one.
  • The prepared operation is held server-side for a limited time (default 30 minutes). If it expires before submit, prepare again.
  • 409 when the same operation was already materialized.
  • Coded 422 when the simulated calldata reverts deterministically: SWAP_AMOUNT_TOO_SMALL when a route rejects the amount as below its minimum, or EXECUTION_SIMULATION_REVERTED with the raw revert reason.
  • 404 for an unknown accountId. 400 when that account belongs to another network, or when EIP-7702 is not supported on the target network — there use /transaction/transfer or /swap/plain/prepare, which answer with the rail that does carry the operation.
  • 429 when the rate limit is exceeded; check the Retry-After response header.

Authentication: X-API-Key

Input — request body

accountId string required
Account id from POST /account/create
networkId string required
Chain id
calls Call[] required
Each: { to, data?, value? } (hex)
transferredAssets Asset[] required
Each: { address, amount } (base-unit hex); drives the percentage fee
feeToken string
Fee token address (omit for native-gas)

Input — example request

{
  "accountId": "ugtp_account_R4LJbmiFqDsJ",
  "networkId": "42161",
  "calls": [
    {
      "to": "0x912ce59144191c1204e64559fe8253a0e49e6548",
      "value": "0x0",
      "data": "0xa9059cbb...transfer of 2148465421470024202 to 0xCa9d...3c60"
    }
  ],
  "transferredAssets": [
    {
      "address": "0x912ce59144191c1204e64559fe8253a0e49e6548",
      "amount": "0x1dd0e1eaf2ee420a"
    }
  ]
}

Output — example response (201)

{
  "operationId": "ugtp_operation_E6SnXDoAl9AQ",
  "userOperation": {
    "sender": "0x9b5fcd6f647a14ebf0f88b1c3e5a3d34b951fc6f",
    "nonce": "0x2",
    "initCode": "0x",
    "callData": "0xb61d27f6000000000000000000000000912ce59144191c…228 bytes",
    "accountGasLimits": "0x000000000000000000000000000084b800000000000000000000000000014393",
    "preVerificationGas": "0xc208",
    "gasFees": "0x00000000000000000000000000000000000000000000000000000000016eafe0",
    "paymasterAndData": "0x49f26881b5ea4d3f3d451c7faa4bca1b4aebcd97…214 bytes",
    "signature": "0x"
  },
  "userOperationHash": "0x1a4fece09e742c04398006ebdae6d03227aa7e822ba825e0c64b44fc78d12cb3",
  "security": {
    "verdict": "safe",
    "reason": null,
    "provider": "checkpoint"
  },
  "ugtpFee": {
    "tokenAmount": "622595557466841530",
    "feeToken": "0x912ce59144191c1204e64559fe8253a0e49e6548"
  },
  "accountType": "eoa_7702",
  "authorizationRequired": false
}

Output — every status this endpoint answers with

StatusWhen
201Prepared operation
400Invalid request body, the referenced account belongs to a different network than the request targets, or (7702-mode projects only) EIP-7702 is not…
401Missing or invalid credentials
404The referenced accountId does not exist.
409Duplicate user operation (same UserOp hash already materialized)
422Unprocessable — either blocked by an unsafe security verdict (returns the prepared operation; enforcement is monitor-only by default), or the…
429Rate limit exceeded — retry after the number of seconds in the `Retry-After` response header
500Internal server error
503Self-funded native rail only, and always retryable — nothing is known to be wrong: code=SELF_FUNDED_ESTIMATE_UNAVAILABLE (the transfer could not be…
Open in Swagger
POST/transaction/submitSubmit (send signed user operation)

Submit what the client signed for a prepared operation. One endpoint serves every rail, and exactly ONE of signature or signedTransaction is required. Re-sending the same payload is idempotent on both shapes.

  • UserOperation rail: the signature covers the userOperationHash, and operationId is always required with it.
  • Executor rail, including a prepare that answered rail "sponsored": the eth_signTypedData_v4 signature over the returned signingPayload, again with operationId.
  • Self-funded rail: the account signs a whole transaction, so send signedTransaction instead — those bytes are the operation's identity. operationId follows what the prepare answered: present for a swap, whose router call is checked against its quote. It is absent for a native transfer, whose operation is created here and whose id comes back in the response.
  • authorization is EIP-7702 only and is refused with 400 on the rails that delegate nothing.
  • 404 when the operation is not found or expired — prepare it again.
  • 409 when it was already submitted, and for two coded retryable conflicts: AUTHORIZATION_IN_FLIGHT, a concurrent EIP-7702 first-operation authorization for this account, and SENDER_OPERATION_IN_FLIGHT, a previous operation that has not settled yet.
  • 429 when the rate limit is exceeded; check the Retry-After response header.

Authentication: X-API-Key

Input — request body

operationId string required
Operation id from prepare. Required with signature; on the self-funded rail send it for a swap and omit it for a native transfer.
signature string
Required unless signedTransaction is sent. On the UserOperation rail, a raw secp256k1 signature of the 32-byte userOperationHash (no EIP-191 prefix). On the executor rail, and on a prepare that answered rail "sponsored", the eth_signTypedData_v4 signature over signingPayload. 0x-prefixed hex, max 4096 chars.
signedTransaction string
Self-funded rail only, and mutually exclusive with signature: the account's own serialized signed EIP-1559 transaction. 0x-prefixed hex.
authorization object
EIP-7702 authorization tuple, required only for the first operation of an unactivated account and refused on the other rails. All six fields are required when present: chainId (integer >= 1), address (0x-prefixed 20-byte hex), nonce (integer >= 0), yParity (0|1), r and s (0x-prefixed 32-byte hex)

Input — example request

{
  "operationId": "ugtp_operation_E6SnXDoAl9AQ",
  "signature": "0xa3087507768f541a4eaa9d46b581c351d29c3137…65 bytes"
}

Output — example response (200)

{
  "operationId": "ugtp_operation_E6SnXDoAl9AQ",
  "status": "queued",
  "userOperationHash": "0x1a4fece09e742c04398006ebdae6d03227aa7e822ba825e0c64b44fc78d12cb3",
  "networkId": "42161"
}

Output — every status this endpoint answers with

StatusWhen
200Submission result
400Invalid body, or — on the self-funded native rail — bytes this rail cannot carry: code=SIGNED_TRANSACTION_UNSUPPORTED_TYPE (not EIP-1559),…
401Missing or invalid credentials
404Operation not found or expired — prepare it again
409This user operation was already submitted under a different operation id; or the operation was already submitted (status mismatch); or, on the…
429Rate limit exceeded — retry after the number of seconds in the `Retry-After` response header
500Internal server error
502code=SIGNED_TRANSACTION_IDENTITY_MISMATCH — the engine derived a different transaction hash from the same bytes, so nothing was recorded and the…
503Self-funded native rail only, retryable: code=SELF_FUNDED_CHAIN_UNAVAILABLE (the chain could not be checked for whether these bytes already landed —…
Open in Swagger
GET/transaction/:idGet operation status

Live engine status of an operation (re-polls the engine). status is the engine UserOpStatus: unknown | received | queued | submitted | confirmed | finalized | failed | dropped. finalized = execution tx reached the network's reorg-safe confirmation depth; terminal. Operations normally appear from submit onward, and a prepared-but-never-submitted operation normally returns 404. An operation may occasionally be visible with status received before submit; clients must not treat visibility as proof of submission and should continue the documented sign-and-submit flow. Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: X-API-Key

Input — query and path parameters

id string required
Operation id

Output — example response (200)

{
  "operationId": "ugtp_operation_E6SnXDoAl9AQ",
  "status": "finalized",
  "transactionHash": "0x5977159b21734798f90fad1e442216dccec579168c00354268085a6e1d608af0",
  "userOperationHash": "0x1a4fece09e742c04398006ebdae6d03227aa7e822ba825e0c64b44fc78d12cb3",
  "networkId": "42161",
  "replacementCount": 0,
  "reason": null
}

Output — every status this endpoint answers with

StatusWhen
200Operation status
401Missing or invalid credentials
404Resource not found
429Rate limit exceeded — retry after the number of seconds in the `Retry-After` response header
500Internal server error
Open in Swagger
GET/transactionList operations

List the calling project's operations, newest first, with optional account/status filters and paging. status is the last-known mirrored engine status (unknown | received | queued | submitted | confirmed | finalized | failed | dropped); call /transaction/:id for a live read. Operations normally appear after submit. An operation may occasionally be listed with status received before submit; clients must not treat listing presence as proof of submission and should continue the documented sign-and-submit flow. Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: X-API-Key

Input — query and path parameters

accountId string
Filter by account id
status string
Filter by status
limit number
Results per page (default 50)
offset number
Pagination offset (default 0)

Output — example response (200)

{
  "operations": [
    {
      "operationId": "ugtp_operation_E6SnXDoAl9AQ",
      "operationKind": "transfer",
      "status": "finalized",
      "networkId": "42161",
      "transactionHash": "0x5977159b21734798f90fad1e442216dccec579168c00354268085a6e1d608af0",
      "userOperationHash": "0x1a4fece09e742c04398006ebdae6d03227aa7e822ba825e0c64b44fc78d12cb3",
      "createdAt": "2026-09-02T13:56:15.463Z"
    }
  ],
  "count": 1,
  "limit": 50,
  "offset": 0
}

Output — every status this endpoint answers with

StatusWhen
200{ operations, count, limit, offset }
401Missing or invalid credentials
429Rate limit exceeded — retry after the number of seconds in the `Retry-After` response header
500Internal server error
Open in Swagger
POST/transaction/transferPrepare same-chain transfer

One or more same-chain transfer legs bundled into a single gasless operation. The gateway resolves the account from ownerAddress, legs may carry different tokens, and THE RAIL IS DECIDED HERE from the network and the asset — branch on the presence of rail in the response.

  • No rail field: the UserOperation rail. Sign userOperationHash and submit the signature.
  • rail "forward_request": the executor rail. Sign the EIP-712 signingPayload with eth_signTypedData_v4 and submit that.
  • rail "self_funded": the native coin, which no allowance can move. The response carries a whole transaction plus the account's on-chain nonce and a non-binding gasSuggestion, and the signed bytes go to submit as signedTransaction. There is no operationId and no ugtpFee, and a native leg must be the only leg.
  • The prepared operation is held server-side for a limited time (default 30 minutes). If it expires before submit, prepare again.
  • 400 when the account belongs to another network, or when the executor rail is asked to carry the native coin. 409 on a duplicate.
  • Coded 422 when the operation cannot execute: INSUFFICIENT_NATIVE_BALANCE or SELF_FUNDED_NOT_EXECUTABLE on the self-funded rail, otherwise SWAP_AMOUNT_TOO_SMALL or EXECUTION_SIMULATION_REVERTED.
  • Coded 503 on the self-funded rail is always retryable — it names a measurement that could not be taken.
  • 429 when the rate limit is exceeded; check the Retry-After response header.

Authentication: X-API-Key

Input — request body

ownerAddress string required
Owner EOA — the gateway resolves the account from it
networkId string required
Chain id
transfers Transfer[] required
Each: { tokenAddress, amount, recipientAddress }; amount = base-unit integer string; native sentinel 0xeee… for the chain coin
feeTokenAddress string
Fee token (omit for native-gas)

Input — example request

{
  "ownerAddress": "0x9b5fcd6f647a14ebf0f88b1c3e5a3d34b951fc6f",
  "networkId": "42161",
  "transfers": [
    {
      "tokenAddress": "0x912ce59144191c1204e64559fe8253a0e49e6548",
      "amount": "2148465421470024202",
      "recipientAddress": "0xCa9d43184Cc3179609f77D38433D363833C73c60"
    }
  ]
}

Output — example response (201)

{
  "operationId": "ugtp_operation_E6SnXDoAl9AQ",
  "userOperation": {
    "sender": "0x9b5fcd6f647a14ebf0f88b1c3e5a3d34b951fc6f",
    "nonce": "0x2",
    "initCode": "0x",
    "callData": "0xb61d27f6000000000000000000000000912ce59144191c…228 bytes",
    "accountGasLimits": "0x000000000000000000000000000084b800000000000000000000000000014393",
    "preVerificationGas": "0xc208",
    "gasFees": "0x00000000000000000000000000000000000000000000000000000000016eafe0",
    "paymasterAndData": "0x49f26881b5ea4d3f3d451c7faa4bca1b4aebcd97…214 bytes",
    "signature": "0x"
  },
  "userOperationHash": "0x1a4fece09e742c04398006ebdae6d03227aa7e822ba825e0c64b44fc78d12cb3",
  "security": {
    "verdict": "safe",
    "reason": null,
    "provider": "checkpoint"
  },
  "ugtpFee": {
    "tokenAmount": "622595557466841530",
    "feeToken": "0x912ce59144191c1204e64559fe8253a0e49e6548"
  },
  "accountType": "eoa_7702",
  "authorizationRequired": false
}

Output — every status this endpoint answers with

StatusWhen
201Prepared operation
400Invalid request body, the referenced account belongs to a different network than the request targets, or (7702-mode projects only) EIP-7702 is not…
401Missing or invalid credentials
404The referenced accountId does not exist.
409Duplicate user operation (same UserOp hash already materialized)
422Unprocessable — either blocked by an unsafe security verdict (returns the prepared operation; enforcement is monitor-only by default), or the…
429Rate limit exceeded — retry after the number of seconds in the `Retry-After` response header
500An unclassified failure, carrying code=TRANSFER_PREPARE_FAILED.
503Self-funded native rail only, and always retryable — nothing is known to be wrong: code=SELF_FUNDED_ESTIMATE_UNAVAILABLE (the transfer could not be…
Open in Swagger
GET/transaction/historyAccount transaction history

The account's own operations — sends, swaps, and cross-chain operations — newest first, each with its asset movements and the fee it actually collected. Scoped to the one account resolved from networkId plus address, and to the calling project. A pure database read: it does not re-poll the engine, fetch receipts, or query bridges, so an address with no gasless activity simply returns an empty list. Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: X-API-Key

Input — query and path parameters

networkId string required
Chain id
address string required
Account address
limit number
Results per page
offset number
Pagination offset

Output — example response (200)

{
  "operations": [],
  "count": 0,
  "limit": 25,
  "offset": 0
}

Output — every status this endpoint answers with

StatusWhen
200{ operations, count, limit, offset }
400Invalid request body or query parameters
401Missing or invalid credentials
429Rate limit exceeded — retry after the number of seconds in the `Retry-After` response header
500Internal server error
Open in Swagger
POST/transaction/allowance-setupRead what to sign for an allowance

Executor rail only. Answers what THIS token needs before it can move gaslessly: kind "permit" returns EIP-712 typed data when the token implements EIP-2612, and kind "approve" names the token, spender, and amount when it does not. Read-only — it signs nothing and sends nothing. Call it after a prepare is refused with code EXECUTOR_ALLOWANCE_MISSING, then relay the signature through /transaction/setup-permit for a permit or /transaction/setup-approval for an approve. Neither the spender nor the amount is ever accepted from the caller. Returns 404 if privateTokenSymbol names no active private token. Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

  • Granting an allowance creates no operation: it is not returned by GET /transaction, does not appear in GET /transaction/history, and has no operationId to poll. What comes back is the on-chain transaction hash, and the allowance itself is the state to check.
  • This read decides nothing on its own — it reports what the token supports. The spender is never a caller-supplied address: for a private deposit it is resolved from privateTokenSymbol, and otherwise it is the executor.

Authentication: X-API-Key

Input — request body

networkId string required
Chain id
ownerAddress string required
The account whose allowance it is
tokenAddress string required
The ERC-20 to be granted
privateTokenSymbol string
Selects a private token's own contract as the spender, which a private deposit needs; resolved from the symbol, never from a caller-supplied address

Input — example request

{
  "networkId": "43114",
  "ownerAddress": "0x0a1758ad202a2059e8c2970b562597ca45d863c4",
  "tokenAddress": "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7"
}

Output — example response (200)

{
  "kind": "permit",
  "primaryType": "Permit",
  "domain": {},
  "types": {},
  "message": {},
  "deadline": 1780000000
}

Output — every status this endpoint answers with

StatusWhen
200What the account must sign
400Invalid request body or query parameters
401Missing or invalid credentials
429Rate limit exceeded — retry after the number of seconds in the `Retry-After` response header
500Internal server error
Open in Swagger
POST/transaction/setup-permitRelay an EIP-2612 permit

Executor rail only, and one-time per token: relays the account's EIP-2612 permit so an account holding ZERO native coin can grant its allowance and transact gaslessly at all. The amount is never accepted from the caller — it is always the maximum — and neither is a spender address. Returns 409 with code PERMIT_ALREADY_GRANTED when this account already permitted the spender, which makes the call safely idempotent from the caller's side.

Authentication: X-API-Key

Input — request body

networkId string required
Chain id
ownerAddress string required
The account granting the allowance
tokenAddress string required
The ERC-20 being permitted
deadline number required
Unix timestamp from the typed data that was signed
signature string required
The account's signature over the permit typed data
privateTokenSymbol string
Selects a private token's own contract as the spender instead of the executor

Input — example request

{
  "networkId": "43114",
  "ownerAddress": "0x0a1758ad202a2059e8c2970b562597ca45d863c4",
  "tokenAddress": "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
  "deadline": 1780000000,
  "signature": "0x..."
}

Output — example response (200)

{
  "transactionHash": "0x...the approve transaction UGTP broadcast",
  "status": "confirmed",
  "allowance": "115792089237316195423570985008687907853269984665640564039457584007913129639935"
}

Output — every status this endpoint answers with

StatusWhen
200Permit relayed
400Invalid request body or query parameters
401Missing or invalid credentials
409code=PERMIT_ALREADY_GRANTED — this account already permitted the executor.
Open in Swagger
POST/transaction/setup-approvalBroadcast the account's own approve

Executor rail only, for tokens that do NOT implement EIP-2612: the account signs an ordinary approve transaction and UGTP broadcasts it, because this platform has a single broadcaster — an account left holding signed bytes has no other route to the chain. Prefer /transaction/setup-permit wherever the token supports permit.

  • Whether any native coin accompanies it is read from chain state and never configured: an account that can already cover its own gas is sent none, and one that falls short is topped up by the difference only.
  • This is ONE-TIME setup and tightly bounded — a per-account, per-token lifetime allowance plus a per-project hourly limit, both reported as 429 once spent. Build it as a step a user passes through once per token, never as something to repeat.
  • Deliberately minimal: the sender, token, spender, and amount are all read out of the signed bytes, and the sender is recovered from the signature, so a body naming any of them is refused.
  • Coded 400 when the bytes are not an acceptable approve. 403 with APPROVAL_SENDER_BLOCKED when this account previously spent a funded allowance on something else.
  • 409 when chain state advises against it: APPROVAL_ALREADY_GRANTED when the allowance is already in place, APPROVAL_TX_NONCE_STALE or APPROVAL_TX_NONCE_IN_FLIGHT when the signed nonce cannot mine.
  • 502, 503, and 504 each name a step that did not complete and whether a retry is safe.

Authentication: X-API-Key

Input — request body

networkId string required
Chain id
rawTransaction string required
The account's own serialized signed approve transaction, 0x-prefixed hex

Input — example request

{
  "networkId": "43114",
  "rawTransaction": "0x02f8..."
}

Output — example response (200)

{
  "fundingTransactionHash": "0xaaa...",
  "approvalTransactionHash": "0xbbb...",
  "approvalStatus": "confirmed",
  "allowance": "115792089237316195423570985008687907853269984665640564039457584007913129639935"
}

Output — every status this endpoint answers with

StatusWhen
200Gas funded and the approval broadcast
400The transaction is not a fundable approve — codes: APPROVAL_TX_UNPARSABLE, APPROVAL_TX_UNSUPPORTED_TYPE, APPROVAL_TX_WRONG_CHAIN,…
401Missing or invalid credentials
403code=APPROVAL_SENDER_BLOCKED — this account previously spent funded gas on a different transaction and is permanently ineligible for funded approvals.
409Chain state says this approval should not be funded — codes: APPROVAL_TX_NONCE_STALE, APPROVAL_TX_NONCE_IN_FLIGHT (the account already has its own…
429The project's hourly funding limit, the account's LIFETIME allowance of funded approvals, the network's hourly operation ceiling, or the hourly…
502code=APPROVAL_FUNDING_FAILED (nothing was spent, retry), APPROVAL_FUNDED_BUT_NOT_BROADCAST (the gas was sent; the account can broadcast the…
503No funder wallet is available for this network right now, the sponsorship budget cannot be verified (SPONSORSHIP_BUDGET_UNAVAILABLE — fail-closed),…
504code=APPROVAL_FUNDING_NOT_CONFIRMED — the funding transfer has not confirmed yet; a retry resumes it without funding again.
Open in Swagger
POST/transaction/register-broadcastReport a client-broadcast transaction

Plain rail only, where the CLIENT signs and broadcasts: report the hash so the operation can reach a terminal status and book its routing revenue. Without this call a plain operation never settles. The gateway never trusts the hash on its own: it is verified against the exact call the prepare recorded. A hash resolving to a different transaction is refused with 403 and TRANSACTION_MISMATCH. A second, different hash for the same operation is a 409, and a hash the node has not seen yet is a retryable 422 with TRANSACTION_NOT_VISIBLE. Returns 400 if the operation was not prepared on the plain rail, and 404 for an unknown operation, including another project's.

Authentication: X-API-Key

Input — request body

operationId string required
The operation the transaction belongs to
transactionHash string required
32-byte transaction hash the client broadcast

Input — example request

{
  "operationId": "ugtp_operation_d99WHSYNXv4j",
  "transactionHash": "0xd805c96742bc1aed12029fe2e1a78c487b635c0d64f94d8520a6715578f67e17"
}

Output — example response (200)

{
  "operationId": "ugtp_operation_d99WHSYNXv4j",
  "transactionHash": "0xd805c96742bc1aed12029fe2e1a78c487b635c0d64f94d8520a6715578f67e17",
  "status": "submitted"
}

Output — every status this endpoint answers with

StatusWhen
200Registered
400Invalid body, or the operation was not prepared on the plain rail.
401Missing or invalid credentials
403code=TRANSACTION_MISMATCH — the hash resolves to a transaction that is not the prepared call (from / to / data / value differ).
404Operation not found (also returned for another project's operation).
409code=TRANSACTION_HASH_ALREADY_REGISTERED — a different hash is already bound to this operation.
422code=TRANSACTION_NOT_VISIBLE — the node has no record of the hash yet.
429Rate limit exceeded — retry after the number of seconds in the `Retry-After` response header
500Internal server error
Open in Swagger
GET/transaction/listAdmin transaction list

Control plane. Paginated transaction list across every project the session user can access, or one project when projectId is given, with filter-scoped revenue totals. Platform staff see all; a client is scoped to its own project memberships.

  • Each row carries its prepare-time security verdict (safe | unsafe | unavailable) when one was recorded.
  • Each row's fee object carries collectedUsdMicros, the collected amount valued in USD micros at the row's snapshotted price. netProfitUsdMicros is the collected fee minus relayer gas, both at execution-time prices. netProfitFeeRaw is that same margin in the fee token's base units, nullable and possibly negative.
  • Routing-fee valuation precedence: the quote-time value when the quote carried one. Else a confirmation-time snapshot, when the fee was collected in the paymaster fee token or the native coin. Else, for legacy rows only, a 1:1 conversion when the fee rode a trusted stable fee token. Else null.
  • A private-token row also carries productFee — our fee on that operation, always in the public underlying — with usdMicros stored at write time; legacy rows fall back to a 1:1 conversion for a $1-pegged stablecoin, else null.
  • totals aggregate over EVERY operation matching the current filter, not just the returned page: collectedUsdMicros, netProfitUsdMicros, lifiFeeUsdMicros, privateFeeUsdMicros, and earnedUsdMicros as their sum.
  • totals.tokens is a per-network, per-asset breakdown of earned revenue, sorted by earnedUsdMicros descending. The same symbol on two networks stays two separate entries.
  • The totals block is viewer-scoped: platform staff receive the platform figures plus projectEarnedUsdMicros, while a client receives only its own earnings.

Authentication: Bearer JWT (project member)

Input — query and path parameters

projectId string
Optional project id filter
limit number
Results per page (default 25, max 100)
offset number
Pagination offset
status string
Status bucket: pending (unknown/received/queued/submitted), confirmed, finalized, failed, or dropped. A raw engine status is also accepted and matched exactly.
networkId string
Exact chain id, or comma-separated chain ids
kind string
Filter by operation kind: transfer | swap | cross_chain_transfer | cross_chain_swap | call | private_deposit | private_transfer | private_withdraw | private_register
transactionHash string
Partial execution tx hash
userOperationHash string
Partial user operation hash
from ISO datetime
Created-at lower bound
to ISO datetime
Created-at upper bound
includeTestnets string
Boolean-as-string, default false. Testnet networks are excluded from results unless true; an explicit networkId filter takes precedence.

Output — example response (200)

{
  "transactions": [
    {
      "id": "ugtp_operation_E6SnXDoAl9AQ",
      "operationKind": "transfer",
      "status": "finalized",
      "networkId": "42161",
      "transactionHash": "0x5977159b21734798f90fad1e442216dccec579168c00354268085a6e1d608af0",
      "userOperationHash": "0x1a4fece09e742c04398006ebdae6d03227aa7e822ba825e0c64b44fc78d12cb3",
      "fee": {
        "symbol": "ARB",
        "collectedFeeRaw": "564385946233880654",
        "collectedUsdMicros": "62782",
        "netProfitUsdMicros": "50548",
        "netProfitFeeRaw": "454406689249794025",
        "lifiFeeAssetId": null,
        "lifiFeeAssetSymbol": null,
        "lifiFeeAssetDecimals": null,
        "lifiFeeTokenRaw": null,
        "lifiFeeUsdMicros": null
      },
      "productFee": null,
      "security": {
        "verdict": "safe"
      },
      "createdAt": "2026-09-02T13:56:15.463Z"
    },
    {
      "id": "ugtp_operation_wdxo2cOMXBIQ",
      "operationKind": "cross_chain_swap",
      "status": "finalized",
      "networkId": "42161",
      "transactionHash": "0x1125e6fa15ea46293efa3bf06e98958b16b140d4c3f2556f13e4c1b319920a4c",
      "userOperationHash": "0x9f165e8e0f3957722ad6520328ce00333c29f7d26ca50401bf7a5da8540c08c8",
      "fee": {
        "symbol": "USDC",
        "collectedFeeRaw": "72738",
        "collectedUsdMicros": "72726",
        "netProfitUsdMicros": "54191",
        "netProfitFeeRaw": "54200",
        "lifiFeeAssetId": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
        "lifiFeeAssetSymbol": "USDC",
        "lifiFeeAssetDecimals": 6,
        "lifiFeeTokenRaw": "500",
        "lifiFeeUsdMicros": "499"
      },
      "productFee": null,
      "security": {
        "verdict": "safe"
      },
      "createdAt": "2026-09-04T09:34:06.000Z"
    },
    {
      "id": "ugtp_operation_BJL0njW5DcGh",
      "operationKind": "private_withdraw",
      "status": "finalized",
      "networkId": "43114",
      "transactionHash": "0x5d0827600d4bf987ec907143c2479f1d84e49e92d26bd967411a21cb03246506",
      "userOperationHash": "0x141d4abead5569196b9c3165e040dab50a6a91940a1da44b779a76c5cc01307d",
      "fee": {
        "symbol": "USDT",
        "collectedFeeRaw": "54441",
        "collectedUsdMicros": "54430",
        "netProfitUsdMicros": "49987",
        "netProfitFeeRaw": "49997",
        "lifiFeeAssetId": null,
        "lifiFeeAssetSymbol": null,
        "lifiFeeAssetDecimals": null,
        "lifiFeeTokenRaw": null,
        "lifiFeeUsdMicros": null
      },
      "productFee": {
        "kind": "bps",
        "amountRaw": "500",
        "symbol": "USDT",
        "decimals": 6,
        "usdMicros": "500"
      },
      "security": {
        "verdict": "safe"
      },
      "createdAt": "2026-09-04T08:46:59.706Z"
    }
  ],
  "total": 3,
  "limit": 25,
  "offset": 0,
  "totals": {
    "collectedUsdMicros": "110000",
    "netProfitUsdMicros": "81000",
    "lifiFeeUsdMicros": "1000000",
    "privateFeeUsdMicros": "5000",
    "earnedUsdMicros": "1086000",
    "tokens": [
      {
        "networkId": "137",
        "assetId": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
        "symbol": "USDT",
        "decimals": 6,
        "earnedTokenRaw": "1023000",
        "earnedUsdMicros": "1023000"
      },
      {
        "networkId": "42161",
        "assetId": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
        "symbol": "USDC",
        "decimals": 6,
        "earnedTokenRaw": "63000",
        "earnedUsdMicros": "63000"
      }
    ]
  }
}

Output — every status this endpoint answers with

StatusWhen
200Transactions
400Invalid request body or query parameters
401Missing or invalid credentials
Open in Swagger
GET/transaction/seriesTransactions-over-time series (admin chart)

Control plane. Per-bucket NEW operation counts over the requested window, zero-filled and UTC-aligned, split by network. Omitting bucket picks a granularity from the window length. Platform staff see all operations; a client is scoped to its project memberships.

Authentication: Bearer JWT

Input — query and path parameters

period string required
day | week | month | custom
bucket string
day | week | month (default derived from window length)
from string
Window start (required when period=custom)
to string
Window end (defaults to now)
projectId string
Filter to one project
includeTestnets string
Boolean-as-string, default false. Testnet networks are excluded unless true; an explicit networkId filter takes precedence.

Output — example response (200)

{
  "period": "month",
  "from": "2026-06-10T00:00:00.000Z",
  "to": "2026-07-10T00:00:00.000Z",
  "bucket": "day",
  "networks": [
    {
      "networkId": "137",
      "name": "Polygon PoS"
    }
  ],
  "series": [
    {
      "bucket": "2026-06-10T00:00:00.000Z",
      "total": 4,
      "byNetwork": {
        "137": 4
      }
    }
  ],
  "total": 4
}

Output — every status this endpoint answers with

StatusWhen
200{ period, from, to, bucket, networks[{networkId,name}], series[{bucket,total,byNetwork}], total }
400Invalid request body or query parameters
401Missing or invalid credentials
Open in Swagger