# UGTP Docs — Universal Gateway Transaction Platform 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. - API base URL: https://api.ugtp.io/v1 - Swagger: https://api.ugtp.io/docs/ - OpenAPI: https://doc.ugtp.io/openapi.yaml - Markdown: https://doc.ugtp.io/docs.md - Authentication example: `X-API-Key: $UGTP_API_KEY` ## 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. ## Start building ### [Send a gasless transaction](https://doc.ugtp.io/#quickstart) Most popular Sponsor gas and track execution through one UGTP flow. ### [Create a smart account](https://doc.ugtp.io/#smart-accounts) Start with identity Resolve or create an ERC-4337 account for an owner address. ### [Build a cross-chain swap](https://doc.ugtp.io/#integrations) Route assets Quote, prepare, sign, submit, and track a LI.FI-backed route. ## Make your first authenticated request Keep secrets server-side, verify your environment, then build the same prepare–sign–submit loop for every operation. ### 1. Authenticate Use a session JWT for onboarding and a project X-API-Key for data-plane requests. Guide: https://doc.ugtp.io/#authentication ### 2. Prepare and sign Prepare an unsigned user operation, then sign its userOperationHash client-side. Guide: https://doc.ugtp.io/#user-operations ### 3. Submit and track Submit the signed operation and poll its operation id until terminal status. Guide: https://doc.ugtp.io/#transactions ### cURL sample ```curl curl "https://api.ugtp.io/v1/network/list" \ -H "X-API-Key: $UGTP_API_KEY" ``` ### JavaScript sample ```javascript const response = await fetch("https://api.ugtp.io/v1/network/list", { headers: { "X-API-Key": process.env.UGTP_API_KEY }, }); const networks = await response.json(); ``` ## 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. ### Bearer session JWT Register, verify, or sign in, then manage projects, members, and API keys. Browse Auth endpoints →: https://doc.ugtp.io/#api-auth ### X-API-Key Send the project key on data-plane calls and rotate it without changing your integration flow. Manage project keys →: https://doc.ugtp.io/#api-projects ## Guides ### [Environments](https://doc.ugtp.io/#environments) Choose staging while validating an integration, then move the same API flow to production. Search context: Production and staging base URLs and migration guidance. Related API groups: health ### [Authentication](https://doc.ugtp.io/#authentication) Session JWTs manage projects and API keys; X-API-Key authenticates project data-plane calls. Search context: Bearer session JWT, project API keys, and X-API-Key usage. Related API groups: auth, projects ### [Smart accounts](https://doc.ugtp.io/#smart-accounts) Resolve deterministic account addresses, create account records, and inspect deployment state. Search context: ERC-4337 smart account resolution and account lifecycle. Related API groups: accounts ### [User operations](https://doc.ugtp.io/#user-operations) Prepare an unsigned operation, sign its hash client-side, and submit it before it expires. Search context: UserOperation preparation, signing, expiry, and submission. Related API groups: transactions ### [Transactions](https://doc.ugtp.io/#transactions) Prepare, submit, and poll gasless transaction operations through terminal execution status. Search context: Gasless transactions, transfers, operation status, and receipts. Related API groups: transactions ### [Status synchronization](https://doc.ugtp.io/#webhooks) UGTP mirrors Engine lifecycle updates asynchronously; public clients poll transaction status because customer-configurable webhook subscriptions are not currently exposed. Search context: Asynchronous status synchronization, polling, retried updates, and idempotent consumers. Related API groups: transactions ### [Networks](https://doc.ugtp.io/#networks) Discover serviceable networks and supported fee tokens before preparing an operation. Search context: Network ids, chain metadata, testnets, and supported tokens. Related API groups: networks ### [Transaction lifecycle](https://doc.ugtp.io/#transaction-lifecycle) Track a prepared ERC-4337 operation from signing and queueing through submission, confirmation, and finality. Search context: Transaction statuses, userOpHash, transactionHash, replacement, retry, and finality. Related API groups: transactions ### [Status updates](https://doc.ugtp.io/#status-updates) Poll by userOpHash and process mirrored lifecycle fields idempotently; public webhook subscriptions are not currently available. Search context: Status payload fields, asynchronous synchronization, polling, retry, and idempotency. Related API groups: transactions ### [Fees](https://doc.ugtp.io/#fees) Use estimates before signing and returned fee breakdowns after execution; live API values remain authoritative. Search context: Fee estimate, feeBreakdown, fee token, collected, refunded, and relayer cost. Related API groups: fees, transactions ### [Deployed contracts](https://doc.ugtp.io/#deployed-contracts) Verify the Paymaster proxy addresses used on supported mainnet networks. Search context: Paymaster proxy contract addresses, chain IDs, and block explorers. Related API groups: networks, transactions ### [Errors and recovery](https://doc.ugtp.io/#errors) Handle coded simulation errors, rate limits, unsafe verdicts, duplicates, and expired preparations. Search context: SWAP_AMOUNT_TOO_SMALL, EXECUTION_SIMULATION_REVERTED, 409, 422, and 429 recovery. Related API groups: transactions, swap ### [Production launch](https://doc.ugtp.io/#staging-to-production) Re-check base URLs, credentials, network availability, and status polling before launch. Search context: Production launch checklist for a tested staging integration. Related API groups: health, networks, transactions ## A coordinated path from intent to execution UGTP keeps client signing explicit while coordinating security, sponsorship, routing, and network execution behind one gateway. ### Nodes - Client (client): Builds intent and signs user-operation hashes. - UGTP Gateway (gateway): Authenticates public requests and orchestrates account, swap, fee, and transaction APIs. - Security analysis (ugtp-service): Analyzes transaction intent before submission. - UGTP Engine (ugtp-service): Prepares, submits, and tracks ERC-4337 user operations. - UGTP Paymaster (ugtp-service): Calculates fees and sponsors gasless operations for supported assets. - LI.FI routing (external-provider): Returns same-chain swap and cross-chain route execution data. - Blockchain network (network): Executes the submitted operation or routed transfer. ### Connections - client → gateway: API request - gateway → security: analyze - gateway → engine: prepare and submit - engine → paymaster: request sponsorship - gateway → routing: discover route - engine → network: execute operation - routing → network: execute route ## Know what each service owns Use these profiles to place retries, validation, observability, and user messaging at the correct boundary. ### [LI.FI routing](https://doc.ugtp.io/#integration-lifi) Provide route discovery and execution data for UGTP same-chain swaps and cross-chain swaps. Lifecycle: Swap quote, route preparation, and routed transfer tracking Inputs: - Source and destination chain - Input and output token - Transfer amount Outputs: - Swap quote and route - Execution calldata - Routed transfer status Trust boundary: Routing data crosses from the UGTP gateway to an external routing provider before network execution. Failure and degraded modes: - No serviceable route - Route simulation rejects the amount - Transfer reaches a failed terminal state Related sections: transactions, networks, errors 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 documentation: https://docs.li.fi/ Swagger targets: - Get routes: https://api.ugtp.io/docs/#/swap - Execute transfer: https://api.ugtp.io/docs/#/swap - Transfer status: https://api.ugtp.io/docs/#/swap ### [UGTP Engine](https://doc.ugtp.io/#integration-engine) Prepare, submit, and track ERC-4337 user operations behind the public Gateway transaction API. Lifecycle: Preparation, submission, and execution tracking Inputs: - Smart account - Calls - Signed user operation Outputs: - Unsigned user operation - User operation hash - Operation and transaction status Trust boundary: The gateway passes validated operation intent into UGTP execution services. Failure and degraded modes: - Unsafe security verdict - Deterministic simulation revert - Prepared operation expires - Duplicate operation Related sections: smart-accounts, user-operations, transactions, errors 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. Swagger targets: - Send transaction: https://api.ugtp.io/docs/#/transaction - Get transaction: https://api.ugtp.io/docs/#/transaction - Batch transactions: https://api.ugtp.io/docs/#/transaction ### [UGTP Paymaster](https://doc.ugtp.io/#integration-paymaster) Sponsor gasless ERC-4337 operations and calculate fees when the selected asset is supported. Lifecycle: Fee estimation and transaction sponsorship Inputs: - User operation - Network - Transferred assets or fee token Outputs: - Sponsored gas - Worst-case UGTP fee or fee estimate Trust boundary: The execution flow relies on UGTP sponsorship policy before the operation reaches the network. Failure and degraded modes: - Unsupported fee token - Fee estimate unavailable - Insufficient amount after fee reserve Related sections: user-operations, transactions, networks, errors 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. Swagger targets: - Send sponsored transaction: https://api.ugtp.io/docs/#/transaction - Batch sponsored transactions: https://api.ugtp.io/docs/#/transaction ## Build around durable primitives Accounts define identity, user operations carry signed intent, and operation records expose progress through finality. ## 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 ## 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. ### Prepare and sign Prepare the operation, sign the returned hash, and retain its userOpHash as the stable operation identifier. ### 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. ### 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. Transaction Swagger: https://api.ugtp.io/docs/#/transaction ## Status updates Use polling as the public integration contract and tolerate retried synchronization. Customer-configurable webhook subscriptions are not currently exposed. UGTP mirrors Engine lifecycle updates asynchronously, so public clients should poll transaction status by userOpHash and process status, transactionHash, reason, and terminal feeBreakdown fields idempotently. - `unknown`: No operation record was found for this userOpHash. Client action: Verify the userOpHash and network before preparing a replacement operation. - `received`: The operation is recorded and waiting to enter the submission queue. Client action: Keep polling by userOpHash; do not create a replacement while it remains active. - `queued`: The operation is queued for relayer submission. Client action: Keep polling by userOpHash; do not create a replacement while it remains active. - `submitted`: The operation has a current outer transactionHash and is awaiting a mined receipt. Client action: Keep polling by userOpHash because the current transactionHash can change during replacement. - `confirmed`: The operation was mined but has not yet reached the network's reorg-safe depth. Client action: Do not retry; continue tracking until finalized if reorg safety matters. - `finalized`: The mined operation reached the network's configured reorg-safe confirmation depth. Client action: Do not retry; this is the successful reorg-safe state. - `failed`: Execution failed and the operation will not advance. Client action: Inspect the reported error, correct the cause, then prepare and sign a new operation. - `dropped`: The operation was dropped and will not advance. Client action: Prepare and sign a new operation if the action is still required. ### Transaction status response ```json { "userOpHash": "0xabc...", "transactionHash": "0xdef...", "status": "confirmed", "reason": null, "feeBreakdown": null } ``` ## 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. The current Paymaster-backed ERC-20 configuration is a $0.05 static fee plus 0.1% of transferred input value. This is current configuration, not a permanent price guarantee; the live estimate and returned transaction data are authoritative. Fee Swagger: https://api.ugtp.io/docs/#/fee ## Deployed contracts Use the Paymaster proxy as the stable integration address on supported mainnets; its backing implementation can be upgraded. - Ethereum Mainnet (chain ID 1): `0x1Fc1032DD0A8702fB4b0EE93Fbb746E151DF7625` — [View on block explorer](https://etherscan.io/address/0x1Fc1032DD0A8702fB4b0EE93Fbb746E151DF7625) - Arbitrum One (chain ID 42161): `0x1Fc1032DD0A8702fB4b0EE93Fbb746E151DF7625` — [View on block explorer](https://arbiscan.io/address/0x1Fc1032DD0A8702fB4b0EE93Fbb746E151DF7625) - Avalanche C-Chain (chain ID 43114): `0x1Fc1032DD0A8702fB4b0EE93Fbb746E151DF7625` — [View on block explorer](https://snowtrace.io/address/0x1Fc1032DD0A8702fB4b0EE93Fbb746E151DF7625) - Polygon PoS (chain ID 137): `0x1Fc1032DD0A8702fB4b0EE93Fbb746E151DF7625` — [View on block explorer](https://polygonscan.com/address/0x1Fc1032DD0A8702fB4b0EE93Fbb746E151DF7625) - Base (chain ID 8453): `0x1Fc1032DD0A8702fB4b0EE93Fbb746E151DF7625` — [View on block explorer](https://basescan.org/address/0x1Fc1032DD0A8702fB4b0EE93Fbb746E151DF7625) - BNB Smart Chain (chain ID 56): `0x1Fc1032DD0A8702fB4b0EE93Fbb746E151DF7625` — [View on block explorer](https://bscscan.com/address/0x1Fc1032DD0A8702fB4b0EE93Fbb746E151DF7625) ## 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. Deterministic failures: SWAP_AMOUNT_TOO_SMALL and EXECUTION_SIMULATION_REVERTED need changed input, not blind retries. ## You are viewing Production The portal, examples, and precise Swagger targets follow the selected deployment environment. Base URL: https://api.ugtp.io/v1 Production metadata and examples do not expose staging hosts. ## Ship with confidence Re-check credentials, supported networks, status polling, fee policy, and production monitoring before you direct real user traffic to UGTP. ## API reference Curated endpoints are preserved here with request context and links to the best available Swagger tag or area. ## Health ### GET /health Health check Returns service health. No authentication required. Authentication: None Response status: 200 #### Response example ```json { "status": "ok", "timestamp": "2026-06-18T12:00:00Z", "version": "1.0.0" } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/health ## Auth ### POST /auth/register Register 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 ```json { "name": "Jane Dev", "email": "user@example.com", "password": "********", "confirmPassword": "********" } ``` Response status: 200 #### Response example ```json { "message": "Verification email sent", "email": "user@example.com" } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/auth ### POST /auth/verify Verify email 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 ```json { "token": "A1B2C3D4E5" } ``` Response status: 200 #### Response example ```json { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "message": "Email verified.", "systemRole": "client_admin" } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/auth ### POST /auth/login Login 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 ```json { "email": "user@example.com", "password": "********" } ``` Response status: 200 #### Response example ```json { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "email": "user@example.com", "systemRole": "client_admin" } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/auth ### POST /auth/resend Resend code 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 ```json { "email": "user@example.com" } ``` Response status: 200 #### Response example ```json { "message": "New verification code sent", "email": "user@example.com" } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/auth ### POST /auth/change-password Change password 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 ```json { "currentPassword": "********", "newPassword": "********", "confirmPassword": "********" } ``` Response status: 200 #### Response example ```json { "message": "Password updated." } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/auth ### GET /auth/current-user Current user 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 status: 200 #### Response example ```json { "id": "ugtp_user_a1b2c3d4e5f6", "email": "user@example.com", "name": "Jane Dev", "systemRole": "client_admin" } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/auth ## Projects & API Keys ### POST /project/create Create project 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 ```json { "name": "My App", "members": [ { "email": "teammate@example.com", "role": "admin" } ] } ``` Response status: 201 #### Response example ```json { "id": "ugtp_proj_a1b2c3d4e5f6", "name": "My App", "apiKey": { "id": "ugtp_project_api_key_a1b2c3d4e5f6", "name": "My App", "keyPrefix": "ugtp2_0123", "value": "ugtp2_0123456789abcdef0123456789abcdef" }, "warnings": [] } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/project ### DELETE /project/:projectId Delete project (soft delete) 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 Response status: 204 Swagger target (best available tag or area): https://api.ugtp.io/docs/#/project ### GET /project/list List my projects 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 status: 200 #### Response example ```json { "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" } ] } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/project ### PATCH /project/:projectId/fees Set LI.FI integrator fee 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 ```json { "lifiFeePercent": 0.25 } ``` Response status: 200 #### Response example ```json { "projectId": "ugtp_proj_a1b2c3d4e5f6", "lifiFeePercent": 0.25 } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/project ### GET /project/all List all projects (staff) 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 status: 200 #### Response example ```json { "projects": [ { "id": "ugtp_proj_a1b2c3d4e5f6", "name": "Acme", "isActive": true, "transactionCount": 4210, "createdAt": "2026-06-18T12:00:00.000Z" } ] } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/project ### GET /project/:projectId/api-key List API keys 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 status: 200 #### Response example ```json { "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" } ] } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/project ### POST /project/:projectId/api-key/create Create API key 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 ```json { "name": "Server key", "scopes": [ "full_access" ] } ``` Response status: 201 #### Response example ```json { "id": "ugtp_key_a1b2c3d4e5f6", "apiKey": "ugtp2_0123456789abcdef0123456789abcdef", "keyPrefix": "ugtp2_0123" } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/project ### DELETE /project/:projectId/api-key/:keyId Revoke API key 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 Response status: 204 Swagger target (best available tag or area): https://api.ugtp.io/docs/#/project ### GET /project/:projectId/members/list List members 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 status: 200 #### Response example ```json { "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" } ] } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/project ### POST /project/:projectId/members/add Add or invite member 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 ```json { "email": "teammate@example.com", "role": "viewer" } ``` Response status: 201 #### Response example ```json { "added": { "status": "added", "member": { "userId": "ugtp_user_a1b2c3d4e5f6", "email": "teammate@example.com", "name": "Teammate", "role": "viewer" } }, "invited": { "status": "invited", "email": "newperson@example.com" } } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/project ### GET /project/:projectId/invitations List pending invitations 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 status: 200 #### Response example ```json { "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" } ] } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/project ### POST /project/:projectId/invitations/:invitationId/resend Resend invitation 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 Response status: 204 Swagger target (best available tag or area): https://api.ugtp.io/docs/#/project ### POST /project/:projectId/invitations/:invitationId/cancel Cancel invitation 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 Response status: 204 Swagger target (best available tag or area): https://api.ugtp.io/docs/#/project ### DELETE /project/:projectId/members/:memberId Remove member 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 Response status: 204 Swagger target (best available tag or area): https://api.ugtp.io/docs/#/project ### GET /project/invitations/accept Preview invitation 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 status: 200 #### Response example ```json { "projectName": "Acme", "email": "teammate@example.com", "role": "viewer", "expiresAt": "2026-07-20T12:00:00.000Z" } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/project ### POST /project/invitations/accept Accept invitation 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 ```json { "token": "a1b2c3d4e5f6...", "password": "********", "name": "Jane Dev" } ``` Response status: 201 #### Response example ```json { "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" } } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/project ### GET /api-key/all List manageable API keys 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 status: 200 #### Response example ```json { "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" } } ] } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/api-key ### POST /api-key/:keyId/revoke Revoke an API key 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 Response status: 204 Swagger target (best available tag or area): https://api.ugtp.io/docs/#/api-key ## Dashboard ### GET /dashboard/stats Dashboard stats 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 status: 200 #### Response example ```json { "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 } ] } ] } } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/dashboard ### GET /dashboard/account-series Account statistics (Users) time series 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 status: 200 #### Response example ```json { "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 } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/dashboard ### GET /dashboard/transactions Dashboard transactions 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 status: 200 #### Response example ```json { "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 } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/dashboard ## Accounts ### POST /account/create Resolve account (derive + cache) 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 ```json { "ownerAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0", "networkId": "43114" } ``` Response status: 201 #### Response example ```json { "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" } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/account ### POST /account/resolve-single Single network-independent resolve 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 ```json { "ownerAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0" } ``` Response status: 200 #### Response example ```json { "address": "0x1234567890abcdef1234567890abcdef12345678" } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/account ### POST /account/info Derive address (preview) 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 ```json { "ownerAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0", "networkId": "43114" } ``` Response status: 200 #### Response example ```json { "ownerAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0", "address": "0x1234567890abcdef1234567890abcdef12345678", "networkId": "43114", "isDeployed": false, "kernelVersion": "v3.3" } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/account ### GET /account/is-deployed Check deployment 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 status: 200 #### Response example ```json { "isDeployed": true } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/account ### GET /account/series Account-creation series (admin chart) 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 status: 200 #### Response example ```json { "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 } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/account ### POST /account/resolve-batch Batch-resolve owners 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 ```json { "ownerAddresses": [ "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0" ] } ``` Response status: 200 #### Response example ```json { "results": [ { "ownerAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0", "smartAccountAddress": "0x1234567890abcdef1234567890abcdef12345678" } ], "count": 1 } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/account ## Transactions ### POST /transaction/prepare Prepare (generate user operation) 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 ```json { "accountId": "ugtp_acc_a1b2c3d4e5f6", "networkId": "43114", "calls": [ { "to": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0", "value": "0x2386f26fc10000", "data": "0x" } ], "transferredAssets": [ { "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", "amount": "0x2386f26fc10000" } ] } ``` Response status: 201 #### Response example ```json { "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" } } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/transaction ### POST /transaction/submit Submit (send signed user operation) 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 ```json { "operationId": "ugtp_op_a1b2c3d4e5f6", "signature": "0x..." } ``` Response status: 200 #### Response example ```json { "operationId": "ugtp_op_a1b2c3d4e5f6", "status": "queued", "userOperationHash": "0xabc...", "networkId": "43114" } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/transaction ### GET /transaction/:id Get 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 #### Parameters - `id` (string, required): Operation id Response status: 200 #### Response example ```json { "operationId": "ugtp_op_a1b2c3d4e5f6", "status": "confirmed", "transactionHash": "0xdef...", "userOperationHash": "0xabc...", "networkId": "43114", "replacementCount": 1, "reason": null } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/transaction ### GET /transaction List 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 #### 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 status: 200 #### Response example ```json { "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 } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/transaction ### POST /transaction/transfer Prepare same-chain transfer 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 ```json { "ownerAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0", "networkId": "43114", "transfers": [ { "tokenAddress": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", "amount": "10000000000000000", "recipientAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0" } ] } ``` Response status: 201 #### Response example ```json { "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" } } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/transaction ## Swap ### GET /swap/chains List swap-supported chains 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 status: 200 #### Response example ```json { "chains": [ { "networkId": "42161", "name": "Arbitrum One", "nativeCurrency": "ETH", "logoUrl": "https://..." } ] } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/swap ### GET /swap/tokens?networkId= List swap tokens for a chain 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 status: 200 #### Response example ```json { "networkId": "42161", "tokens": [ { "address": "0x...", "symbol": "USDC", "name": "USD Coin", "decimals": 6, "networkId": "42161" } ] } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/swap ### POST /swap/quote Preview a same- or cross-chain swap 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 ```json { "ownerAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0", "networkId": "137", "toNetworkId": "42161", "fromToken": "0x...", "toToken": "0x...", "amount": "1000000", "slippage": 0.005 } ``` Response status: 200 #### Response example ```json { "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" } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/swap ### POST /swap/prepare Build a gasless same- or cross-chain swap user operation 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 ```json { "ownerAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0", "networkId": "137", "toNetworkId": "42161", "fromToken": "0x...", "toToken": "0x...", "amount": "1000000", "slippage": 0.005 } ``` Response status: 201 #### Response example ```json { "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" } } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/swap ## Networks ### GET /network/list List networks 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 status: 200 #### Response example ```json { "networks": [ { "name": "Avalanche C-Chain", "networkId": "43114", "explorerUrl": "https://snowtrace.io", "nativeCoin": "AVAX", "wrappedNative": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7" } ] } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/network ### GET /network/:networkId Get network 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 status: 200 #### Response example ```json { "name": "Avalanche C-Chain", "networkId": "43114", "explorerUrl": "https://snowtrace.io", "nativeCoin": "AVAX", "wrappedNative": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7" } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/network ### GET /network/:networkId/tokens List network tokens 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 status: 200 #### Response example ```json { "networkId": "43114", "tokens": [ { "address": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", "symbol": "USDC", "decimals": 6 } ] } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/network ## Fees ### POST /fee/estimate Estimate gasless fee 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 ```json { "networkId": "43114", "senderAddress": "0x1234567890abcdef1234567890abcdef12345678", "calls": [ { "to": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0", "value": "0x2386f26fc10000", "data": "0x" } ], "transferredAssets": [ { "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", "amount": "0x2386f26fc10000" } ], "feeToken": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E" } ``` Response status: 200 #### Response example ```json { "estimatedFee": "0.0512", "estimatedFeeRaw": "0xc7f8", "symbol": "USDC" } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/fee ## System Admins ### POST /user/system-admin/create Create system admin 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 ```json { "name": "Ops Admin", "email": "admin@example.com", "password": "********" } ``` Response status: 201 #### Response example ```json { "id": "ugtp_user_a1b2c3d4e5f6", "email": "admin@example.com" } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/user ### GET /user/system-admin/list List system admins List platform staff — owners and system admins. Restricted to the platform owner. Authentication: Bearer JWT (platform owner) Response status: 200 #### Response example ```json { "admins": [ { "id": "ugtp_user_a1b2c3d4e5f6", "email": "admin@example.com", "name": "Ops Admin", "systemRole": "system_admin", "createdAt": "2026-06-18T12:00:00.000Z" } ] } ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/user ### DELETE /user/system-admin/:userId/delete Remove system admin 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 status: 204 #### Response example ```json (no content) ``` Swagger target (best available tag or area): https://api.ugtp.io/docs/#/user