> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mobula.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Bridge Status

> [Alpha Preview] Look up the status of a bridge intent, or long-poll until it terminates.

<Warning>
  **Alpha Preview** — Endpoints and response shape may change without notice.
</Warning>

Two endpoints share this page:

* `GET /api/2/bridge/status/{id}` — returns the current row immediately.
* `GET /api/2/bridge/status/{id}/wait` — long-polls server-side until the
  intent reaches a terminal state (or the timeout elapses).

## Path parameter

`id` accepts **any** of these — pass whichever you have:

* `intentId` from `/quote` (format `xxxxxxx-xxxxxxx-xxx`).
* The on-chain `bytes32` intent ID (EVM only, emitted by `MobulaBridge`).
* The deposit TX hash.
* The fill TX hash.

If nothing matches, the response is **not** an error — it's

```json theme={null}
{ "data": { "id": "...", "status": "pending", "message": "Intent not found — deposit may still be processing" } }
```

This is the expected state right after the deposit is broadcast but before
the solver has indexed it. Keep polling.

## Response

```json theme={null}
{
  "data": {
    "intentId": "a3b4bav-e34523c-324",
    "status": "filled",
    "failureReason": null,
    "originChainId": "evm:8453",
    "destinationChainId": "solana:solana",
    "sender": "0x...",
    "recipient": "...",
    "amountIn": "0.05",
    "amountInUsd": 167.42,
    "amountOut": "0.68421052",
    "amountOutUsd": 165.10,
    "depositTxHash": "0x...",
    "fillTxHash": "5xL...",
    "fillTxHashPending": false,
    "settleTxHash": null,
    "latencyMs": 487,
    "timestamps": {
      "depositDetected": "2026-05-23T12:00:01.123Z",
      "fillSent": "2026-05-23T12:00:01.500Z",
      "fillConfirmed": "2026-05-23T12:00:01.610Z",
      "settled": null
    },
    "createdAt": "2026-05-23T12:00:00.000Z"
  }
}
```

`latencyMs` is the deposit-detected → fill-confirmed delta. `settleTxHash` /
`timestamps.settled` populate only once the solver has been reimbursed on
the origin chain — that step is async and can lag the user-visible fill. On a
slippage refund, `failureReason.code` is `"slippage"` and a human-readable
`message` field is added telling the user to raise their slippage.

