Swagger

Universal Gateway Transaction Platform

Ship chain-abstracted transactions without the infrastructure maze.

One integration for smart accounts, sponsored gas, security analysis, and cross-chain routing—built for teams that want a reliable path from intent to finality.

Production API https://api.ugtp.io/v1

Quickstart

Make your first authenticated request

Keep secrets server-side, verify your environment, then build the same prepare–sign–submit loop for every operation.

  1. 01

    Authenticate

    Use a session JWT for onboarding and a project X-API-Key for data-plane requests.

    Read the guide →
  2. 02

    Prepare and sign

    Prepare an unsigned user operation, then sign its userOperationHash client-side.

    Read the guide →
  3. 03

    Submit and track

    Submit the signed operation and poll its operation id until terminal status.

    Read the guide →
curl "https://api.ugtp.io/v1/network/list" \
  -H "X-API-Key: $UGTP_API_KEY"

Environment

You are viewing Production

The portal, examples, and precise Swagger targets follow the selected deployment environment.

Base URLhttps://api.ugtp.io/v1

Production metadata and examples do not expose staging hosts.

Authentication

Two credentials, two trust boundaries

Session JWTs administer projects and keys. Project API keys authenticate application traffic; never place either credential in browser code or public AI prompts.

02

X-API-Key

Send the project key on data-plane calls and rotate it without changing your integration flow.

Manage project keys →

System architecture

A coordinated path from intent to execution

UGTP keeps client signing explicit while coordinating security, sponsorship, routing, and network execution behind one gateway.

01

Client

Builds intent and signs user-operation hashes.

02

UGTP Gateway

Authenticates public requests and orchestrates account, swap, fee, and transaction APIs.

03

Security analysis

Analyzes transaction intent before submission.

04

UGTP Engine

Prepares, submits, and tracks ERC-4337 user operations.

05

UGTP Paymaster

Calculates fees and sponsors gasless operations for supported assets.

06

LI.FI routing

Returns same-chain swap and cross-chain route execution data.

07

Blockchain network

Executes the submitted operation or routed transfer.

client gateway: API requestgateway security: analyzegateway engine: prepare and submitengine paymaster: request sponsorshipgateway routing: discover routeengine network: execute operationrouting network: execute route

Integration profiles

Know what each service owns

Use these profiles to place retries, validation, observability, and user messaging at the correct boundary.

External provider

LI.FI routing

Provide route discovery and execution data for UGTP same-chain swaps and cross-chain swaps.

Lifecycle stage

Swap quote, route preparation, and routed transfer tracking

Trust boundary

Routing data crosses from the UGTP gateway to an external routing provider before network execution.

Inputs → outputs

  • Source and destination chain
  • Input and output token
  • Transfer amount
  • Swap quote and route
  • Execution calldata
  • Routed transfer status

Plan for

  • No serviceable route
  • Route simulation rejects the amount
  • Transfer reaches a failed terminal state

Operational ownership

LI.FI operates the external routing provider; UGTP owns the gateway integration and user-facing API contract.

Approved source

