> For the complete documentation index, see [llms.txt](https://docs.ur.app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ur.app/api-reference/cards/crypto-backed-card.md).

# Crypto Backed Card

> This document is the Partner-facing API reference for **Card Mode: Crypto Backed**. It covers the three integration surfaces a Partner must implement on top of their chosen Account Mode: **Prefund Account operations**, the **Card Authorization callback** (UR → Partner, synchronous), and the **Card Mode webhooks** (post-swipe and Prefund balance alerts).
>
> For the conceptual definition of Card Mode and how it relates to Account Mode, see [Integration Guide](https://docs.ur.app/getting-started/integration-guide#card-mode-where-card-spend-draws-funds-from). For the Account Mode API surfaces this mode plugs into, see [API Reference: Managed Custody Mode](/api-reference/account/managed-custody-mode.md) or [API Reference: External Wallet Access Mode](/api-reference/account/external-wallet-access-mode.md).

***

## 1. Mode context

### 1.1 What Card Mode: Crypto Backed is

**Card Mode** answers a single question: when the user taps the card, **which balance is debited**?

* **Card Mode: Fiat Only**: the card draws exclusively from the user's UR fiat balance. UR runs the authorization on-chain; the Partner is **not** in the authorization path.
* **Card Mode: Crypto Backed** *(this document)*: the card can settle directly against the user's crypto holdings via a Partner-funded **Prefund Account**, with no per-swipe off-ramp required. UR synchronously consults the Partner at authorization time; the Partner asynchronously debits the user's crypto after a successful swipe.

Card Mode is **orthogonal to Account Mode**; a Partner can pair Crypto Backed with either External Wallet Access Mode or Managed Custody Mode.

### 1.2 The two phases

Crypto Backed runs in two phases that operate on different timescales:

| Phase             | Initiator  | Cadence                            | What happens                                                                                                                                                                                                                                                                                                                   |
| ----------------- | ---------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **1. Prefund**    | Partner    | Scheduled (recurring, or on alert) | Partner calls the UR off-ramp contract with USDC, targeting its own Prefund Account. The Prefund Account is credited in the Partner's settlement currency.                                                                                                                                                                     |
| **2. Card spend** | User (tap) | Real-time                          | Mastercard routes the auth to UR → UR calls the Partner webhook → Partner returns a `decision` → UR settles against the Prefund Account (`APPROVE`) or the user's UR fiat balance (`PASS`) → after Mastercard finalizes, UR sends a post-swipe webhook to the Partner → on an `APPROVE`, the Partner debits the user's crypto. |

The Prefund Account exists to absorb real-time card authorizations: Partners cannot bridge user crypto fast enough to meet UR's 1 s total authorization window, so UR is pre-funded and the Partner reconciles asynchronously.

### 1.3 What this document covers vs. doesn't

**This document covers** Partner-facing surfaces that exist only in Card Mode: Crypto Backed:

* [§3 Prefund Account](#id-3-prefund-account) funding flow + balance API
* [§4 Card Authorization callback (UR → Partner, synchronous)](#id-4-card-authorization-callback-ur-greater-than-partner)
* [§5 Card Mode webhooks](#id-5-card-mode-webhooks) (post-swipe transaction + Prefund balance alert)
* [§6 Implementation checklist](#id-6-implementation-checklist)

**This document does NOT cover** endpoints that are common to both Card Modes (Create Card, Get Card Info, Set Default Currency, Card history). Those live in the Account Mode API reference under the Card section:

* Managed Custody Partners → [API Reference: Managed Custody Mode §11](/api-reference/account/managed-custody-mode.md#id-11-card)
* External Wallet Access Partners → [API Reference: External Wallet Access Mode](/api-reference/account/external-wallet-access-mode.md#id-3-card)

### 1.4 Partner prerequisites

Before integrating Card Mode: Crypto Backed, the Partner must have:

* An Account Mode chosen and operational (External Wallet Access or Managed Custody).
* **KYB (Know Your Business) completed with UR.** UR requires KYB before it provisions your Prefund Account.
* The ability to make an **initial USDC off-ramp** into the Prefund Account to seed card spending capacity.
* The ability to **schedule recurring off-ramp calls** that top up the Prefund Account on a supported chain.
* The ability to respond to the Card Authorization callback within **500 ms** with valid Partner Auth signatures.
* The ability to **reliably debit user crypto after the swipe is approved**, typically via a smart contract wallet under the Partner's programmatic control, or centralized custody, so the user cannot move the crypto between approval and debit. UR does not enforce this on the Partner's behalf.
* Operational tolerance for **working-float management**; if the Prefund Account drains below the minimum balance, authorizations begin to decline.

***

## 2. API foundation

This document inherits the API foundation defined in the Account Mode reference: base URLs, Partner Auth (EIP-191) headers, response envelope, idempotency rules. See:

* Managed Custody Partners → [API Reference: Managed Custody Mode §2](/api-reference/account/managed-custody-mode.md#id-2-api-foundation)
* External Wallet Access Partners → [API Reference: External Wallet Access Mode](/api-reference/account/external-wallet-access-mode.md#id-1-overview)

Two header-block shorthands are used below:

* **Partner-Scoped Partner Auth Headers**: `X-Api-Signature` + `X-Api-Deadline` + `X-Api-PublicKey`, **no** user identity. Used for Prefund Account endpoints ([§3](#id-3-prefund-account)).
* **UR-Signed Inbound**: for requests where UR calls the Partner ([§4](#id-4-card-authorization-callback-ur-greater-than-partner) callback and [§5](#id-5-card-mode-webhooks) webhooks), UR signs the body with EIP-191 using UR's signer key. The Partner must verify the signature against UR's public key before acting on the payload.

***

## 3. Prefund Account

> **Scope:** Partner-scoped (not user-scoped). One Prefund Account per Partner, regardless of how many users the Partner has onboarded. A single Prefund Account can hold a balance in more than one settlement currency.

### 3.1 What the Prefund Account is

The Prefund Account is a Partner-level operational funding account used to settle card swipes that the Partner approves with `decision: APPROVE`. The Partner funds it; UR debits it during card settlement; the Partner asynchronously debits the user's crypto to replenish its own books.

It is **not**:

* A user balance; users cannot see it, cannot deposit to it, and cannot withdraw from it.
* A real-time authorization gate the Partner queries per swipe. Use it for **operational monitoring only**; UR performs Prefund availability checks inside the Card Authorization flow ([§4](#id-4-card-authorization-callback-ur-greater-than-partner)).

### 3.2 Funding the Prefund Account

You fund the Prefund Account yourself, by calling the UR off-ramp contract with your Prefund Account as the off-ramp target account. UR does not run an operator-mediated deposit step, and there is no separate deposit address to send crypto to.

```mermaid
sequenceDiagram
    participant Partner as Partner Treasury
    participant ORC as UR Off-ramp Contract
    participant Pre as Prefund Account<br/>(Partner-scoped)

    Note over Partner, Pre: Scheduled top-up (recurring, or triggered by a balance alert)
    Partner->>ORC: 1. Call off-ramp with USDC, target = your Prefund Account
    ORC-->>Pre: 2. Credit the Prefund Account in your settlement currency
```

**Currently supported:**

* **Off-ramp USDC on supported chains:** call the off-ramp contract with USDC on any UR [Supported Chain](https://docs.ur.app/api-reference/cards/pages/0KrXzznVQkBgtZweDaXH#id-3.1.9-get-supported-chain-config). USDC is the only deposit currency today.
* **Settlement currency:** the currency your Prefund Account is held in, on Mantle, as a tokenized fiat balance. It defaults to `USD`. To use a different settlement currency, agree it with UR **before** onboarding; the settlement currency you pick affects your off-ramp pricing.
* **Minimum balance:** agreed with UR during onboarding, **per settlement currency**. UR emits `prefund.balance.alert` ([§5.5](#id-5.5-prefund-balance-alert-event-prefund.balance.alert)) when a currency's balance reaches or falls below its threshold.

Operational constraints:

* **Off-ramp to your own Prefund Account, never to a user account.** Confirm your Prefund Account details with UR before your first production top-up, and use them as the off-ramp target account.
* **Do not assume an off-ramp is instantly reflected.** The off-ramp settles asynchronously; monitor the Prefund Balance API ([§3.3](#id-3.3-get-prefund-balance)) and the `prefund.balance.alert` webhook ([§5.5](#id-5.5-prefund-balance-alert-event-prefund.balance.alert)) rather than assuming the credit has landed.
* **Maintain a buffer above the minimum balance.** Each `prefund.balance.alert` indicates the Partner is now exposed to authorization decline risk on the next swipe. Plan top-up SLA accordingly.

### 3.3 Get Prefund balance

| Item    | Value                               |
| ------- | ----------------------------------- |
| Method  | `GET`                               |
| Path    | `/api/fma/v1/prefund-balance`       |
| Headers | Partner-Scoped Partner Auth Headers |

This endpoint is **partner-scoped**, not user-scoped. It returns the current state of the Partner's Prefund Account.

Request body: none. Query parameters: none.

Response example:

The response is a **flat, single-currency** object describing the Prefund Account's settlement currency (defaults to `USD`):

```json
{
  "code": 0,
  "message": "",
  "data": {
    "partnerId": "partner_id",
    "account": "0xPrefundAccAddress",
    "currency": "USD",
    "balance": "125000.00",
    "minBalance": "50000.00",
    "level": "normal",
    "allowance": "125000.00",
    "updatedAt": 1713700000
  }
}
```

Response fields:

| Field        | Description                                                                                                                                   |
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `partnerId`  | Partner identifier registered with UR.                                                                                                        |
| `account`    | On-chain address of the Prefund Account (Mantle).                                                                                             |
| `currency`   | The settlement currency, agreed with UR (defaults to `USD`, see [§3.2](#id-3.2-funding-the-prefund-account)).                                 |
| `balance`    | Current balance, decimal string.                                                                                                              |
| `minBalance` | Minimum balance threshold. `prefund.balance.alert` fires when the balance reaches or falls below it.                                          |
| `level`      | Balance level relative to `minBalance`. This endpoint emits `"normal"` (balance > `minBalance`) or `"warning"` (balance ≤ `minBalance`) only. |
| `allowance`  | The card-spend contract allowance for this account, as a human-readable amount.                                                               |
| `updatedAt`  | Unix seconds, last balance update.                                                                                                            |

> This endpoint returns a single currency and does **not** include a `settleMode` field. Record your configured settlement mode from onboarding; it is not read back from this response.

Usage recommendations:

* **Polling cadence**: operational dashboard cadence, e.g. no more frequently than once per minute unless UR advises otherwise.
* **Authoritative source**: treat this endpoint as the source of truth for `balance` and `minBalance`. Webhook payloads are eventually consistent.
* **Reactive pattern**: use `prefund.balance.alert` ([§5.5](#id-5.5-prefund-balance-alert-event-prefund.balance.alert)) as a passive trigger; call this endpoint to confirm current state before initiating a top-up.
* **Not for authorization decisions**: do not call this endpoint from inside the Card Authorization callback ([§4](#id-4-card-authorization-callback-ur-greater-than-partner)). UR already performs Prefund availability checks before approving. Adding a Partner-side check increases your 500 ms callback latency for no benefit.

***

## 4. Card authorization callback (UR → Partner)

> **Scope:** Required only for Card Mode: Crypto Backed. Card Mode: Fiat Only Partners are not in the authorization path at all.

When the user taps the card, UR **synchronously** calls a Partner-hosted authorization endpoint. The Partner returns an approval decision and the currency to settle in; UR then performs final balance, limit, compliance, and Prefund checks before responding to Mastercard.

### 4.1 Settlement modes

Partners configure one of two settlement modes during onboarding. The mode determines what UR sends in the authorization request and what the Partner is expected to return.

| Mode                   | `settleMode`         | Description                                                                                                                                                                                                                                                                                                                                              |
| ---------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Fixed**              | `fixed` (default)    | UR pre-computes the exact amount it will debit from the Prefund Account, in the Partner's configured settlement currency, including FX spread and interchange. UR sends that amount in the `settlement` field of the authorization request. The Partner approves or declines against that pre-calculated amount, and does not choose the debit currency. |
| **Partner Controlled** | `partner_controlled` | The Partner chooses which Prefund settlement currency to debit on each swipe. UR sends the raw Mastercard transaction and settlement details, and the Partner returns the chosen currency in `paidCurrency`. Because UR does not know the currency in advance, it cannot pre-compute the debit amount; the Partner sizes it.                             |

**Fixed mode** is designed for Partners who hold a single-currency Prefund Account and want UR to handle all FX conversion. The Partner does not need to perform any currency conversion; the `settlement.amount` in the request is the exact amount that will be debited from the Prefund Account.

### 4.2 Endpoint contract

The Partner hosts an endpoint at a URL registered with UR during onboarding (e.g. `https://api.partner.example/ur/card-authorizations`). UR calls it on every card authorization for users whose accounts are configured with Card Mode: Crypto Backed.

| Item           | Value                                                                                                                                                         |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Method         | `POST`                                                                                                                                                        |
| Path           | Partner-hosted (registered with UR during onboarding)                                                                                                         |
| Direction      | **UR → Partner**                                                                                                                                              |
| Auth           | Mutual EIP-191: UR signs the request body; Partner signs the response body. Both parties verify the other's signature against pinned addresses.               |
| Latency budget | **≤ 500 ms** Partner response time. The Partner's 500 ms sits inside UR's 1 s total authorization window; UR uses the remaining \~500 ms for internal checks. |
| Failure mode   | Timeout / 5xx / signature-verification failure / invalid response → UR treats as **DECLINE** for this swipe.                                                  |

The Partner's authorization endpoint should be **low-latency, idempotent, auditable, and fail-safe**. Treat the 500 ms budget as a hard ceiling; design for p99 well under it.

### 4.3 Flow

```mermaid
sequenceDiagram
    participant MC as Mastercard Network
    participant UR as UR
    participant Partner as Partner Backend
    participant Pool as Prefund Account
    participant UserBal as User UR Fiat Balance

    MC->>UR: 1. Authorization request (user taps card)
    Note over UR: 2. Build signed authorization envelope
    UR->>Partner: 3. POST signed { event, data: { requestId, action, transaction, settlement, merchant, ... } }
    Note over Partner: 4. Verify UR signature, evaluate authorization
    Partner-->>UR: 5. Signed { requestId, decisionId, decision, paidCurrency }
    Note over UR: 6. Verify Partner signature, apply decision
    UR->>UR: 7. Final checks: balance, limit, compliance, Prefund availability
    alt decision = APPROVE AND UR checks pass
      UR->>Pool: 8a. Book against Prefund Account in paidCurrency
      UR-->>MC: 9. APPROVED (within 1 s)
    else any decline
      UR-->>MC: 9. DECLINED
    end
    Note over UR, Partner: Result follow-up (§5.4)
    UR->>Partner: 10. transaction_v2 webhook (MARQETA_AUTHORIZE)
    opt Prefund Account was used and approved
      Partner->>Partner: 11. Debit equivalent crypto from user
    end
```

### 4.4 Request signing (UR → Partner)

UR signs every authorization request body with EIP-191 using UR's server private key. The signature and signer address are sent in HTTP headers:

| Header            | Description                                                                                                  |
| ----------------- | ------------------------------------------------------------------------------------------------------------ |
| `X-Api-Signature` | EIP-191 signature over the raw JSON request body (`0x`-prefixed hex, 65 bytes).                              |
| `X-Api-PublicKey` | UR's signer Ethereum address. The Partner **must** verify this matches the address pinned during onboarding. |
| `X-Request-Id`    | Matches `data.requestId` in the body. Convenience header for logging.                                        |

Verification: recover the signer address from `EIP191(body)` using `X-Api-Signature`, then assert it equals the pinned UR public key. Reject if it doesn't match.

### 4.5 Request body (UR → Partner)

```json
{
  "event": "card_authorization",
  "timestamp": 1713700000,
  "data": {
    "version": "1",
    "requestId": "auth-req-uuid-001",
    "expiresAt": 1713700500,
    "action": "AUTHORIZE",
    "partnerId": "partner_example",
    "userId": "7123456789",
    "authorizationToken": "marqeta-auth-token-001",
    "transaction": {
      "currency": "SGD",
      "amount": "10000"
    },
    "settlement": {
      "currency": "USD",
      "amount": "7450"
    },
    "merchant": {
      "id": "merchant-123",
      "name": "Coffee Shop",
      "mcc": "5812",
      "city": "Singapore",
      "country": "SG"
    }
  }
}
```

Field reference:

| Field                       | Type   | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| --------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `event`                     | string | Always `"card_authorization"`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `timestamp`                 | number | Unix seconds when the request was built.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `data.version`              | string | Protocol version. Currently `"1"`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `data.requestId`            | string | UR-generated idempotency key. The Partner **must** echo this back in the response and respond identically to retries with the same `requestId`.                                                                                                                                                                                                                                                                                                                                                                                                       |
| `data.expiresAt`            | number | Unix milliseconds. The Partner should reject requests where `expiresAt < now`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `data.action`               | string | `"AUTHORIZE"` for initial authorization, `"INCREMENT"` for an incremental auth on the same transaction.                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `data.partnerId`            | string | Partner identifier.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `data.userId`               | string | User's UR ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `data.authorizationToken`   | string | Card network authorization token.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `data.transaction.currency` | string | Merchant's transaction currency (ISO 4217 alpha code, e.g. `"SGD"`).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `data.transaction.amount`   | string | Transaction amount in the transaction currency (integer string in the token's smallest unit).                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `data.settlement.currency`  | string | Settlement currency symbol (e.g. `"USD"`, `"EUR"`). In **fixed mode**, this is always the Partner's configured `settleCurrency`. In **partner\_controlled mode**, this is the Mastercard network settlement currency.                                                                                                                                                                                                                                                                                                                                 |
| `data.settlement.amount`    | string | Settlement amount. **Its meaning depends on your `settleMode`; read it accordingly.** In **fixed mode**, this is the **pre-computed Prefund debit amount**, including FX spread and interchange. It is the exact amount UR debits from your Prefund Account if you approve, so you can check it against your balance directly. In **partner\_controlled mode**, this is the **Mastercard network settlement amount**. It is **not** your Prefund debit; UR applies FX and interchange at execution, so convert it yourself before you size the debit. |
| `data.merchant.*`           | object | Merchant details from the card network.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |

### 4.6 Response body (Partner → UR)

The Partner **must** sign the response body with EIP-191 and include signature headers:

| Header            | Description                                                                                                 |
| ----------------- | ----------------------------------------------------------------------------------------------------------- |
| `X-Api-Signature` | EIP-191 signature over `body + " " + deadline` (the response body bytes, a space, and the deadline string). |
| `X-Api-Deadline`  | Unix seconds expiry for the response signature (e.g. 60 s from now).                                        |
| `X-Api-PublicKey` | Partner's signer Ethereum address.                                                                          |

**Approval:**

```json
{
  "requestId": "auth-req-uuid-001",
  "decisionId": "partner-decision-uuid-001",
  "decision": "APPROVE",
  "paidCurrency": "USD"
}
```

**Decline:**

```json
{
  "requestId": "auth-req-uuid-001",
  "decisionId": "partner-decision-uuid-002",
  "decision": "DECLINE",
  "paidCurrency": ""
}
```

**Pass (fall back to user's own balance):**

```json
{
  "requestId": "auth-req-uuid-001",
  "decisionId": "partner-decision-uuid-003",
  "decision": "PASS",
  "paidCurrency": ""
}
```

Response field reference:

| Field          | Required   | Description                                                                                                                                                                                                                                                                                                                                                                                                                 |
| -------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `requestId`    | Yes        | Must match `data.requestId` from the request. UR rejects mismatches.                                                                                                                                                                                                                                                                                                                                                        |
| `decisionId`   | Yes        | Partner-generated unique ID for this decision. Used for audit trail and reconciliation.                                                                                                                                                                                                                                                                                                                                     |
| `decision`     | Yes        | `"APPROVE"`, `"DECLINE"`, or `"PASS"`. Any other value is treated as an error (UR declines).                                                                                                                                                                                                                                                                                                                                |
| `paidCurrency` | On APPROVE | The currency to debit from the funding source that `decision` selected (ISO symbol, e.g. `"USD"`, `"EUR"`). On `APPROVE`, it names a currency in your **Prefund Account**. On `PASS`, it names a currency in the **user's UR fiat balance**; leave it empty to let UR select the currency for you. Not used on `DECLINE`. See the mode tables in [§4.6 examples](#examples-by-settlement-mode) for every valid combination. |

#### Decision semantics

| `decision` | Effect                                                                                                                                                                                                                   |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `APPROVE`  | Settle against the **Prefund Account** in `paidCurrency`. UR still performs final checks and may override to DECLINE.                                                                                                    |
| `DECLINE`  | Reject the authorization entirely. The card swipe is declined.                                                                                                                                                           |
| `PASS`     | Skip double booking for this swipe. UR falls back to the **user's own UR fiat balance**; the swipe behaves as if the user were on Card Mode: Fiat Only. No Prefund debit occurs; no Partner-side crypto debit is needed. |

`PASS` is useful when the Partner wants to selectively route certain transactions to the user's own balance; for example, small transactions below a threshold, or when the user has sufficient fiat balance and the Partner prefers not to consume Prefund capacity.

Important behavior:

* **UR retains final authority**: even after an `APPROVE`, UR performs balance / limit / compliance / Prefund availability checks and can still decline.
* **Increment consistency**: for `action: INCREMENT`, the Partner must return the same `decision` and `paidCurrency` as the original `AUTHORIZE` for the same transaction. UR rejects currency changes mid-transaction. If the original was `PASS`, the increment must also be `PASS`.
* **Fixed mode simplification**: in fixed mode, the `settlement.amount` in the request already includes FX fees. The Partner only needs to check if the Prefund Account can absorb that amount and approve or decline. `PASS` is also available in fixed mode if the Partner wants to route specific transactions to the user's balance.

#### Examples by settlement mode

Your `settleMode` changes only what you may return on `APPROVE`. `PASS` and `DECLINE` behave identically in both modes. The following tables list every valid combination.

**Fixed mode.** UR pre-computes the debit, so you never run a currency conversion:

| `decision` | `paidCurrency`                                  | What UR does                                                                                                  |
| ---------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `APPROVE`  | Equal to `settlement.currency` from the request | Debits `settlement.amount` from your Prefund Account. That amount already includes FX spread and interchange. |
| `APPROVE`  | Any other currency                              | Declines the swipe. In fixed mode you cannot choose the debit currency.                                       |
| `APPROVE`  | Empty                                           | Declines the swipe. `paidCurrency` is required on `APPROVE`.                                                  |
| `PASS`     | Empty                                           | Settles from the user's UR fiat balance and selects the currency for you. Your Prefund Account is untouched.  |
| `PASS`     | A currency the user holds                       | Settles from that currency of the user's UR fiat balance. Declines if that balance is short.                  |
| `DECLINE`  | Empty                                           | Declines the swipe.                                                                                           |

**Partner controlled mode.** You choose the Prefund currency, so UR cannot pre-compute the debit:

| `decision` | `paidCurrency`                                | What UR does                                                                                                 |
| ---------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `APPROVE`  | Any currency your Prefund Account holds       | Debits that currency from your Prefund Account. UR applies FX spread and interchange at execution.           |
| `APPROVE`  | A currency your Prefund Account does not hold | Declines the swipe.                                                                                          |
| `APPROVE`  | Empty                                         | Declines the swipe. `paidCurrency` is required on `APPROVE`.                                                 |
| `PASS`     | Empty                                         | Settles from the user's UR fiat balance and selects the currency for you. Your Prefund Account is untouched. |
| `PASS`     | A currency the user holds                     | Settles from that currency of the user's UR fiat balance. Declines if that balance is short.                 |
| `DECLINE`  | Empty                                         | Declines the swipe.                                                                                          |

### 4.7 Fail-safe semantics

UR treats the following as **DECLINE** (the Partner's authorization endpoint is considered unavailable):

| Failure                                                               | UR action                                                                             |
| --------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| HTTP timeout (no response within 500 ms)                              | Decline with internal reason `partner_timeout`                                        |
| HTTP 5xx                                                              | Decline with internal reason `partner_5xx`                                            |
| HTTP 4xx                                                              | Decline with internal reason `partner_4xx` (likely Partner config error; investigate) |
| Invalid or missing signature on response                              | Decline with internal reason `partner_sig_invalid`                                    |
| `requestId` mismatch                                                  | Decline with internal reason `partner_invalid_response`                               |
| Missing `decisionId`                                                  | Decline with internal reason `partner_invalid_response`                               |
| Invalid `decision` value                                              | Decline with internal reason `partner_invalid_response`                               |
| `decision: APPROVE` but `paidCurrency` missing                        | Decline with internal reason `partner_invalid_response`                               |
| Unsupported `paidCurrency`                                            | Decline with internal reason `partner_invalid_response`                               |
| Response body exceeds 64 KB                                           | Decline with internal reason `partner_invalid_response`                               |
| Partner signature deadline expired or too far in the future (> 5 min) | Decline with internal reason `partner_sig_invalid`                                    |

UR exposes the internal decline reason on the post-swipe transaction webhook ([§5.4](#id-5.4-transaction-event-post-swipe-event-transaction-data.type-crd)) for reconciliation.

**Operational note on the authorization URL.** The Partner-hosted authorization URL is registered with UR during onboarding. To update it (e.g. environment migration), coordinate the change with UR; there is no self-serve endpoint for this.

### 4.8 Recommended `reason` codes

> For Partners who include a `reason` field in their response (optional, for reconciliation/analytics).

| `reason`                       | When to use                                           |
| ------------------------------ | ----------------------------------------------------- |
| `ok`                           | Approve.                                              |
| `insufficient_user_crypto`     | User does not have enough crypto on the Partner side. |
| `insufficient_partner_prefund` | Partner's Prefund Account is too low.                 |
| `user_blocked`                 | Partner-side compliance block.                        |
| `merchant_blocked`             | Partner-side MCC / merchant block.                    |
| `card_disabled`                | Card is locked or frozen on the Partner side.         |
| `internal_error`               | Catch-all decline; treat as transient.                |

### 4.9 Fixed mode: FX quote details

In **fixed mode**, UR pre-calculates the `settlement.amount` using on-chain FX rates from the card authorization contract. The calculation mirrors the contract's actual debit logic to ensure the quoted amount matches the on-chain settlement:

* **Same-currency** (e.g. Prefund is USD, settlement is USD): `settlement.amount` = network settlement amount, no FX.
* **Cross-currency** (e.g. Prefund is EUR, settlement is USD): UR calls the contract's `getRate` and `getSpread` view functions to compute the exact amount including FX spread and interchange fees.

The quoted amount is deterministic at the time of the authorization request. Between the quote and the on-chain settlement (typically < 1 second), exchange rates may shift marginally. This is operationally acceptable for the card authorization use case.

Partners in fixed mode do **not** need to perform any FX calculation; the `settlement.amount` is ready to use for balance checks and ledger entries.

***

## 5. Card Mode webhooks

UR delivers Card Mode events via the standard webhook envelope. The Partner must verify every webhook signature before acting on it.

### 5.1 Webhook envelope

```json
{
  "event": "<event-type>",
  "data": {},
  "timestamp": 1704234567
}
```

### 5.2 Signature verification

UR signs webhook bodies with EIP-191. Verification steps:

1. Read the **exact raw request body string** (do not re-serialize).
2. Recover the signer address using the body and `X-Api-Signature`.
3. Accept the event only if the signer address matches the **UR public key** provided out of band.

See [Signature and Verification](/api-reference/signature-and-verify.md) for the canonical recovery algorithm.

### 5.3 Retry and idempotency

* The Partner should return `HTTP 200` within **10 seconds**.
* UR retries non-200 / timed-out webhook deliveries **up to 3 times**, with a **5-minute interval**.
* Use `data.id` as the idempotency key for card payment `transaction_v2` events.

### 5.4 Card payment transaction (`event: "transaction_v2"`)

UR sends `transaction_v2` when a card payment transaction reaches a terminal state. Card payment records use `data.type = "MARQETA_AUTHORIZE"`. The same event covers successful and failed card payments.

Confirmed payment example:

```json
{
  "event": "transaction_v2",
  "data": {
    "id": 353244,
    "txHash": "0x30e3...",
    "blockNumber": 39376686,
    "createTimeE9": 1780298041358000000,
    "broadcastTimeE9": 1780298041358000000,
    "finalTimeE9": 1780298045000000000,
    "type": "MARQETA_AUTHORIZE",
    "chainId": "eip155:5003",
    "chainName": "MNT",
    "urId": "6570621322",
    "direction": "OUT",
    "amount": "0.06",
    "currency": "usd",
    "status": "CONFIRMED",
    "detailsJson": "{\"authorizationId\":\"card-auth-0001\",\"balance\":\"12360572.18\",\"cardCurrency\":\"USD\",\"cardId\":\"card-id-0001\",\"city\":\"Singapore\",\"country\":\"SGP\",\"mcc\":\"7299\",\"merchant\":\"Merchant ABC\",\"merchantId\":\"merchant-0001\",\"settlementAmount\":\"0.06\",\"settlementCurrency\":\"EUR\",\"transactionAmount\":\"0.06\",\"transactionCurrency\":\"USD\"}"
  },
  "timestamp": 1780298051
}
```

Failed payment example:

```json
{
  "event": "transaction_v2",
  "data": {
    "id": 353245,
    "txHash": "0x9eec...",
    "blockNumber": 96571332,
    "createTimeE9": 1781272976000000000,
    "broadcastTimeE9": 1781272976000000000,
    "finalTimeE9": 1781272976000000000,
    "type": "MARQETA_AUTHORIZE",
    "chainId": "eip155:5000",
    "chainName": "MNT",
    "urId": "4596332147",
    "direction": "OUT",
    "amount": "15.39",
    "currency": "cnh",
    "status": "FAILED",
    "detailsJson": "{\"authorizationId\":\"card-auth-0002\",\"merchantId\":\"merchant-0002\",\"merchant\":\"Merchant ABC\",\"mcc\":\"5999\",\"city\":\"Shanghai\",\"country\":\"CHN\",\"cardId\":\"card-id-0002\",\"cardCurrency\":\"USD\",\"transactionCurrency\":\"CNH\",\"settlementCurrency\":\"EUR\",\"transactionAmount\":\"15.39\",\"settlementAmount\":\"1.97\",\"reason\":\"Insufficient USD24 allowance\"}"
  },
  "timestamp": 1781272979
}
```

Key fields for Crypto Backed reconciliation:

| Field                 | Description                                                                                    |
| --------------------- | ---------------------------------------------------------------------------------------------- |
| `id`                  | UR transaction record ID. Use this field as the idempotency key for `transaction_v2` delivery. |
| `type`                | Card payment transactions use `MARQETA_AUTHORIZE`.                                             |
| `status`              | Terminal transaction status, such as `CONFIRMED` or `FAILED`.                                  |
| `direction`           | `OUT` for card spend.                                                                          |
| `amount` / `currency` | Amount and currency recorded on the transaction row.                                           |
| `txHash`              | On-chain transaction hash when available.                                                      |
| `detailsJson`         | Stringified JSON with card payment details. Parse this value before reading nested fields.     |

`detailsJson` may include the following card payment fields:

| Field                 | Description                                                                             |
| --------------------- | --------------------------------------------------------------------------------------- |
| `authorizationId`     | Card authorization identifier.                                                          |
| `balance`             | Balance value recorded with the card payment.                                           |
| `cardCurrency`        | Card currency.                                                                          |
| `cardId`              | Card identifier.                                                                        |
| `city`                | Merchant city.                                                                          |
| `country`             | Merchant country.                                                                       |
| `mcc`                 | Merchant category code.                                                                 |
| `merchant`            | Merchant display name.                                                                  |
| `merchantId`          | Merchant identifier.                                                                    |
| `settlementAmount`    | Settlement amount recorded for the card payment.                                        |
| `settlementCurrency`  | Settlement currency recorded for the card payment.                                      |
| `transactionAmount`   | Merchant transaction amount.                                                            |
| `transactionCurrency` | Merchant transaction currency.                                                          |
| `reason`              | Failure reason. This field appears only when the card payment record includes a reason. |

**Partner crypto-debit responsibility.** When a Crypto Backed card payment succeeds, the partner is responsible for computing and debiting the corresponding user crypto amount in its own system. UR reports the card payment transaction data; UR does not move user crypto on the partner's behalf.

Use `transaction_v2` records with `data.type = "MARQETA_AUTHORIZE"` and `data.status = "FAILED"` for failed card payments. The failure reason is in `detailsJson.reason` when UR has one.

### 5.5 Prefund balance alert (`event: "prefund.balance.alert"`)

UR emits `prefund.balance.alert` when the balance of one settlement currency in your Prefund Account crosses a threshold. Use it as a passive trigger to top up before authorizations start to decline.

The alert is **per currency**, not per account. If two currencies cross a threshold, UR sends two events.

Example:

```json
{
  "event": "prefund.balance.alert",
  "data": {
    "partnerId": "partner_id",
    "account": "0xPrefundAccAddress",
    "currency": "EUR",
    "balance": "18000.00",
    "minBalance": "20000.00",
    "previousLevel": "normal",
    "level": "warning",
    "updatedAt": 1713700000
  },
  "timestamp": 1713700005
}
```

Fields:

| Field           | Description                                                            |
| --------------- | ---------------------------------------------------------------------- |
| `partnerId`     | Partner identifier registered with UR.                                 |
| `account`       | On-chain address of the Prefund Account (Mantle).                      |
| `currency`      | The settlement currency this alert is about.                           |
| `balance`       | Balance in that currency at the moment the level changed.              |
| `minBalance`    | The threshold configured for that currency.                            |
| `previousLevel` | The level before this change.                                          |
| `level`         | The level after this change: `"normal"`, `"warning"`, or `"critical"`. |
| `updatedAt`     | Unix seconds, the balance update that triggered the alert.             |

**When UR sends it.** The alert is edge-triggered: UR sends it only when `level` **changes** for a currency, not repeatedly while the balance sits below the threshold. You therefore receive an alert on the way down (`normal` to `warning`, `warning` to `critical`) and again on recovery (back to `normal`), and nothing in between.

**How to handle it:**

* Treat the alert as a **trigger, not as truth**. Call [Get Prefund balance](#id-3.3-get-prefund-balance) to confirm the current state before you top up; webhook payloads are eventually consistent.
* Top up **that currency**. A `warning` on one currency says nothing about the others.
* Idempotency key: `account` + `currency` + `updatedAt`. Retry and delivery semantics follow [§5.3](#id-5.3-retry-and-idempotency).
* A `critical` level means the next swipe in that currency is at real risk of declining. Treat it as a page, not a ticket.

***

## 6. Implementation checklist

Before going live with Card Mode: Crypto Backed:

**Prefund operations**

* Confirm the off-ramp chain, the deposit currency, your settlement currency, and the minimum balance (all agreed during onboarding).
* Confirm your Prefund Account details with UR, and use them as the off-ramp target account.
* Implement recurring off-ramp automation with an SLA aligned to your minimum-balance buffer.
* Build an operational dashboard that polls `GET /prefund-balance` and surfaces `level` transitions.
* Subscribe to `prefund.balance.alert` ([§5.5](#id-5.5-prefund-balance-alert-event-prefund.balance.alert)) and route it to your ops channel. Alerts are per currency.

**Card Authorization callback**

* Host the authorization endpoint at the URL registered with UR (see the operational note in [§4.7](#id-4.7-fail-safe-semantics)).
* Verify every inbound request's EIP-191 signature against UR's public key before doing any business logic.
* Implement the handler with a **p99 < 500 ms** budget. Pre-compute everything you can; avoid cross-region database calls in the hot path.
* Decide `decision` deterministically. Return `APPROVE` to settle from your Prefund Account, `PASS` to fall back to the user's own UR fiat balance, or `DECLINE` to reject the swipe. See [Examples by settlement mode](#examples-by-settlement-mode) for every valid `decision` and `paidCurrency` combination.
* Make the handler **idempotent on `requestId`**. UR may retry on transient network errors, and you must return the same decision for the same `requestId`.
* Build a fail-safe path: if any internal dependency fails, return `decision: "DECLINE"` with a `reason` rather than a 5xx. UR treats a 5xx as a decline anyway, but an explicit decline preserves the `reason` for reconciliation.

**Post-swipe + reconciliation**

* Subscribe to the `transaction_v2` webhook (§5.4) and verify its signature.
* When you returned `APPROVE` and the swipe settled against your Prefund Account, perform the user-side crypto debit once the `transaction_v2` webhook reports `status: CONFIRMED`. Record it against the same `requestId`.
* Handle the override case: you returned `decision: APPROVE`, but UR still declined the swipe. Clean up any speculative Partner-side state.
* Decide your refund policy and document it for end users.

**Operational**

* Treat webhook delivery as **at-least-once** and implement idempotency on `data.id` (transactions) and `account + currency + updatedAt` (Prefund alerts).
* Maintain monitoring on Partner-side authorization handler latency, success rate, and decline reason distribution.

***

## 7. Reference docs

* [Integration Guide](https://docs.ur.app/getting-started/integration-guide): Account Mode × Card Mode framing.
* [API Reference: Managed Custody Mode](/api-reference/account/managed-custody-mode.md): common card endpoints (Create Card, Get Card Info, Set Default Currency, history) and the Account Mode this plugs into.
* [API Reference: External Wallet Access Mode](/api-reference/account/external-wallet-access-mode.md): the alternative Account Mode this can pair with.
* [Signature and Verification](/api-reference/signature-and-verify.md): EIP-191 signing and verification for Partner Auth and webhook signatures.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://docs.ur.app/api-reference/cards/crypto-backed-card.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
