# OpenAPIs

## API Configurations

### Base URLs

The API is deployed across different environments. Please use the appropriate base URL for your integration stage .

<table><thead><tr><th>Environment</th><th>Base URL</th></tr></thead><tbody><tr><td><strong>Testnet</strong></td><td><pre><code><strong>https://uropenapi-qa.ur-inc.xyz
</strong></code></pre></td></tr><tr><td><strong>Production</strong></td><td><code>https://openapi.ur.app</code></td></tr></tbody></table>

### API Authentication

The authentication method for the following Core Banking APIs refers to [this document](/developer-resources/signature-and-verify.md#part-a-partner-authentication-ur-open-api-and-webhooks).

## Mint URID

**Endpoint: POST /v1/mint/nft**

Allows the Partner to create a registered user in the UR system via an API call and simultaneously mint a UR-Bank NFT ID for the user.

Before calling this endpoint, first query the Account NFT contract with `tokenOfOwnerByIndex` to check whether the user address already owns an NFT:

* If an NFT already exists, reuse the existing `tokenId` and do not call mint again
* If no NFT exists, call this endpoint to mint a new URID

Calling mint for an address that already has an NFT can return `10005` (`Duplicate Mint`).

**Request Parameters**:

```json
{
  "email": "example@test.com",
  "evmAddress": "0xD4FcD82E3589b81A6de532e92E574761CC619531",
  "signature": "0x7f561411c39e993c807cd49d336e32bc2ec5e9ca12035a7efaa724ca760f97dc66f45a73b1b5dbf605127ee9ec5989e88972ba6c9a09d3684b0dec436229cc2701",
  "hash": "test-hash-1768792402",
  "deadline": "1768792702"
}
```

| Field        | Type   | Required | Description                                                                                                                                                      |
| ------------ | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `email`      | string | Yes      | User's email address                                                                                                                                             |
| `evmAddress` | string | Yes      | User's EVM Wallet Address                                                                                                                                        |
| `signature`  | string | Yes      | User's wallet signature (EIP-191). See [User Authentication](/developer-resources/signature-and-verify.md#part-b-user-authentication-ur-api) for signing details |
| `hash`       | string | Yes      | A Keccak256 hash of the business payload or a unique message                                                                                                     |
| `deadline`   | string | Yes      | Unix timestamp (seconds) for signature expiration                                                                                                                |

**User Signature Generation**:

1. Construct base message: `baseMessage = hash + deadline` (string concatenation)
2. Generate intermediate hash: `intermediateHash = Keccak256(baseMessage)`
3. Construct final message: `finalMessage = "I agree to access my profile. " + intermediateHash.hex()`
4. User signs the `finalMessage` using their wallet (EIP-191)

For detailed signing and verification rules, please refer to [Signature and Verification](/developer-resources/signature-and-verify.md).

**Response Example**:

```json
{
  "code": 0,
  "message": "success",
  "data": {
    "tokenId": 6021634448,
    "txHash": "0xb21c3e15de2f85fad9b04729f6db9ed6477fe08524b4db91a8e35ef2ab08039e"
  }
}
```

**Response Field Description**:

* `tokenId`: The minted NFT Token ID, required for all subsequent API calls
* `txHash`: On-chain transaction hash

## Fetch UR Account Information

**Endpoint: POST /v1/profile**

Get user profile information including on-chain status and ERC20 authorization information.

**Request Parameters**:

```json
{
  "urId": 12345,
  "authCode": "UAC_a1b2c3"
}
```

| Field      | Type   | Required | Description                                                                                                |
| ---------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------- |
| `urId`     | int64  | No       | UR user ID, can directly query on-chain information                                                        |
| `authCode` | string | No       | OAuth authorization code for future extension (can exchange via authorization code when `urId` is missing) |

> **Note**: At least one of `urId` or `authCode` must be provided. Currently, the endpoint primarily uses `urId` for queries, `authCode` field is for future authorization code extension scenarios.

**Response Example**:

```json
{
  "code": 0,
  "message": "",
  "data": {
    "urId": 12345,
    "chainStatus": 5,
    "evmAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    "usedLimit": 120000000,
    "clientLimit": 500000000,
    "startLimitDate": 1714377600,
    "allowances": [
      {
        "tokenSymbol": "USD",
        "hasAllowance": true
      },
      {
        "tokenSymbol": "EUR",
        "hasAllowance": true
      },
      {
        "tokenSymbol": "CHF",
        "hasAllowance": false
      },
      {
        "tokenSymbol": "JPY",
        "hasAllowance": true
      },
      {
        "tokenSymbol": "CNH",
        "hasAllowance": false
      },
      {
        "tokenSymbol": "SGD",
        "hasAllowance": false
      },
      {
        "tokenSymbol": "HKD",
        "hasAllowance": false
      }
    ],
    "kycRetryVerificationLevel": 0,
    "kycRetryVerificationLevelStr": "UNKNOWN",
    "kycCurrentStep": 0,
    "kycCurrentStepStr": "UNKNOWN",
    "bankAccounts": {
      "CHF": [
        {
          "account": "CH93 0000 0000 0001 2345 6",
          "bankName": "SR Saphirstein AG",
          "bankAddress": "Bellerivestrasse 245, 8008 Zurich, Switzerland",
          "bic": "SAHHCHZ2"
        }
      ],
      "EUR": [
        {
          "account": "CH93 0000 0000 0001 2345 6",
          "bankName": "SR Saphirstein AG",
          "bankAddress": "Bellerivestrasse 245, 8008 Zurich, Switzerland",
          "bic": "SAHHCHZ2"
        }
      ]
    },
    "crsInfo": {
      "needCrs": true,
      "restrictDate": 1778402365685,
      "url": "https://ur-fe2.qa4.gomantle.org/info-provide/crs?hash=D9cEvsYk&source=ur"
    }
  }
}
```

**Response Field Description**:

* `urId`: UR NFT ID
* `chainStatus`: [On-chain status](broken://pages/4Z6cLaUUPkiE0GxSp83d)
  * `1` - SoftBlocked
  * `2` - Tourist
  * `3` - Blocked
  * `4` - Closed
  * `5` - Live (Normal)
* `evmAddress`: User's EVM address
* `usedLimit`: Current Accumulated Usage: The total volume used within the current 30-day billing cycle. This value is retrieved directly from the UR [smart contract ](/developer-resources/smart-contracts.md#account)on-chain.
  * <mark style="color:$info;">The value is denominated in CHF with 2 decimal places (e.g., a return value of</mark> <mark style="color:$info;">`505`</mark> <mark style="color:$info;">represents</mark> <mark style="color:$info;">`5.05 CHF`</mark><mark style="color:$info;">). This limit is incremented by all fiat-related operations, including Card Spending, FX, On-ramping, and Cash Payouts.</mark>
* `clientLimit`: Maximum Monthly Limit: The total spending capacity allocated to the user for a 30-day period.
  * <mark style="color:$info;">The unit and retrieval method are identical to</mark> <mark style="color:$info;">`usedLimit`</mark><mark style="color:$info;">.</mark> <mark style="color:$warning;">Any transaction (FX, Card Spending, On-ramp, or Cash Payout) that exceeds this threshold will be rejected by the smart contract. Partners are highly recommended to perform a pre-transaction check against the user's remaining limit (</mark><mark style="color:$warning;">`clientLimit`</mark> <mark style="color:$warning;">-</mark> <mark style="color:$warning;">`usedLimit`</mark><mark style="color:$warning;">) before initiating payment flows.</mark>
* `startLimitDate`: Limit start timestamp (seconds, UTC)
* `allowances`: ERC20 authorization list (batch queried on-chain via multicall)
  * `tokenSymbol`: Token symbol (USD, EUR, CHF, JPY, CNH, SGD, HKD)
  * `hasAllowance`: true if allowance has been set, otherwise false
* `kycRetryVerificationLevel`: KYC retry verification level (integer enum)
* `kycRetryVerificationLevelStr`: KYC retry verification level string
* `kycCurrentStep`: KYC current step (integer enum)
* `kycCurrentStepStr`: KYC current step string
* `bankAccounts`: Bank account information, keyed by currency code (e.g., `CHF`, `EUR`). Each currency maps to an array of bank account objects:
  * `account`: IBAN account number (generated based on user's NFT ID, Swiss CH IBAN format)
  * `bankName`: Bank name (e.g., `SR Saphirstein AG`)
  * `bankAddress`: Bank address
  * `bic`: SWIFT/BIC code (e.g., `SAHHCHZ2`)
* `crsInfo`: CRS information :
  * `needCrs`: Indicates whether the user is required to complete the CRS process.
  * `restrictDate`: Represents the deadline or restriction timestamp for completing the CRS process, usually in Unix timestamp format (milliseconds). After this time, the user may be softBlocked.
  * `url`: The CRS submission link that directs the user to the CRS information collection page.

\*\*

**KycRetryVerificationLevel Enum**:

| Value | Name                      | Description                                                      |
| ----- | ------------------------- | ---------------------------------------------------------------- |
| `0`   | UNKNOWN                   | Default value, Normal First KYC                                  |
| `1`   | ResetAll                  | Clear Sumsub + Sign, redo KYC + signature                        |
| `2`   | ResetSumsub               | Clear Sumsub KYC info, restart KYC                               |
| `3`   | ResetSign                 | Clear signature info, re-sign                                    |
| `4`   | ResetGPS                  | Proactive GPS reset (created manually in admin)                  |
| `5`   | RetryGPS                  | Passive GPS retry (auto-created when GPS fails) **(Deprecated)** |
| `6`   | RetryVerificationPassport | Supplement passport info                                         |
| `7`   | ResetKYCToReadID          | Reset KYC flow, use Sumsub + ReadID to redo KYC **(Deprecated)** |

**kycCurrentStep Enum**:

| Value | Name      | Description   |
| ----- | --------- | ------------- |
| `0`   | UNKNOWN   | Default value |
| `1`   | FormA     | Form A step   |
| `2`   | IDScan    | ID Scan step  |
| `3`   | SignFormA | Sign Form A   |
| `4`   | Review    | Review step   |
| `5`   | Rejected  | Rejected      |

## Fetch UR Account Balance

**Endpoint: POST /v1/balance**

Get user balance information.

**Request Parameters**:

```json
{
  "urId": 12345
}
```

| Field  | Type  | Required | Description |
| ------ | ----- | -------- | ----------- |
| `urId` | int64 | Yes      | UR user ID  |

**Response Example**:

```json
{
  "code": 0,
  "message": "",
  "data": [
    {
      "symbol": "USDC",
      "amount": "1000.50"
    },
    {
      "symbol": "USD24",
      "amount": "5000.00"
    }
  ]
}
```

**Response Field Description**:

* `symbol`: Token symbol
* `amount`: Balance amount, formatted according to Token's `Decimals` precision (e.g., 2 or 6 decimal places)

## Fetch Transaction History

**POST /v1/transactions**

Get user transaction records, supports multi-condition filtering and cursor pagination. The endpoint returns top-level pagination markers (`hasNextPage`, etc.) and structured information for each transaction.

**Request Parameters**:

```json
{
  "urId": 7639951412,
  "chainId": "eip155:5000",
  "symbol": "USD24",
  "type": "P2P",
  "transactionTypes": ["P2P", "FRX"],
  "pageSize": 20,
  "fromTimestamp": 1701234567,
  "toTimestamp": 1704234567,
  "direction": "ALL",
  "minAmount": "1000",
  "maxAmount": "100000",
  "status": "completed",
  "currencies": ["USD", "EUR"],
  "cursorTimestamp": 1702000000,
  "cursorId": 123456789,
  "prevCursorTimestamp": 1701990000,
  "prevCursorId": 123456700
}
```

| Field                                  | Type      | Required | Description                                                                                                      |
| -------------------------------------- | --------- | -------- | ---------------------------------------------------------------------------------------------------------------- |
| `urId`                                 | int64     | Yes      | UR user ID                                                                                                       |
| `type`                                 | string    | No       | Single transaction type filter: P2P, FRX, CTU, CRD, CDP, CWP, CTF                                                |
| `transactionTypes`                     | \[]string | No       | Multiple transaction types filter (mutually exclusive with `type`, which only supports a single value)           |
| `chainId`                              | string    | No       | Chain ID (e.g., `eip155:5000`)                                                                                   |
| `symbol`                               | string    | No       | Token symbol (on-chain Token or fiat 24 series)                                                                  |
| `currencies`                           | \[]string | No       | Fiat identifier array: EUR, USD, CHF, CNH                                                                        |
| `pageSize`                             | int       | No       | Items per page, default 50, max 100                                                                              |
| `fromTimestamp` / `toTimestamp`        | int64     | No       | Time range filter, Unix seconds                                                                                  |
| `direction`                            | string    | No       | Transaction direction: IN, OUT, ALL                                                                              |
| `minAmount` / `maxAmount`              | string    | No       | Amount range filter (on-chain smallest unit string, follows amount description at the beginning of the document) |
| `status`                               | string    | No       | Transaction status filter (pending/completed/rejected/…)                                                         |
| `cursorTimestamp` / `cursorId`         | int64     | No       | Cursor for next page pagination (obtained from previous page's `nextCursor`)                                     |
| `prevCursorTimestamp` / `prevCursorId` | int64     | No       | Cursor for previous page pagination (optional)                                                                   |

**Response Example**:

```json
{
  "code": 0,
  "message": "",
  "data": [
    {
      "title": "Transfer to Alice",
      "subtitle": "#7639",
      "amount": "-100.50",
      "type": "P2P",
      "timestamp": 1704234567,
      "currency": "USD",
      "direction": "OUT",
      "txHash": "0x21b4...",
      "chainId": "eip155:5000",
      "inputToken": "USD24",
      "inputAmount": "100.50",
      "status": "completed"
    },
    {
      "title": "Card Payment",
      "subtitle": "Merchant ABC",
      "amount": "-36.90",
      "type": "CRD",
      "timestamp": 1704234000,
      "currency": "USD",
      "direction": "OUT",
      "txHash": "0x3051...",
      "chainId": "eip155:5000",
      "mcc": 5812,
      "status": "pending"
    }
  ],
  "hasNextPage": true,
  "hasPrevPage": false,
  "nextCursor": {
    "timestamp": 1704233500,
    "id": 99887766
  },
  "prevCursor": {
    "timestamp": 1704235000,
    "id": 99887700
  },
  "currentPageSize": 2
}
```

**Response Field Description**:

**Top-level fields**

* `code` / `message`: Status code and description, `0` indicates success.
* `data`: Transaction records array.
* `hasNextPage` / `hasPrevPage`: Whether next/previous page exists.
* `nextCursor` / `prevCursor`: Pagination cursors (next page uses `cursorTimestamp/cursorId`, previous page uses `prevCursorTimestamp/prevCursorId`).
* `currentPageSize`: Number of entries returned in current page.

**Each item in `data` array (same as `/v1/transaction/query`)**

* `title` / `subtitle`: Display title information.
* `amount`: Amount string with sign to indicate direction.
* `type`: Transaction type abbreviation (P2P, FRX, CTU, CRD, CDP, CWP, CTF, etc.).
* `timestamp`: Transaction occurrence Unix seconds.
* `currency`: Token (USD24, EUR24, etc.).
* `direction`: `IN` / `OUT`.
* `txHash`: Transaction hash (if applicable).
* `chainId`: CAIP-2 chain identifier (e.g., `eip155:5000`).
* `image`: Transaction display icon or card logo.
* `inputToken` / `inputAmount` / `inputTokenAddress`: Original chain info for cross-chain transactions like CTU, CSW.
* `outputAmount`: Amount after FRX/CSW exchange.
* `token` / `tokenAddress`: Token and contract address involved in certain card or aggregated transactions.
* `mcc`, `reference`, `bankAccount`: Fields specific to card payments or bank transfers.
* `crdMultiToken`, `ctuExternalSender`, `fromAddress`, `toAddress`, etc.: Additional fields attached by transaction type.
* `status`: `pending` / `completed` / `rejected` / `unknown`.
* `statusCode` / `crdCurrency`: Card-returned status code, original currency, and other supplementary info.
* `listingTitle`, `txHashUrl`, `txIdIcon`, `officialName`: Supplementary fields for client display.

## Fetch Transaction Details

**POST /v1/transaction/query**

Query aggregated transaction details based on on-chain transaction hash, supports single or multiple hashes.

The `txHash` field accepts both **EVM transaction hashes** (`0x`-prefixed hex) and **Solana transaction signatures** (Base58-encoded, 32–88 characters). The server auto-detects the format.

**Request Parameters**:

```json
{
  "txPairs": [
    {
      "urId": 7639951412,
      "txHash": "0x1234567890abcdef..."
    },
    {
      "urId": 7639951412,
      "txHash": "65ocipMENgErDtSxqo16J2JY8V2raAHeMLDgQNnRibbe5dc4JsENok34dWgcoh1qgBQwEi54jcSejgW6RsQB7S5s"
    }
  ]
}
```

| Field     | Type           | Required | Description                                                                                                      |
| --------- | -------------- | -------- | ---------------------------------------------------------------------------------------------------------------- |
| `txPairs` | array\<object> | Yes      | Query pairs, each item contains `urId` + `txHash`. `txHash` accepts EVM hex (`0x…`) or Solana Base58 signatures. |

**Response Example**:

```json
{
  "code": 0,
  "message": "",
  "data": [
    // Each item follows the TransactionData structure, see "Public Data Structures" section
  ]
}
```

**Response Field Description**:

* `code`: Status code, `0` indicates success
* `message`: Status message, empty string `""` when successful
* `data`: Array of transaction records, each item follows the [TransactionData](#transactiondata) structure (see "Public Data Structures" section)

**Notes**:

* Returns `code=0` only if all pairs are successfully queried; if any pair has no record, returns `code=4001`, `message` is `transaction not found`
* Each item in the returned array has the same structure as a single record in the `/v1/transactions` list

## Create Sumsub Access Token By Network

**Endpoint: POST /v1/create-access-token-by-network**

Creates a Sumsub SDK access token for a specified tokenId on a given network. This endpoint is used to initiate KYC verification for users on external (non-Mantle) networks. The partner ID is automatically embedded into the Sumsub external user ID for traceability.

**Prerequisites**:

* The partner must be whitelisted in Nacos (`ur-other-sumsub-allowed-partners`)
* The on-chain `walletProvider(tokenId)` must match the requesting partner ID

**Request Parameters**:

```json
{
  "tokenId": "100000",
  "network": "5000"
}
```

| Field     | Type   | Required | Description                                                  |
| --------- | ------ | -------- | ------------------------------------------------------------ |
| `tokenId` | string | Yes      | UR NFT token ID on the target network                        |
| `network` | string | Yes      | Chain ID (e.g., `"5000"` for Mantle, `"42161"` for Arbitrum) |

**Response Example**:

```json
{
  "code": 0,
  "message": "ok",
  "data": {
    "token": "sbx:aGVsbG8gd29ybGQ..."
  }
}
```

**Response Field Description**:

* `token`: Sumsub SDK access token, used to initialize the Sumsub WebSDK on the client side

**Error Codes**:

| Code    | Description                                                  |
| ------- | ------------------------------------------------------------ |
| `10001` | Partner not authenticated or not authorized                  |
| `20002` | Missing required parameters (`tokenId` or `network`)         |
| `20003` | `tokenId` does not belong to the requesting partner          |
| `50002` | Internal error (Sumsub client not configured or API failure) |

## Query Sumsub Status By Network

**Endpoint: POST /v1/sumsub-status-by-network**

Queries the current Sumsub KYC status for a specified tokenId on a given network. Returns the latest webhook status including review result and rejection details. Partners can only view records they created, or records with no partner association (legacy C-side data).

**Prerequisites**:

* The partner must be whitelisted in Nacos (`ur-other-sumsub-allowed-partners`)

**Request Parameters**:

```json
{
  "tokenId": "100000",
  "network": "5000"
}
```

| Field     | Type   | Required | Description                                                  |
| --------- | ------ | -------- | ------------------------------------------------------------ |
| `tokenId` | string | Yes      | UR NFT token ID on the target network                        |
| `network` | string | Yes      | Chain ID (e.g., `"5000"` for Mantle, `"42161"` for Arbitrum) |

**Response Example**:

```json
{
  "code": 0,
  "message": "ok",
  "data": {
    "type": "applicantWorkflowCompleted",
    "reviewStatus": "completed",
    "reviewAnswer": "GREEN",
    "applicantId": "65a1b2c3d4e5f6a7b8c9d0e1",
    "applicantType": "individual",
    "createdAtMs": 1714377600000
  }
}
```

**Response Field Description**:

* `type`: Sumsub webhook type (e.g., `applicantWorkflowCompleted`, `applicantReviewed`)
* `reviewStatus`: Review status (`init`, `pending`, `completed`, `onHold`)
* `reviewAnswer`: Review result (`GREEN` for approved, `RED` for rejected)
* `reviewRejectType`: Rejection type (only present when rejected, e.g., `FINAL`, `RETRY`)
* `levelName`: Sumsub verification level name
* `rejectLabels`: Array of rejection reason labels (only present when rejected)
* `applicantId`: Sumsub applicant ID
* `applicantType`: Applicant type (e.g., `individual`)
* `createdAtMs`: Record creation timestamp in milliseconds

**Error Codes**:

| Code    | Description                                          |
| ------- | ---------------------------------------------------- |
| `10001` | Partner not authenticated or not authorized          |
| `20002` | Missing required parameters (`tokenId` or `network`) |
| `50002` | Internal error (query failed)                        |

**Notes**:

* If no record exists for the given tokenId + network, returns `code=0` with an empty `data` object
* The query is scoped to the requesting partner's data: records created by other partners are not visible

## Apply USD Payin

**Endpoint: POST /v1/apply-usd-payin**

Initiate a USD Payin application for a specific URId and return an application ID. Once submitted, the operations team will review and approve the request. Upon approval, the user can proceed with the USD Payin operation.

**Prerequisites**:

* Users must have performed at least one EUR or CHF Payin operation before they can submit the application.

**Request Parameters**:

```json
{
  "urId": 100000,
  "remark": "Key User"
}
```

| Field    | Type   | Required | Description        |
| -------- | ------ | -------- | ------------------ |
| `urId`   | int64  | Yes      | UR NFT token ID    |
| `remark` | string | Yes      | Application Remark |

**Response Example**:

```json
{
  "code": 0,
  "message": "ok",
  "data": {
    "applyId": 14563
  }
}
```

**Response Field Description**:

* `applyId`: Unique Application ID

**Error Codes**:


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.ur.app/developer-resources/openapis.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