Repository evidence: public/openapi.yaml /crosschain/* Cross-Chain routes; src/content/endpoints.ts Swap group and the deployed swap Swagger tag; LI.FI official documentation.

Official provider documentation ↗

UGTP service

UGTP Engine

Prepare, submit, and track ERC-4337 user operations behind the public Gateway transaction API.

Lifecycle stage

Preparation, submission, and execution tracking

Trust boundary

The gateway passes validated operation intent into UGTP execution services.

Inputs → outputs

  • Smart account
  • Calls
  • Signed user operation
  • Unsigned user operation
  • User operation hash
  • Operation and transaction status

Plan for

  • Unsafe security verdict
  • Deterministic simulation revert
  • Prepared operation expires
  • Duplicate operation

Operational ownership

UGTP operates the Engine boundary exposed through the public transaction and account APIs.

Approved source

Repository evidence: public/openapi.yaml Account and Transactions sections; src/content/endpoints.ts Accounts and Transactions groups.

UGTP service

UGTP Paymaster

Sponsor gasless ERC-4337 operations and calculate fees when the selected asset is supported.

Lifecycle stage

Fee estimation and transaction sponsorship

Trust boundary

The execution flow relies on UGTP sponsorship policy before the operation reaches the network.

Inputs → outputs

  • User operation
  • Network
  • Transferred assets or fee token
  • Sponsored gas
  • Worst-case UGTP fee or fee estimate

Plan for

  • Unsupported fee token
  • Fee estimate unavailable
  • Insufficient amount after fee reserve

Operational ownership

UGTP operates sponsorship policy and the Paymaster boundary exposed by public transaction responses.

Approved source

Repository evidence: relevant Transactions definitions in public/openapi.yaml; Fees, Networks, and Transactions groups in src/content/endpoints.ts.

Core concepts

Build around durable primitives

Accounts define identity, user operations carry signed intent, and operation records expose progress through finality.

01

Smart accounts

Resolve deterministic account addresses, create account records, and inspect deployment state.

02

User operations

Prepare an unsigned operation, sign its hash client-side, and submit it before it expires.

03

Transactions

Prepare, submit, and poll gasless transaction operations through terminal execution status.

04

Status synchronization

UGTP mirrors Engine lifecycle updates asynchronously; public clients poll transaction status because customer-configurable webhook subscriptions are not currently exposed.

05

Networks

Discover serviceable networks and supported fee tokens before preparing an operation.

Operate

Transaction lifecycle

Prepare and sign once, then follow the stable operation identifier to a terminal state.

userOpHash is the stable correlation key for the operation. transactionHash is the current outer transaction hash and can change during replacement or recovery.

If submission times out before a response, it is safe to resend the same signed operation with the same userOpHash. Do not prepare or sign a replacement while that operation remains active; continue polling the original userOpHash.

  1. 01

    Prepare and sign

    Prepare the operation, sign the returned hash, and retain its userOpHash as the stable operation identifier.

  2. 02

    Submit and track

    Submit the signed operation and poll by userOpHash. The transactionHash identifies the current outer handleOps transaction and can change when that transaction is replaced.

  3. 03

    Confirm and finalize

    Treat confirmed as mined and finalized as reorg-safe. Continue tracking the stable userOpHash if a reorg returns the operation to submitted.

Inspect transaction APIs in Swagger ↗

Asynchronous status

Status updates

Use polling as the public integration contract and tolerate retried synchronization.

StatusMeaningClient action
unknownNo operation record was found for this userOpHash.Verify the userOpHash and network before preparing a replacement operation.
receivedThe operation is recorded and waiting to enter the submission queue.Keep polling by userOpHash; do not create a replacement while it remains active.
queuedThe operation is queued for relayer submission.Keep polling by userOpHash; do not create a replacement while it remains active.
submittedThe operation has a current outer transactionHash and is awaiting a mined receipt.Keep polling by userOpHash because the current transactionHash can change during replacement.
confirmedThe operation was mined but has not yet reached the network's reorg-safe depth.Do not retry; continue tracking until finalized if reorg safety matters.
finalizedThe mined operation reached the network's configured reorg-safe confirmation depth.Do not retry; this is the successful reorg-safe state.
failedExecution failed and the operation will not advance.Inspect the reported error, correct the cause, then prepare and sign a new operation.
droppedThe operation was dropped and will not advance.Prepare and sign a new operation if the action is still required.
{
  "userOpHash": "0xabc...",
  "transactionHash": "0xdef...",
  "status": "confirmed",
  "reason": null,
  "feeBreakdown": null
}

Pricing data

Fees

Reserve the estimate before signing, then reconcile the actual fee returned after execution.

Reserve the estimate

Fee estimates are pre-execution planning values. Keep the returned reserve outside the transfer or swap so the submitted operation can pay its gas and applicable UGTP fee.

Read the actual fee after settlement

Account history reports actual collected fee components only after a terminal receipt persists them; the fee can be null before settlement.

UGTP fee components

For paymaster-backed ERC-20 operations, the UGTP fee is a $0.05 static fee plus 0.1% of the transferred input value. Native self-funded operations do not pay a UGTP fee.

Fee token identity

Use the returned fee-token address, symbol, and decimals to identify the asset and interpret all token-denominated amounts.

Signed maximum

When present, the signed maximum is the worst-case amount authorized before execution; it is a ceiling, not the settled charge.

Collected and refunded amounts

After settlement, compare the collected amount with any refunded amount to reconcile what the operation actually paid.

Relayer outer-transaction cost

The relayer outer-transaction cost reports the network cost of the outer transaction separately from token-denominated charges.

Optional Paymaster accounting

Read additional Paymaster accounting fields only when present in the response; availability depends on the operation and persisted receipt data.

Inspect fee estimation in Swagger ↗

Onchain reference

Deployed contracts

Use the Paymaster proxy as the stable integration address on supported mainnets; its backing implementation can be upgraded.

Workflow guide

Prepare, sign, submit, observe

Treat preparation as an expiring snapshot. Sign only its returned hash, submit it, then poll operation status by userOpHash to reach a terminal state.

Prepare
Sign client-side
Submit
Track finality

Complete reference

API reference

All 50 endpoints. Curated endpoints are preserved here with request context and links to the best available Swagger tag or area.

1 endpoint

Health

GET/healthHealth check
Details

Returns service health. No authentication required.

Authentication: None

Response 200

{
  "status": "ok",
  "timestamp": "2026-06-18T12:00:00Z",
  "version": "1.0.0"
}
Open in Swagger

6 endpoints

Auth

POST/auth/registerRegister
Details

Register a new user with name, email, and password (min 8 chars). Emails a verification code (when EMAIL_PROVIDER=console, the gateway logs the code to its console). Returns 400 if the email is from a disposable/throwaway domain ({ "error": "Disposable email addresses are not allowed" }). Returns 429 Too Many Requests if the rate limit is exceeded — this endpoint is strictly limited to prevent email bombing (check the Retry-After response header).

Authentication: None

Request body

name string required
Display name
email string required
User email address
password string required
Password, minimum 8 characters
confirmPassword string required
Must match password

Request example

{
  "name": "Jane Dev",
  "email": "user@example.com",
  "password": "********",
  "confirmPassword": "********"
}

Response 200

{
  "message": "Verification email sent",
  "email": "user@example.com"
}
Open in Swagger
POST/auth/verifyVerify email
Details

Verify the email with the 10-character code sent during registration. Returns a session JWT used for the project & API-key management endpoints. (The API key itself is minted later — see Projects & API Keys.) Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: None

Request body

token string required
The verification code emailed at registration

Request example

{
  "token": "A1B2C3D4E5"
}

Response 200

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "message": "Email verified.",
  "systemRole": "client_admin"
}
Open in Swagger
POST/auth/loginLogin
Details

Authenticate with email and password. Returns a session JWT (for the project & API-key management endpoints) plus the caller's platform systemRole (owner | system_admin | client_admin). Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: None

Request body

email string required
User email
password string required
User password

Request example

{
  "email": "user@example.com",
  "password": "********"
}

Response 200

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "email": "user@example.com",
  "systemRole": "client_admin"
}
Open in Swagger
POST/auth/resendResend code
Details

Resend the email verification code for an unverified account. Requests are rate-limited to one per minute. Returns 429 Too Many Requests if the rate limit is exceeded — this endpoint is strictly limited to prevent email bombing (check the Retry-After response header).

Authentication: None

Request body

email string required
User email

Request example

{
  "email": "user@example.com"
}

Response 200

{
  "message": "New verification code sent",
  "email": "user@example.com"
}
Open in Swagger
POST/auth/change-passwordChange password
Details

Change the signed-in user's password. Verifies currentPassword; newPassword must be at least 8 characters and match confirmPassword.

Authentication: Bearer JWT (client session)

Request body

currentPassword string required
Current password
newPassword string required
New password, minimum 8 characters
confirmPassword string required
Must match newPassword

Request example

{
  "currentPassword": "********",
  "newPassword": "********",
  "confirmPassword": "********"
}

Response 200

{
  "message": "Password updated."
}
Open in Swagger
GET/auth/current-userCurrent user
Details

Returns the signed-in user with their live platform systemRole (owner | system_admin | client_admin), read from the DB — so a role change is reflected on the next load without re-login.

Authentication: Bearer JWT (client session)

Response 200

{
  "id": "ugtp_user_a1b2c3d4e5f6",
  "email": "user@example.com",
  "name": "Jane Dev",
  "systemRole": "client_admin"
}
Open in Swagger

18 endpoints

Projects & API Keys

POST/project/createCreate project
Details

Create a project. The authenticated user becomes its owner (only owner/admin members can mint API keys). Optionally seeds additional members by email and always auto-issues a full-access API key named after the project (raw key in apiKey.value — also persisted, so it stays retrievable later by owners/admins via GET /project/:projectId/api-key). Unknown/duplicate member emails never fail the request — they are reported in warnings instead.

Authentication: Bearer JWT (client session)

Request body

name string required
Project name
members array
Optional seed members (max 20). Each item: { email: string, role?: "admin"|"member"|"viewer" (default "member") }

Request example

{
  "name": "My App",
  "members": [
    {
      "email": "teammate@example.com",
      "role": "admin"
    }
  ]
}

Response 201

{
  "id": "ugtp_proj_a1b2c3d4e5f6",
  "name": "My App",
  "apiKey": {
    "id": "ugtp_project_api_key_a1b2c3d4e5f6",
    "name": "My App",
    "keyPrefix": "ugtp2_0123",
    "value": "ugtp2_0123456789abcdef0123456789abcdef"
  },
  "warnings": []
}
Open in Swagger
DELETE/project/:projectIdDelete project (soft delete)
Details

Owner-only soft delete: archives the project (marks it inactive) and revokes all its still-active API keys. Nothing is removed from the database — operations, fees, status events, security reports, and memberships are retained for history/analytics. Idempotent — repeat calls still return 204. Returns 404 both if the project doesn't exist and if the caller is not its owner (non-owners get the same not-found response, never a 403).

Authentication: Bearer JWT (project owner)

Parameters

projectId string required
Project id
Open in Swagger
GET/project/listList my projects
Details

Projects the caller is a member of. Archived (soft-deleted / inactive) projects are excluded. Each item includes membersCount (all members), apiKeysCount (active keys only), lifiFeePercent (the project's LI.FI integrator fee, as a percent — e.g. 0.2 = 0.2%), and lifiIntegrator (the LI.FI Partner Portal integration id the fees are attributed to).

Authentication: Bearer JWT (client session)

Response 200

{
  "projects": [
    {
      "id": "ugtp_proj_a1b2c3d4e5f6",
      "name": "My App",
      "isActive": true,
      "role": "owner",
      "createdAt": "2026-06-18T12:00:00.000Z",
      "membersCount": 3,
      "apiKeysCount": 2,
      "lifiFeePercent": 0.2,
      "lifiIntegrator": "extrasafe"
    }
  ]
}
Open in Swagger
PATCH/project/:projectId/feesSet LI.FI integrator fee
Details

Set the project's LI.FI integrator fee. lifiFeePercent is a human-facing percent (e.g. 0.25 = 0.25%), 0–2, at most 2 decimal places (it must map exactly onto integer basis points). Requires an owner/admin membership on the project, or platform staff.

Authentication: Bearer JWT (project owner/admin or platform staff)

Parameters

projectId string required
Project id

Request body

lifiFeePercent number required
Fee percent, 0–2, at most 2 decimal places

Request example

{
  "lifiFeePercent": 0.25
}

Response 200

{
  "projectId": "ugtp_proj_a1b2c3d4e5f6",
  "lifiFeePercent": 0.25
}
Open in Swagger
GET/project/allList all projects (staff)
Details

Platform-staff only (owner / system_admin): every project, sorted by transaction count (desc), capped at 50, with an optional case-insensitive name search. Backs the owner/admin project picker; clients use GET /project/list (scoped to their memberships).

Authentication: Bearer JWT (platform owner/system admin)

Parameters

search string
Case-insensitive project-name filter
limit number
Max results (default 50, max 50)

Response 200

{
  "projects": [
    {
      "id": "ugtp_proj_a1b2c3d4e5f6",
      "name": "Acme",
      "isActive": true,
      "transactionCount": 4210,
      "createdAt": "2026-06-18T12:00:00.000Z"
    }
  ]
}
Open in Swagger
GET/project/:projectId/api-keyList API keys
Details

List a project's API keys. Requires membership on the project. Each key includes a value field (the raw key string, or null if it was issued before plaintext storage was added) — but only when the caller is a project owner or admin; viewers get every other field with value omitted entirely.

Authentication: Bearer JWT (project member)

Parameters

projectId string required
Project id

Response 200

{
  "apiKeys": [
    {
      "id": "ugtp_key_a1b2c3d4e5f6",
      "name": "Server key",
      "keyPrefix": "ugtp2_0123",
      "scopes": [
        "full_access"
      ],
      "isActive": true,
      "expiresAt": null,
      "lastUsedAt": "2026-06-18T12:00:00.000Z",
      "createdAt": "2026-06-18T12:00:00.000Z",
      "revokedAt": null,
      "value": "ugtp2_0123456789abcdef0123456789abcdef"
    }
  ]
}
Open in Swagger
POST/project/:projectId/api-key/createCreate API key
Details

Mint a project API key. The raw key is returned in this response and also persisted (alongside its SHA-256 hash, used for auth) so it stays retrievable later by project owners/admins via GET /project/:projectId/api-key. Requires an owner/admin membership on the project. A project can hold at most ONE active key: returns 409 Conflict while an active (non-revoked, unexpired) key exists — revoke it first to mint a replacement.

Authentication: Bearer JWT (project owner/admin)

Parameters

projectId string required
Project id

Request body

name string required
Key label
scopes string[]
Key scopes (default ["full_access"]; full_access is the only scope today)

Request example

{
  "name": "Server key",
  "scopes": [
    "full_access"
  ]
}

Response 201

{
  "id": "ugtp_key_a1b2c3d4e5f6",
  "apiKey": "ugtp2_0123456789abcdef0123456789abcdef",
  "keyPrefix": "ugtp2_0123"
}
Open in Swagger
DELETE/project/:projectId/api-key/:keyIdRevoke API key
Details

Revoke a project API key. Requires an owner/admin membership on the project.

Authentication: Bearer JWT (project owner/admin)

Parameters

projectId string required
Project id
keyId string required
API key id
Open in Swagger
GET/project/:projectId/members/listList members
Details

List project members. Readable by any project member or platform staff.

Authentication: Bearer JWT (project member or platform staff)

Parameters

projectId string required
Project id

Response 200

{
  "members": [
    {
      "id": "ugtp_member_a1b2c3d4e5f6",
      "userId": "ugtp_user_a1b2c3d4e5f6",
      "email": "user@example.com",
      "name": "Jane Dev",
      "role": "owner",
      "createdAt": "2026-06-18T12:00:00.000Z"
    }
  ]
}
Open in Swagger
POST/project/:projectId/members/addAdd or invite member
Details

Add a project member by email. If the email belongs to an existing UGTP user they're added directly (201, { status: "added", member }). If the email is new, a pending invitation is created and emailed to the recipient (202, { status: "invited", email }) — see /project/invitations/accept. Requires an owner/admin membership on the project. Returns 404 if the project doesn't exist, 400 if the email is already a member.

Authentication: Bearer JWT (project owner/admin)

Parameters

projectId string required
Project id

Request body

email string required
User or invitee email
role string
admin | member | viewer (default member)

Request example

{
  "email": "teammate@example.com",
  "role": "viewer"
}

Response 201

{
  "added": {
    "status": "added",
    "member": {
      "userId": "ugtp_user_a1b2c3d4e5f6",
      "email": "teammate@example.com",
      "name": "Teammate",
      "role": "viewer"
    }
  },
  "invited": {
    "status": "invited",
    "email": "newperson@example.com"
  }
}
Open in Swagger
GET/project/:projectId/invitationsList pending invitations
Details

List a project's pending invitations. Requires an owner/admin membership on the project.

Authentication: Bearer JWT (project owner/admin)

Parameters

projectId string required
Project id

Response 200

{
  "invitations": [
    {
      "id": "ugtp_invite_a1b2c3d4e5f6",
      "email": "teammate@example.com",
      "role": "viewer",
      "status": "pending",
      "expiresAt": "2026-07-20T12:00:00.000Z",
      "createdAt": "2026-07-13T12:00:00.000Z"
    }
  ]
}
Open in Swagger
POST/project/:projectId/invitations/:invitationId/resendResend invitation
Details

Resend the invitation email. The invitation must still be pending and belong to this project (400 otherwise). Requires an owner/admin membership on the project.

Authentication: Bearer JWT (project owner/admin)

Parameters

projectId string required
Project id
invitationId string required
Invitation id
Open in Swagger
POST/project/:projectId/invitations/:invitationId/cancelCancel invitation
Details

Cancel a pending invitation. The invitation must still be pending and belong to this project (400 otherwise). Requires an owner/admin membership on the project.

Authentication: Bearer JWT (project owner/admin)

Parameters

projectId string required
Project id
invitationId string required
Invitation id
Open in Swagger
DELETE/project/:projectId/members/:memberIdRemove member
Details

Remove a non-owner project member. Requires an owner/admin membership on the project.

Authentication: Bearer JWT (project owner/admin)

Parameters

projectId string required
Project id
memberId string required
Project member id
Open in Swagger
GET/project/invitations/acceptPreview invitation
Details

Public, session-less preview of a project invitation for an accept-invitation landing page — shows who invited the recipient and to which project before they set a password. Read-only. Returns 404 if the token is unknown, 410 if it expired, 400 if token is missing.

Authentication: None

Parameters

token string required
Invitation token from the emailed link

Response 200

{
  "projectName": "Acme",
  "email": "teammate@example.com",
  "role": "viewer",
  "expiresAt": "2026-07-20T12:00:00.000Z"
}
Open in Swagger
POST/project/invitations/acceptAccept invitation
Details

Public, session-less. Consumes the invite token and either creates the account + project membership for a new email (auto-logs in, returning a real sessionToken) or attaches an already-existing user to the project (requiresLogin: true, sessionToken: null — the client must log in normally; auto-login off just the invite token would let anyone with the link take over an existing account). Returns 404 if the token is unknown, 410 if it expired. Rate-limited to 5 requests/hour per IP+email — check the Retry-After response header on 429.

Authentication: None

Request body

token string required
Invitation token from the emailed link
password string required
Password, minimum 8 characters
name string required
Display name for a new account

Request example

{
  "token": "a1b2c3d4e5f6...",
  "password": "********",
  "name": "Jane Dev"
}

Response 201

{
  "newUser": {
    "requiresLogin": false,
    "sessionToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "projectId": "ugtp_proj_a1b2c3d4e5f6",
    "email": "teammate@example.com"
  },
  "existingUser": {
    "requiresLogin": true,
    "sessionToken": null,
    "projectId": "ugtp_proj_a1b2c3d4e5f6",
    "email": "teammate@example.com"
  }
}
Open in Swagger
GET/api-key/allList manageable API keys
Details

API keys the caller can manage with their owning project, newest first, capped at 100: every key for platform staff (owner / system_admin), or just the caller's owner/admin projects' keys for a project admin. Backs the cross-project key console.

Authentication: Bearer JWT (platform staff or project owner/admin)

Response 200

{
  "apiKeys": [
    {
      "id": "ugtp_key_a1b2c3d4e5f6",
      "name": "Server key",
      "keyPrefix": "ugtp2_0123",
      "scopes": [
        "full_access"
      ],
      "isActive": true,
      "projectId": "ugtp_proj_a1b2c3d4e5f6",
      "projectName": "Acme",
      "lastUsedAt": "2026-06-18T12:00:00.000Z",
      "createdAt": "2026-06-18T12:00:00.000Z",
      "revokedAt": null,
      "revokedBy": null
    },
    {
      "id": "ugtp_key_f6e5d4c3b2a1",
      "name": "Old key",
      "keyPrefix": "ugtp2_9876",
      "scopes": [
        "full_access"
      ],
      "isActive": false,
      "projectId": "ugtp_proj_a1b2c3d4e5f6",
      "projectName": "Acme",
      "lastUsedAt": null,
      "createdAt": "2026-06-10T12:00:00.000Z",
      "revokedAt": "2026-06-17T09:30:00.000Z",
      "revokedBy": {
        "name": "Ada Admin",
        "email": "ada@acme.com"
      }
    }
  ]
}
Open in Swagger
POST/api-key/:keyId/revokeRevoke an API key
Details

Revoke an API key by id. Platform staff may revoke any key; a project owner/admin may revoke only keys in a project they manage. Backs the cross-project key console.

Authentication: Bearer JWT (platform staff or project owner/admin)

Parameters

keyId string required
API key id
Open in Swagger

3 endpoints

Dashboard

GET/dashboard/statsDashboard stats
Details

Dashboard metrics for a day/week/month/custom period across all projects the session user can access, or one project when projectId is provided: operation counts, platform fees grouped by network → fee token (token totals plus per-token USD from the Chainlink price snapshotted on each fee, in micros where 1e6 = $1), and a separate lifi section with the LI.FI integrator fee earned on swap / cross_chain_swap operations (totals, grouped by operation kind, and grouped by network → fee token). Fee USD fields are null when no Chainlink price was snapshotted for that token/operation.

Authentication: Bearer JWT (project member)

Parameters

projectId string
Optional project id filter
period string
day | week | month | custom (default day)
from ISO datetime
Required when period=custom
to ISO datetime
Range end; defaults to now
includeTestnets string
Boolean-as-string, default false. Testnet networks are excluded from results unless true; an explicit networkId filter takes precedence.

Response 200

{
  "period": "week",
  "operations": {
    "total": 42,
    "byStatus": {
      "confirmed": 40,
      "failed": 2
    },
    "byNetwork": {
      "42161": 42
    }
  },
  "fees": {
    "totalCollectedUsdMicros": "1250000",
    "totalNetUsdMicros": "900000",
    "byNetwork": [
      {
        "networkId": "42161",
        "operations": 42,
        "collectedUsdMicros": "1250000",
        "netUsdMicros": "900000",
        "tokens": [
          {
            "feeAssetId": "0xB97E...",
            "symbol": "USDC",
            "decimals": 6,
            "gasFeeRaw": "850000",
            "staticFeeRaw": "50000",
            "transferFeeRaw": "400000",
            "collectedFeeRaw": "1250000",
            "netRevenueRaw": "1250000",
            "operations": 40,
            "collectedUsdMicros": "1250000",
            "netUsdMicros": "900000"
          }
        ]
      }
    ]
  },
  "lifi": {
    "totalFeeUsdMicros": "300000",
    "byKind": {
      "swap": {
        "operations": 5,
        "feeUsdMicros": "200000"
      },
      "cross_chain_swap": {
        "operations": 2,
        "feeUsdMicros": "100000"
      }
    },
    "byNetwork": [
      {
        "networkId": "42161",
        "tokens": [
          {
            "assetId": "0xB97E...",
            "symbol": "USDC",
            "decimals": 6,
            "feeTokenRaw": "300000",
            "feeUsdMicros": "300000",
            "operations": 7
          }
        ]
      }
    ]
  }
}
Open in Swagger
GET/dashboard/account-seriesAccount statistics (Users) time series
Details

New accounts bucketed over a time window (1 account = 1 point on its creation date), split by network, for the Users statistics page. Returns zero-filled continuous buckets plus the pre-window baseline per network, so a cumulative total-growth curve (or per-bucket new accounts) can be rendered client-side. period picks the window; bucket is the independent granularity (derived from period when omitted). Filters narrow which accounts are counted: networkId, address, activeWithin (accounts with an operation in the last window), and projectId (accounts that transacted under that project). Platform staff see all accounts; clients are scoped to accounts that transacted under a project they belong to.

Authentication: Bearer JWT (project member)

Parameters

period string
day | week | month | sixMonths | year | custom (default month)
bucket string
day | week | month bucket granularity; derived from period when omitted
from ISO datetime
Required when period=custom
to ISO datetime
Range end; defaults to now
networkId string
Chain id, or comma-separated chain ids
address string
Partial account address
activeWithin string
day | week | month | sixMonths | year — only accounts active in the last window
projectId string
Only accounts that transacted under this project

Response 200

{
  "period": "month",
  "from": "2026-06-07T00:00:00.000Z",
  "to": "2026-07-07T00:00:00.000Z",
  "bucket": "day",
  "networks": [
    {
      "networkId": "137",
      "name": "Polygon PoS"
    },
    {
      "networkId": "42161",
      "name": "Arbitrum One"
    }
  ],
  "baseline": {
    "total": 84,
    "byNetwork": {
      "137": 60,
      "42161": 24
    }
  },
  "series": [
    {
      "bucket": "2026-06-07T00:00:00.000Z",
      "total": 4,
      "byNetwork": {
        "137": 3,
        "42161": 1
      }
    },
    {
      "bucket": "2026-06-08T00:00:00.000Z",
      "total": 0,
      "byNetwork": {
        "137": 0,
        "42161": 0
      }
    }
  ],
  "total": 44
}
Open in Swagger
GET/dashboard/transactionsDashboard transactions
Details

Paginated transaction list across all projects the session user can access, or one project when projectId is provided, with status, time, network, transaction-hash, and user-operation-hash filters. Each row includes its prepare-time security verdict (safe | unsafe | unavailable) when one was recorded. Each row's fee object also carries collectedUsdMicros (gross fee — the collected amount valued in USD micros at the row's snapshotted Chainlink price), netProfitUsdMicros (paymaster net margin: collected fee minus relayer gas, both at execution-time prices, USD micros), netProfitFeeRaw (the same net margin expressed in the fee token's base units — the token-denominated profit, not the gross; nullable, may be negative) and the LI.FI integrator fee fields (lifiFeeAssetId/Symbol/Decimals, lifiFeeTokenRaw, lifiFeeUsdMicros) — all nullable, populated only when a price/receipt was available (LI.FI fields additionally only for swap / cross_chain_swap operations with a terminal receipt).

Authentication: Bearer JWT (project member)

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
transactionHash string
Partial execution tx hash
userOperationHash string
Partial ERC-4337 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.

Response 200

{
  "transactions": [
    {
      "id": "ugtp_op_a1b2c3d4e5f6",
      "operationKind": "transfer",
      "status": "confirmed",
      "networkId": "42161",
      "transactionHash": "0xdef...",
      "userOperationHash": "0xabc...",
      "fee": {
        "symbol": "USDC",
        "collectedFeeRaw": "25000",
        "collectedUsdMicros": "25000",
        "netProfitUsdMicros": "18000",
        "netProfitFeeRaw": "18000",
        "lifiFeeAssetId": null,
        "lifiFeeAssetSymbol": null,
        "lifiFeeAssetDecimals": null,
        "lifiFeeTokenRaw": null,
        "lifiFeeUsdMicros": null
      },
      "security": {
        "verdict": "safe"
      },
      "createdAt": "2026-06-18T12:00:00.000Z"
    },
    {
      "id": "ugtp_op_b2c3d4e5f6a1",
      "operationKind": "swap",
      "status": "confirmed",
      "networkId": "137",
      "transactionHash": "0xfed...",
      "userOperationHash": "0xcba...",
      "fee": {
        "symbol": "USDC",
        "collectedFeeRaw": "60000",
        "collectedUsdMicros": "60000",
        "netProfitUsdMicros": "45000",
        "netProfitFeeRaw": "45000",
        "lifiFeeAssetId": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
        "lifiFeeAssetSymbol": "USDT",
        "lifiFeeAssetDecimals": 6,
        "lifiFeeTokenRaw": "1000000",
        "lifiFeeUsdMicros": "1000000"
      },
      "security": {
        "verdict": "safe"
      },
      "createdAt": "2026-06-18T12:05:00.000Z"
    }
  ],
  "total": 2,
  "limit": 25,
  "offset": 0
}
Open in Swagger

6 endpoints

Accounts

POST/account/createResolve account (derive + cache)
Details

Derive the deterministic smart-account address for an owner on a network (via the engine) and cache it. Idempotent — re-derivation refreshes the cached details and keeps a stable id. Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: X-API-Key

Request body

ownerAddress string required
Owner EOA address
networkId string required
Chain id (e.g. 43114)

Request example

{
  "ownerAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
  "networkId": "43114"
}

Response 201

{
  "id": "ugtp_acc_a1b2c3d4e5f6",
  "address": "0x1234567890abcdef1234567890abcdef12345678",
  "networkId": "43114",
  "networkKind": "evm",
  "details": {
    "schemaVersion": 1,
    "networkKind": "evm",
    "evm": {
      "ownerAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
      "implementationVersion": "v3.3"
    }
  },
  "createdAt": "2026-06-18T12:00:00.000Z",
  "updatedAt": "2026-06-18T12:00:00.000Z"
}
Open in Swagger
POST/account/resolve-singleSingle network-independent resolve
Details

Map one owner EOA to its deterministic smart-account address. The address is network-independent (identical on every chain) — no networkId needed. Stateless (creates no account row). For many owners prefer /account/resolve-batch. Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: X-API-Key

Request body

ownerAddress string required
Owner EOA address

Request example

{
  "ownerAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0"
}

Response 200

{
  "address": "0x1234567890abcdef1234567890abcdef12345678"
}
Open in Swagger
POST/account/infoDerive address (preview)
Details

Stateless preview of the deterministic smart-account address (+ implementation version + on-chain deployment flag) for an owner — without persisting an account. Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: X-API-Key

Request body

ownerAddress string required
Owner EOA address
networkId string required
Chain id

Request example

{
  "ownerAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
  "networkId": "43114"
}

Response 200

{
  "ownerAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
  "address": "0x1234567890abcdef1234567890abcdef12345678",
  "networkId": "43114",
  "isDeployed": false,
  "kernelVersion": "v3.3"
}
Open in Swagger
GET/account/is-deployedCheck deployment
Details

Whether a smart-account address is already deployed on-chain for the given network. Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: X-API-Key

Parameters

address string required
Smart-account address
networkId string required
Chain id

Response 200

{
  "isDeployed": true
}
Open in Swagger
GET/account/seriesAccount-creation series (admin chart)
Details

Account-creation time series for the admin dashboard's Accounts chart: zero-filled per-bucket NEW account counts (UTC-aligned buckets) plus a pre-window baseline per network so the client can derive a cumulative curve (cumulative = baseline + running sum). networks is ordered by in-window count, descending. Omitting bucket picks a granularity from the window length (≤31d day, ≤~6mo week, else month). Platform staff see all accounts; clients are scoped to accounts attributed (via operations) to their projects.

Authentication: Bearer JWT

Parameters

period string required
day | week | month | sixMonths | year | custom
bucket string
day | week | month (default derived from window length)
from string
Window start ISO date (required when period=custom)
to string
Window end ISO date (default now)
networkId string
Filter to one chain id
address string
Exact account address (case-insensitive)
activeWithin string
day | week | month | sixMonths | year — only accounts with an operation in this trailing window
projectId string
Filter to accounts attributed to one project
includeTestnets string
Boolean-as-string, default false. Testnet networks are excluded from results unless true; an explicit networkId filter takes precedence.

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"
    },
    {
      "networkId": "42161",
      "name": "Arbitrum One"
    }
  ],
  "baseline": {
    "total": 7,
    "byNetwork": {
      "137": 5,
      "42161": 2
    }
  },
  "series": [
    {
      "bucket": "2026-06-10T00:00:00.000Z",
      "total": 0,
      "byNetwork": {}
    },
    {
      "bucket": "2026-06-11T00:00:00.000Z",
      "total": 3,
      "byNetwork": {
        "137": 2,
        "42161": 1
      }
    }
  ],
  "total": 3
}
Open in Swagger
POST/account/resolve-batchBatch-resolve owners
Details

Map many owner EOAs to their smart-account addresses in one call. The address is network-independent, so only owner addresses are needed. Stateless (creates no accounts). Default cap: 1,000 owners per request (env-configurable via RESOLVE_BATCH_MAX_ADDRESSES) — paginate for larger sets. Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: X-API-Key

Request body

ownerAddresses string[] required
Owner EOA addresses (1–1000 default, env-configurable)

Request example

{
  "ownerAddresses": [
    "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0"
  ]
}

Response 200

{
  "results": [
    {
      "ownerAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
      "smartAccountAddress": "0x1234567890abcdef1234567890abcdef12345678"
    }
  ],
  "count": 1
}
Open in Swagger

5 endpoints

Transactions

POST/transaction/preparePrepare (generate user operation)
Details

Build an unsigned ERC-4337 user operation for the given calls. Runs the security check (an unsafe verdict returns 422). transferredAssets are the asset(s) the gasless fee is charged against. The response includes ugtpFee — the worst-case fee the paymaster will charge ({ tokenAmount, feeToken }, base units). Sign the returned userOperationHash client-side, then call /transaction/submit. The prepared operation is held server-side for a limited time (default 30 minutes). If it expires before submit, prepare again. Returns 409 if a duplicate user operation (same UserOp hash) was already materialized. If the simulated calldata reverts deterministically (e.g. the route's bridge/DEX rejects the amount as below its minimum), returns 422 with a coded error ({ error, code, reason }) — e.g. code SWAP_AMOUNT_TOO_SMALL, or EXECUTION_SIMULATION_REVERTED with the raw revert reason. Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: X-API-Key

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, non-sponsored)

Request example

{
  "accountId": "ugtp_acc_a1b2c3d4e5f6",
  "networkId": "43114",
  "calls": [
    {
      "to": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
      "value": "0x2386f26fc10000",
      "data": "0x"
    }
  ],
  "transferredAssets": [
    {
      "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
      "amount": "0x2386f26fc10000"
    }
  ]
}

Response 201

{
  "operationId": "ugtp_op_a1b2c3d4e5f6",
  "userOperation": {
    "sender": "0x1234567890abcdef1234567890abcdef12345678",
    "nonce": "0x0",
    "initCode": "0x",
    "callData": "0x...",
    "accountGasLimits": "0x...",
    "preVerificationGas": "0x...",
    "gasFees": "0x...",
    "paymasterAndData": "0x...",
    "signature": "0x"
  },
  "userOperationHash": "0xabc...",
  "security": {
    "verdict": "safe",
    "reason": null,
    "provider": "mock"
  },
  "ugtpFee": {
    "tokenAmount": "52000",
    "feeToken": "0xc2132d05d31c914a87c6611c10748aeb04b58e8f"
  }
}
Open in Swagger
POST/transaction/submitSubmit (send signed user operation)
Details

Submit the client-signed user operation to the engine relayer. Returns the engine UserOpStatus. Returns 404 if the operation is not found or has expired — prepare it again. Returns 409 if this user operation was already submitted under a different operation id, or the operation was already submitted (status mismatch). Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: X-API-Key

Request body

operationId string required
Operation id from /prepare
signature string required
EIP-191 signature of userOperationHash (0x-prefixed hex, max 4096 chars)

Request example

{
  "operationId": "ugtp_op_a1b2c3d4e5f6",
  "signature": "0x..."
}

Response 200

{
  "operationId": "ugtp_op_a1b2c3d4e5f6",
  "status": "queued",
  "userOperationHash": "0xabc...",
  "networkId": "43114"
}
Open in Swagger
GET/transaction/:idGet operation status
Details

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

Parameters

id string required
Operation id

Response 200

{
  "operationId": "ugtp_op_a1b2c3d4e5f6",
  "status": "confirmed",
  "transactionHash": "0xdef...",
  "userOperationHash": "0xabc...",
  "networkId": "43114",
  "replacementCount": 1,
  "reason": null
}
Open in Swagger
GET/transactionList operations
Details

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

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)

Response 200

{
  "operations": [
    {
      "operationId": "ugtp_op_a1b2c3d4e5f6",
      "operationKind": "transfer",
      "status": "confirmed",
      "networkId": "43114",
      "transactionHash": "0xdef...",
      "userOperationHash": "0xabc...",
      "createdAt": "2026-06-18T12:00:00.000Z"
    }
  ],
  "count": 1,
  "limit": 50,
  "offset": 0
}
Open in Swagger
POST/transaction/transferPrepare same-chain transfer
Details

Convenience flow for same-chain transfers: encodes one or more transfer legs into a single user operation (multi-token supported). Like /prepare it returns an unsigned user op to sign + submit (plus ugtpFee — the worst-case paymaster fee in the fee token); an unsafe security verdict returns 422. Cross-chain is not yet available. The prepared operation is held server-side for a limited time (default 30 minutes). If it expires before submit, prepare again. Returns 409 if a duplicate user operation (same UserOp hash) was already materialized. If the simulated calldata reverts deterministically (e.g. the route's bridge/DEX rejects the amount as below its minimum), returns 422 with a coded error ({ error, code, reason }) — e.g. code SWAP_AMOUNT_TOO_SMALL, or EXECUTION_SIMULATION_REVERTED with the raw revert reason. Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: X-API-Key

Request body

ownerAddress string required
Owner EOA — the gateway resolves the smart 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, non-sponsored)

Request example

{
  "ownerAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
  "networkId": "43114",
  "transfers": [
    {
      "tokenAddress": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
      "amount": "10000000000000000",
      "recipientAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0"
    }
  ]
}

Response 201

{
  "operationId": "ugtp_op_a1b2c3d4e5f6",
  "userOperation": {
    "sender": "0x1234567890abcdef1234567890abcdef12345678",
    "nonce": "0x0",
    "initCode": "0x",
    "callData": "0x...",
    "accountGasLimits": "0x...",
    "preVerificationGas": "0x...",
    "gasFees": "0x...",
    "paymasterAndData": "0x...",
    "signature": "0x"
  },
  "userOperationHash": "0xabc...",
  "security": {
    "verdict": "safe",
    "reason": null,
    "provider": "mock"
  },
  "ugtpFee": {
    "tokenAmount": "52000",
    "feeToken": "0xc2132d05d31c914a87c6611c10748aeb04b58e8f"
  }
}
Open in Swagger

4 endpoints

Swap

GET/swap/chainsList swap-supported chains
Details

LI.FI-supported chains filtered to the gateway's serviceable networks. Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: X-API-Key

Response 200

{
  "chains": [
    {
      "networkId": "42161",
      "name": "Arbitrum One",
      "nativeCurrency": "ETH",
      "logoUrl": "https://..."
    }
  ]
}
Open in Swagger
GET/swap/tokens?networkId=List swap tokens for a chain
Details

LI.FI tokens available on the given network. Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: X-API-Key

Parameters

networkId string required
Chain id, e.g. 42161

Response 200

{
  "networkId": "42161",
  "tokens": [
    {
      "address": "0x...",
      "symbol": "USDC",
      "name": "USD Coin",
      "decimals": 6,
      "networkId": "42161"
    }
  ]
}
Open in Swagger
POST/swap/quotePreview a same- or cross-chain swap
Details

Recommended LI.FI route output + fees for fromToken -> toToken. Set toNetworkId to a different chain for a cross-chain swap. Preview only; no operation is created. feeReserveTokenAmount is the engine's own fee estimate for this exact swap — keep this much of the input token OUTSIDE the swap amount or the op is rejected (ERC-20 input: the paymaster fee; native input: the self-funded native gas; null when the engine estimate is unavailable). ugtpFeeUsd is null for native input (no paymaster involved, no UGTP fee). If the quote-time simulation of the real approve+swap calldata reverts deterministically (e.g. the route's bridge rejects the amount as below its minimum), returns 422 with a coded error ({ error, code, reason }, e.g. code SWAP_AMOUNT_TOO_SMALL) so clients can warn before the user signs. Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: X-API-Key

Request body

ownerAddress string required
Owner EOA — the gateway resolves the smart account from it
networkId string required
Source chain id (where the gasless op runs)
toNetworkId string
Destination chain id (defaults to networkId; a different value makes it cross-chain)
fromToken string required
Input token address (on networkId)
toToken string required
Output token address (on toNetworkId)
amount string required
Input amount, base units
slippage number
Slippage tolerance as a fraction (0.005 = 0.5%); LI.FI default when omitted

Request example

{
  "ownerAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
  "networkId": "137",
  "toNetworkId": "42161",
  "fromToken": "0x...",
  "toToken": "0x...",
  "amount": "1000000",
  "slippage": 0.005
}

Response 200

{
  "fromAmount": "1000000",
  "toAmount": "990000",
  "fromAmountUsd": "1.00",
  "toAmountUsd": "0.99",
  "rate": "0.99",
  "minimumReceived": "0.985050",
  "minimumReceivedUsd": "0.985",
  "networkFeeUsd": "0.01",
  "lifiFeesUsd": "0.002",
  "ugtpFeeUsd": "0.051",
  "ugtpFeeStaticUsd": 0.05,
  "ugtpFeePercent": 0.1,
  "feeTokenAmount": "0.061",
  "feeTokenSymbol": "USDC",
  "feeReserveTokenAmount": "0.061",
  "slippage": 0.005,
  "estimatedTimeSeconds": 30,
  "bridge": "uniswap",
  "provider": "uniswap"
}
Open in Swagger
POST/swap/prepareBuild a gasless same- or cross-chain swap user operation
Details

Wraps the LI.FI swap/bridge tx as a gasless UserOp (approve + swap) on the source networkId, charging the fee from fromToken (or feeToken). For cross-chain (toNetworkId differs) the gasless op runs on the source chain and LI.FI bridges to the destination. The response includes ugtpFee — the worst-case paymaster fee ({ tokenAmount, feeToken }, base units). Sign userOperationHash and submit via /transaction/submit; poll /transaction/:id. An unsafe security verdict returns 422. The prepared operation is held server-side for a limited time (default 30 minutes). If it expires before submit, prepare again. Returns 409 if a duplicate user operation (same UserOp hash) was already materialized. If the simulated calldata reverts deterministically (e.g. the route's bridge/DEX rejects the amount as below its minimum), returns 422 with a coded error ({ error, code, reason }) — e.g. code SWAP_AMOUNT_TOO_SMALL, or EXECUTION_SIMULATION_REVERTED with the raw revert reason. Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: X-API-Key

Request body

ownerAddress string required
Owner EOA — the gateway resolves the smart account from it
networkId string required
Source chain id (where the gasless op runs)
toNetworkId string
Destination chain id (defaults to networkId; a different value makes it cross-chain)
fromToken string required
Input token address (on networkId)
toToken string required
Output token address (on toNetworkId)
amount string required
Input amount, base units
slippage number
Slippage tolerance as a fraction (0.005 = 0.5%); LI.FI default when omitted
feeToken string
Fee token address (defaults to fromToken)

Request example

{
  "ownerAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
  "networkId": "137",
  "toNetworkId": "42161",
  "fromToken": "0x...",
  "toToken": "0x...",
  "amount": "1000000",
  "slippage": 0.005
}

Response 201

{
  "operationId": "ugtp_operation_...",
  "userOperation": {
    "sender": "0x1234567890abcdef1234567890abcdef12345678",
    "nonce": "0x0",
    "initCode": "0x",
    "callData": "0x...",
    "accountGasLimits": "0x...",
    "preVerificationGas": "0x...",
    "gasFees": "0x...",
    "paymasterAndData": "0x...",
    "signature": "0x"
  },
  "userOperationHash": "0xabc...",
  "security": {
    "verdict": "safe",
    "reason": null,
    "provider": "mock"
  },
  "ugtpFee": {
    "tokenAmount": "52000",
    "feeToken": "0xc2132d05d31c914a87c6611c10748aeb04b58e8f"
  }
}
Open in Swagger

3 endpoints

Networks

GET/network/listList networks
Details

Supported networks (proxied from the engine catalog). Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: X-API-Key

Response 200

{
  "networks": [
    {
      "name": "Avalanche C-Chain",
      "networkId": "43114",
      "explorerUrl": "https://snowtrace.io",
      "nativeCoin": "AVAX",
      "wrappedNative": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7"
    }
  ]
}
Open in Swagger
GET/network/:networkIdGet network
Details

A single network's metadata. Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: X-API-Key

Parameters

networkId string required
Chain id

Response 200

{
  "name": "Avalanche C-Chain",
  "networkId": "43114",
  "explorerUrl": "https://snowtrace.io",
  "nativeCoin": "AVAX",
  "wrappedNative": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7"
}
Open in Swagger
GET/network/:networkId/tokensList network tokens
Details

Tokens supported on a network (e.g. fee tokens). Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: X-API-Key

Parameters

networkId string required
Chain id

Response 200

{
  "networkId": "43114",
  "tokens": [
    {
      "address": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
      "symbol": "USDC",
      "decimals": 6
    }
  ]
}
Open in Swagger

1 endpoint

Fees

POST/fee/estimateEstimate gasless fee
Details

Preview the gasless fee (in the fee token) the paymaster would bill for the given calls + transferred assets, without submitting. Pass senderAddress (the smart-account address) directly; omit feeToken for the native-gas estimate. Returns 429 Too Many Requests if the rate limit is exceeded (check the Retry-After response header).

Authentication: X-API-Key

Request body

networkId string required
Chain id
senderAddress string required
Smart-account address
calls Call[] required
Each: { to, data?, value? } (hex)
transferredAssets Asset[] required
Each: { address, amount } (base-unit hex)
feeToken string
Fee token address (omit for native-gas estimate)

Request example

{
  "networkId": "43114",
  "senderAddress": "0x1234567890abcdef1234567890abcdef12345678",
  "calls": [
    {
      "to": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
      "value": "0x2386f26fc10000",
      "data": "0x"
    }
  ],
  "transferredAssets": [
    {
      "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
      "amount": "0x2386f26fc10000"
    }
  ],
  "feeToken": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E"
}

Response 200

{
  "estimatedFee": "0.0512",
  "estimatedFeeRaw": "0xc7f8",
  "symbol": "USDC"
}
Open in Swagger

3 endpoints

System Admins

POST/user/system-admin/createCreate system admin
Details

Create a new system_admin account (name, email, password). Restricted to the platform owner. The account is created pre-verified so the admin can sign in immediately.

Authentication: Bearer JWT (platform owner)

Request body

name string required
Display name
email string required
Admin email address
password string required
Password, minimum 8 characters

Request example

{
  "name": "Ops Admin",
  "email": "admin@example.com",
  "password": "********"
}

Response 201

{
  "id": "ugtp_user_a1b2c3d4e5f6",
  "email": "admin@example.com"
}
Open in Swagger
GET/user/system-admin/listList system admins
Details

List platform staff — owners and system admins. Restricted to the platform owner.

Authentication: Bearer JWT (platform owner)

Response 200

{
  "admins": [
    {
      "id": "ugtp_user_a1b2c3d4e5f6",
      "email": "admin@example.com",
      "name": "Ops Admin",
      "systemRole": "system_admin",
      "createdAt": "2026-06-18T12:00:00.000Z"
    }
  ]
}
Open in Swagger
DELETE/user/system-admin/:userId/deleteRemove system admin
Details

Revoke a system admin's platform access by demoting them to a client admin (the user account is kept — other records reference it). Restricted to the platform owner; the owner cannot remove their own access, and only system_admin users can be removed.

Authentication: Bearer JWT (platform owner)

Response 204

(no content)
Open in Swagger

Troubleshooting

Errors and recovery

Branch on status and stable error code, preserve the operation id in logs, and retry only when the response tells you the request is safe to repeat.

400

Invalid request

Correct malformed fields or an unsupported network or token.

401 / 403

Authentication or policy

Refresh the session JWT or verify project API-key scope.

409

Duplicate operation

Reuse the existing operation id and continue polling.

422

Simulation rejected

Inspect the coded verdict, adjust the calls, and prepare again.

429

Rate limited

Honor Retry-After and retry with bounded exponential backoff.

Next steps

Ship with confidence

Re-check credentials, supported networks, status polling, fee policy, and production monitoring before you direct real user traffic to UGTP.