`fillTxHashPending` is `true` when the fill is **already complete**
(`status: "filled"`) but its canonical destination tx hash hasn't been indexed
yet — the case for **Hyperliquid destinations**, whose L1 hash is assigned a
moment after the funds move. While it's `true`, `fillTxHash` is `null`. Treat the
bridge as done as soon as you see `filled`; fetch the hash separately (see the
[best practice below](#best-practice-don-t-block-completion-on-the-fill-hash-hyperliquid)).

## Status lifecycle

The statuses the solver actually writes:

| Status     | Meaning                                                      | Terminal?            |
| ---------- | ------------------------------------------------------------ | -------------------- |
| `pending`  | Row not yet created, or deposit not yet detected.            | No                   |
| `filling`  | Deposit detected; fill in progress on the destination chain. | No                   |
| `filled`   | Fill confirmed — user has received funds.                    | **Yes** (happy path) |
| `settled`  | Solver reimbursed on origin chain.                           | Yes                  |
| `refunded` | Fill couldn't complete; user refunded on the origin chain.   | Yes                  |
| `failed`   | Refund also failed — manual intervention.                    | Yes                  |

On deposit detection the solver writes `filling` **directly** — there is no
intermediate `deposited` status. (`deposited` and `retrying` exist in the
underlying type enum but are not currently emitted; retries are tracked in a
separate queue and the intent stays in `filling`.) Treat any non-terminal status
as "keep waiting," and any unknown status defensively.

The terminal set is `filled`, `settled`, `failed`, `refunded`. Stop polling once
you see one of them.

## `GET /status/{id}/wait`

Long-poll variant. Blocks server-side until the intent reaches `filled`,
`settled`, `failed`, or `refunded`, then returns the same shape as
`/status/{id}`. If the window elapses first, you get the **current**
(non-terminal) row and should call again.

### Query parameter

| Name                | Notes                                                                                                                                                                                                                                                                                |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `timeout`           | Milliseconds. Default `30000`, capped at `60000`.                                                                                                                                                                                                                                    |
| `waitForFillTxHash` | `true` to hold the long-poll until the canonical `fillTxHash` is present, not just until the intent is terminal. Use it to fetch the real Hyperliquid hash *after* you've already shown the bridge complete. Every other destination returns immediately (its hash is set up front). |

### How it actually waits

The server holds the request open and resolves it the moment your intent
reaches a terminal state (`filled`, `settled`, `failed`, or `refunded`) — the
result is always current, with no client-side polling interval to tune.

In practice, fills are delivered with the latency of the destination chain —
typically a few hundred ms. The 30 s default is just an upper bound; you almost
always return earlier.

### Recommended client loop

```typescript theme={null}
async function waitUntilDone(intentId: string) {
  while (true) {
    const res = await fetch(
      `${API}/api/2/bridge/status/${intentId}/wait?apiKey=YOUR_API_KEY`,
    );
    const { data } = await res.json();

    if (data.status === "filled" || data.status === "settled") return data;
    if (data.status === "failed" || data.status === "refunded") {
      throw new Error(data.message ?? `Bridge ${data.status}`);
    }
    // pending / filling — re-fire immediately
  }
}
```

Pass the key as the `?apiKey=` query param (not an `Authorization` header).
Don't add a client-side sleep — the server already blocks until something
happens; firing again with no delay keeps one open long-poll waiting for the
next state change.

### Stale-response handling

If you start a new bridge while a previous `/wait` is still in flight, the
previous response will arrive **after** your new `intentId` is active. Track
the active intent ID client-side and discard any `/wait` result that doesn't
match it — otherwise you'll attach an old fill TX to the new attempt.

### Best practice: don't block completion on the fill hash (Hyperliquid)

A Hyperliquid-destination fill is **final the instant the funds leave HL**, but
HL only assigns the canonical L1 transaction hash a moment later. The solver
therefore marks the intent `filled` immediately and patches `fillTxHash` in
afterward — so a `filled` response can briefly carry `fillTxHash: null` with
`fillTxHashPending: true`.

Show the user "bridge complete" as soon as `status` is `filled` — **do not wait
on the hash**. Then, only if you want an explorer link, make a second long-poll
with `?waitForFillTxHash=true` and update your UI when the hash lands:

```typescript theme={null}
// 1) Already terminal — show "complete" now (the hash may still be pending).
const final = await waitUntilDone(intentId);

// 2) Optional: fetch the canonical fill hash in the background, then patch the UI.
if (final.status === "filled" && final.fillTxHashPending) {
  const res = await fetch(
    `${API}/api/2/bridge/status/${intentId}/wait?waitForFillTxHash=true&apiKey=YOUR_API_KEY`,
  );
  const { data } = await res.json();
  if (data.fillTxHash) updateExplorerLink(data.fillTxHash); // your UI patch
}
```

Every other destination returns a real `fillTxHash` up front (`fillTxHashPending:
false`), so the second call returns immediately and this branch is a no-op.

## Example

```typescript theme={null}
const API = "https://api.mobula.io";
const KEY = "YOUR_API_KEY"; // pass as ?apiKey=, not an Authorization header

// Instant lookup
const inst = await fetch(`${API}/api/2/bridge/status/${intentId}?apiKey=${KEY}`);
const { data } = await inst.json();
console.log(data.status, data.fillTxHash, data.latencyMs);

// Long-poll until terminal
const wait = await fetch(`${API}/api/2/bridge/status/${intentId}/wait?apiKey=${KEY}`);
const { data: final } = await wait.json();
```

See the [Bridge Implementation guide](/guides/bridge-implementation) for the
full no-sleep `while(true)` polling loop and how to handle stale responses
when running multiple bridges in parallel.


## OpenAPI

````yaml get /2/bridge/status/{id}
openapi: 3.0.0
info:
  version: 1.0.0
  title: Mobula API
  description: >-
    Documentation of the Mobula API


    **Demo API**: The default server (demo-api.mobula.io) is a demo API with
    rate limits.

    For production use, please use api.mobula.io with an API key from
    https://admin.mobula.io
servers:
  - url: https://demo-api.mobula.io/api/
    description: Demo API (rate limited, for testing only)
  - url: https://api.mobula.io/api/
    description: Production API (requires API key)
security: []
tags:
  - name: V2 - Token
    description: Token details, price, security, ATH, and holder data
  - name: V2 - Market Data
    description: Market details, OHLCV history, and lighthouse metrics
  - name: V2 - Trades
    description: Token trades, enriched trades, and trade filters
  - name: V2 - Wallet
    description: Wallet positions, activity, trades, analysis, and labels
  - name: V2 - Assets
    description: Cross-chain asset details and price history
  - name: V2 - Swap
    description: Swap quoting and execution
  - name: V2 - Perps
    description: Perpetual futures quoting, execution, and positions
  - name: V2 - Bridge
    description: Cross-chain bridge quoting and intent status (Alpha Preview)
  - name: V2 - DeFi
    description: Bonding pools and pulse data
  - name: V2 - Search
    description: Universal fast search
  - name: V2 - Blockchains
    description: System metadata and chain listings
  - name: V1 - Market Data
    description: Market prices, history, sparklines, pairs, and multi-data
  - name: V1 - Wallet
    description: Wallet portfolio, transactions, history, and NFTs
  - name: V1 - Token
    description: First buyers
  - name: V1 - Trades
    description: Market trades by pair
  - name: V1 - Metadata
    description: Token metadata, categories, trendings, and news
  - name: V1 - Assets
    description: List all assets
  - name: V1 - Search
    description: Search for assets, tokens, and pairs
  - name: V1 - DeFi
    description: Bonding pool pulse data
  - name: V1 - Blockchains
    description: Blockchain listings, pairs, and stats
  - name: V1 - Webhooks
    description: Webhook management
  - name: V1 - Feed
    description: Custom feed creation
paths:
  /2/bridge/status/{id}:
    get:
      tags:
        - V2 - Bridge
      summary: Get bridge intent status
      description: >-
        [Alpha Preview] Look up a bridge intent's lifecycle status by intent ID
        (bytes32) or by the deposit/fill transaction hash. Status values:
        pending (intent not yet detected), deposited, filling, filled (recipient
        has received funds), settled (solver has withdrawn deposited funds),
        retrying, refunded, failed.
      parameters:
        - schema:
            type: string
            description: Intent ID (0x + 64 hex), deposit TX hash, or fill TX hash.
          required: true
          description: Intent ID (0x + 64 hex), deposit TX hash, or fill TX hash.
          name: id
          in: path
      responses:
        '200':
          description: >-
            Bridge intent status. Returns status="pending" with a `message` when
            the intent has not yet been detected on-chain.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                        description: >-
                          Echoes the requested id when the intent has not yet
                          been detected.
                      intentId:
                        type: string
                        description: On-chain intent ID (bytes32).
                      status:
                        type: string
                        enum:
                          - pending
                          - deposited
                          - filling
                          - filled
                          - settled
                          - retrying
                          - refunded
                          - failed
                      failureReason:
                        type: object
                        nullable: true
                        description: >-
                          Structured failure object on terminal failures (e.g. {
                          code "slippage" }); null otherwise.
                      originChainId:
                        type: string
                      destinationChainId:
                        type: string
                      sender:
                        type: string
                      recipient:
                        type: string
                      amountIn:
                        type: string
                      amountInUsd:
                        type: number
                        nullable: true
                      amountOut:
                        type: string
                        nullable: true
                      amountOutUsd:
                        type: number
                        nullable: true
                      depositTxHash:
                        type: string
                        nullable: true
                      fillTxHash:
                        type: string
                        nullable: true
                      fillTxHashPending:
                        type: boolean
                        description: >-
                          true when the intent is filled but the canonical
                          destination tx hash is not yet indexed (HyperLiquid
                          destinations). While true, fillTxHash is null.
                      settleTxHash:
                        type: string
                        nullable: true
                      latencyMs:
                        type: number
                        nullable: true
                        description: >-
                          End-to-end fill latency in milliseconds
                          (deposit-detected → fill-confirmed).
                      timestamps:
                        type: object
                        properties:
                          depositDetected:
                            type: string
                            nullable: true
                          fillSent:
                            type: string
                            nullable: true
                          fillConfirmed:
                            type: string
                            nullable: true
                          settled:
                            type: string
                            nullable: true
                      createdAt:
                        type: string
                      message:
                        type: string
                        description: >-
                          Present when the intent has not yet been detected, or
                          as a human-readable hint on a slippage refund
                          (failureReason.code = "slippage").
                    required:
                      - status
                required:
                  - data

````