# REST API Reference

All public endpoints live under the common base URL:

| Environment | Base URL                                                                                         |
| ----------- | ------------------------------------------------------------------------------------------------ |
| **Mainnet** | [`https://bridge.goliath.net/api/v1`](https://bridge.goliath.net/api/v1)                         |
| **Testnet** | Contact support for the current testnet base URL — it may change as testnet environments evolve. |

All responses are `application/json`. All timestamps are ISO-8601 UTC. Amounts are **atomic units** (stringified big integers) with the token's decimals applied — `amountFormatted` fields give the human-readable equivalent for convenience.

## Authentication

None. All endpoints documented on this page are public. Rate limits apply per IP.

{% hint style="info" %}
Admin endpoints under `/api/v1/admin/*` exist for operational use by the Goliath team and are **not public** — they are omitted from this reference. Do not build against them.
{% endhint %}

## Error Envelope

Failing requests return:

```json
{
  "error": "ERROR_CODE",
  "message": "Human-readable description"
}
```

Common codes:

| HTTP | Code                                                                     | Meaning                                                                                                                                                                                                                                    |
| ---- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 400  | `VALIDATION_ERROR`                                                       | Malformed or missing parameter. `message` names the offending field.                                                                                                                                                                       |
| 400  | `BELOW_MINIMUM`                                                          | Amount below the per-token minimum.                                                                                                                                                                                                        |
| 400  | `DEADLINE_EXPIRED`                                                       | Intent `deadline` already in the past.                                                                                                                                                                                                     |
| 400  | `SIGNATURE_INVALID` / `SIGNATURE_MISMATCH` / `SIGNATURE_DOMAIN_REJECTED` | EIP-712 signature rejected.                                                                                                                                                                                                                |
| 403  | `SENDER_MISMATCH`                                                        | `senderAddress` does not match the original signer.                                                                                                                                                                                        |
| 404  | `OPERATION_NOT_FOUND` / `INTENT_NOT_FOUND`                               | No record for the given identifier (yet).                                                                                                                                                                                                  |
| 409  | `INTENT_ALREADY_BOUND` / `INTENT_NOT_PENDING` / `DUPLICATE_ORIGIN_TX`    | Conflict with existing state.                                                                                                                                                                                                              |
| 410  | `INTENT_EXPIRED`                                                         | Signed intent expired.                                                                                                                                                                                                                     |
| 429  | `RATE_LIMIT_EXCEEDED`                                                    | Too many requests.                                                                                                                                                                                                                         |
| 503  | `SIGNATURE_VERIFICATION_UNAVAILABLE`                                     | ERC-1271 signature verification is transiently unavailable, or the smart-account `senderAddress` is not yet deployed on Goliath. Retry after 1–5 s. See [Smart Account Integration](/developer-guide/bridge/smart-account-integration.md). |
| 503  | `SERVICE_PAUSED`                                                         | New XCN intents temporarily disabled.                                                                                                                                                                                                      |

***

## `GET /bridge/status`

Look up a single bridge operation.

### Query parameters

At least one of:

| Parameter      | Type     | Notes                                                     |
| -------------- | -------- | --------------------------------------------------------- |
| `originTxHash` | `string` | Origin-chain transaction hash (`0x` + 64 hex).            |
| `depositId`    | `string` | `bytes32` from the `Deposit` event (Ethereum → Goliath).  |
| `withdrawId`   | `string` | `bytes32` from the `Withdraw` event (Goliath → Ethereum). |

### Response 200 — `BridgeOperationResponse`

```json
{
  "operationId": "c59f9a66-9b22-4b43-9e2a-0b3f6c...",
  "direction": "ETHEREUM_TO_GOLIATH",
  "status": "COMPLETED",
  "token": "USDC",
  "amount": "100000000",
  "amountFormatted": "100.0",
  "fee": {
    "amount": "5000000",
    "formatted": "5.0",
    "bps": 25
  },
  "outputAmount": "95000000",
  "outputAmountFormatted": "95.0",
  "sender": "0xabc...",
  "recipient": "0xabc...",
  "originChainId": 1,
  "destinationChainId": 327,
  "originTxHash": "0x...",
  "destinationTxHash": "0x...",
  "originConfirmations": 12,
  "requiredConfirmations": 12,
  "timestamps": {
    "depositedAt": "2026-04-19T10:00:00.000Z",
    "finalizedAt": "2026-04-19T10:02:24.000Z",
    "destinationSubmittedAt": "2026-04-19T10:02:28.000Z",
    "completedAt": "2026-04-19T10:02:40.000Z"
  },
  "estimatedCompletionTime": null,
  "holdUntil": null,
  "error": null,
  "isSameWallet": true,
  "settlement": {
    "chainId": 327,
    "chainName": "goliath",
    "tokenSymbol": "USDC",
    "tokenAddress": "0xC8410270bb53f6c99A2EFe6eD3686a8630Efe22B",
    "tokenIsNative": false,
    "amountAtomic": "95000000",
    "amountFormatted": "95.0",
    "txHash": "0x...",
    "completedAt": "2026-04-19T10:02:40.000Z"
  }
}
```

### Key fields

| Field                                           | Type                                                                             | Notes                                                                      |
| ----------------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| `direction`                                     | `"ETHEREUM_TO_GOLIATH" \| "GOLIATH_TO_ETHEREUM"`                                 |                                                                            |
| `status`                                        | See [status lifecycle](/developer-guide/bridge/architecture.md#status-lifecycle) |                                                                            |
| `amount` / `amountFormatted`                    | atomic / human-readable                                                          | Input amount (pre-fee).                                                    |
| `fee`                                           | `{ amount, formatted, bps } \| null`                                             | Deducted fee. `null` for deposits.                                         |
| `outputAmount` / `outputAmountFormatted`        | atomic / human-readable                                                          | Amount delivered to recipient.                                             |
| `originConfirmations` / `requiredConfirmations` | numbers                                                                          | Progress toward source-chain finality.                                     |
| `estimatedCompletionTime`                       | ISO 8601 \| `null`                                                               | Best-effort ETA.                                                           |
| `holdUntil`                                     | ISO 8601 \| `null`                                                               | Non-null on G→E while in the hold period.                                  |
| `settlement`                                    | object \| `null`                                                                 | Populated on terminal `COMPLETED` state; mirrors the destination delivery. |

Poll this endpoint every 5–10 seconds after submitting the origin tx. Stop polling once `status` is `COMPLETED`, `EXPIRED`, or `FAILED`.

### Response 404 — `OPERATION_NOT_FOUND`

Expected for the first several seconds after the origin tx is mined, while the relayer indexes the event. Treat as "keep polling".

***

## `GET /bridge/history`

List operations for a given wallet address, newest first. `address` matches either `sender` or `recipient`.

### Query parameters

| Parameter   | Type                                             | Default | Notes                                                                                           |
| ----------- | ------------------------------------------------ | ------- | ----------------------------------------------------------------------------------------------- |
| `address`   | `string`                                         | —       | Required. Lowercased EVM address.                                                               |
| `limit`     | `number`                                         | `10`    | Max `100`.                                                                                      |
| `offset`    | `number`                                         | `0`     | For pagination.                                                                                 |
| `status`    | `string`                                         | —       | Optional filter — any [status value](/developer-guide/bridge/architecture.md#status-lifecycle). |
| `direction` | `"ETHEREUM_TO_GOLIATH" \| "GOLIATH_TO_ETHEREUM"` | —       | Optional filter.                                                                                |

### Response 200

```json
{
  "operations": [ { /* BridgeOperationResponse */ }, … ],
  "pagination": {
    "total": 42,
    "limit": 10,
    "offset": 0,
    "hasMore": true
  }
}
```

Use `pagination.hasMore` to drive infinite-scroll in mobile clients.

***

## `GET /bridge/fee-quote`

Preview the fee and output amount for a pending transfer. Call this before prompting the user to sign — it is the canonical source of fee and minimum data.

### Query parameters

| Parameter   | Type                                             | Notes                                    |
| ----------- | ------------------------------------------------ | ---------------------------------------- |
| `token`     | `"ETH" \| "USDC" \| "XCN"`                       | Required.                                |
| `amount`    | `string`                                         | Required. Human-readable (e.g. `"100"`). |
| `direction` | `"ETHEREUM_TO_GOLIATH" \| "GOLIATH_TO_ETHEREUM"` | Required.                                |

### Response 200

```json
{
  "inputAmount":     "100000000",
  "inputFormatted":  "100.0",
  "feeAmount":       "5000000",
  "feeFormatted":    "5.0",
  "feeBps":          25,
  "outputAmount":    "95000000",
  "outputFormatted": "95.0",
  "token":           "USDC"
}
```

For `ETHEREUM_TO_GOLIATH`, `feeAmount = 0` and `outputAmount = inputAmount`.

### Response 400 — `BELOW_MINIMUM`

```json
{
  "error": "BELOW_MINIMUM",
  "message": "Amount 1 USDC is below the minimum bridge amount of 10.0 USDC",
  "minAmount": "10000000",
  "minAmountFormatted": "10.0"
}
```

***

## `GET /bridge/limits`

Returns the current fee configuration and per-token minimums for both directions. Safe to cache in the client for a few minutes.

### Response 200

```json
{
  "goliathToEthereum": {
    "feeBps": 25,
    "tokens": {
      "ETH":  { "minAmount": "10000000000000000",    "minAmountFormatted": "0.01",
                "minFee":    "3000000000000000",     "minFeeFormatted":    "0.003" },
      "USDC": { "minAmount": "10000000",             "minAmountFormatted": "10.0",
                "minFee":    "5000000",              "minFeeFormatted":    "5.0" },
      "XCN":  { "minAmount": "5000000000000000000000", "minAmountFormatted": "5000.0",
                "minFee":    "1000000000000000000000", "minFeeFormatted":    "1000.0" }
    }
  },
  "ethereumToGoliath": {
    "feeBps": 0,
    "tokens": {
      "ETH":  { "minAmount": "0", "minAmountFormatted": "0", "minFee": "0", "minFeeFormatted": "0" },
      "USDC": { "minAmount": "0", "minAmountFormatted": "0", "minFee": "0", "minFeeFormatted": "0" },
      "XCN":  { "minAmount": "0", "minAmountFormatted": "0", "minFee": "0", "minFeeFormatted": "0" }
    }
  }
}
```

Ethereum → Goliath has no fee and no minimums enforced by the protocol — you may still want a client-side sanity check (e.g. "dust" amounts below gas cost).

***

## `POST /bridge/xcn-withdraw-intent`

Register a signed intent to withdraw **native XCN** from Goliath to Ethereum. Required only for native-XCN withdrawals — not for ETH or USDC withdrawals.

See the full flow in [Goliath → Ethereum — Native XCN](/developer-guide/bridge/goliath-to-ethereum.md#native-xcn-withdraw).

{% hint style="info" %}
**ERC-1271 smart-account signatures are accepted.** When `senderAddress` is a deployed contract, the bridge verifies the signature via an on-chain `isValidSignature(bytes32,bytes)` call and requires magic `0x1626ba7e`. The legacy `isValidSignature(bytes,bytes)` variant is not accepted. See [Smart Account Integration](/developer-guide/bridge/smart-account-integration.md) for the full contract, examples, and failure modes.
{% endhint %}

### Request body

```json
{
  "senderAddress":    "0x...",
  "recipientAddress": "0x...",
  "amountAtomic":     "5000000000000000000000",
  "idempotencyKey":   "…≤128 chars…",
  "deadline":         1713523200,
  "nonce":            "1713521400000",
  "signature":        "0x…"
}
```

| Field              | Type     | Notes                                                                         |
| ------------------ | -------- | ----------------------------------------------------------------------------- |
| `senderAddress`    | `string` | EVM address of the signer. Must match the recovered signer.                   |
| `recipientAddress` | `string` | EVM address on Ethereum that will receive XCN (as ERC-20).                    |
| `amountAtomic`     | `string` | **18-decimal wei string** (e.g. `parseEther("5000").toString()`).             |
| `idempotencyKey`   | `string` | Client-generated idempotency key, ≤128 characters. Use `crypto.randomUUID()`. |
| `deadline`         | `number` | Unix seconds. Must be in the future. Use `now + 1800` (30 min).               |
| `nonce`            | `string` | Any unique string. `Date.now().toString()` is fine.                           |
| `signature`        | `string` | EIP-712 signature over the message below.                                     |

### EIP-712 domain and types

```js
domain = { name: "GoliathBridge", version: "1", chainId: 327 };   // 8901 on testnet

types = {
  XcnWithdrawIntent: [
    { name: "senderAddress",    type: "address" },
    { name: "recipientAddress", type: "address" },
    { name: "amountAtomic",     type: "string"  },
    { name: "idempotencyKey",   type: "string"  },
    { name: "deadline",         type: "uint256" },
    { name: "nonce",            type: "string"  },
  ],
};
```

### Response 200

```json
{
  "intentId":             "35a3b8c8-4c10-4e77-8c68-…",
  "relayerWalletAddress": "0x90F26908Ee30C8fA6812f6BA66c050a86C8aF6cB",
  "expiresAt":            "2026-04-19T11:00:00.000Z"
}
```

After receiving this response, send native XCN `value = amountAtomic` from the `senderAddress` wallet to `relayerWalletAddress`, then call [`/bridge/xcn-withdraw-intent/bind-origin`](#post-bridge-xcn-withdraw-intent-bind-origin).

### Relevant errors

`VALIDATION_ERROR` (bad body), `DEADLINE_EXPIRED`, `SIGNATURE_INVALID`, `SIGNATURE_MISMATCH`, `SIGNATURE_DOMAIN_REJECTED`, `SIGNATURE_VERIFICATION_UNAVAILABLE` (ERC-1271 only — transient RPC or undeployed smart account), `BELOW_MINIMUM`, `SERVICE_PAUSED`.

***

## `POST /bridge/xcn-withdraw-intent/bind-origin`

Pair a previously registered intent with the Goliath tx hash that transferred native XCN to the relayer.

### Request body

```json
{
  "intentId":       "35a3b8c8-4c10-4e77-8c68-…",
  "senderAddress":  "0x...",
  "originTxHash":   "0x…"
}
```

### Response 200

```json
{
  "intentId":     "35a3b8c8-4c10-4e77-8c68-…",
  "originTxHash": "0x…"
}
```

### Relevant errors

| HTTP | Code                   | Meaning                                             |
| ---- | ---------------------- | --------------------------------------------------- |
| 404  | `INTENT_NOT_FOUND`     | Wrong `intentId`.                                   |
| 409  | `INTENT_NOT_PENDING`   | Intent already progressed.                          |
| 409  | `INTENT_ALREADY_BOUND` | Already bound to a different tx.                    |
| 409  | `DUPLICATE_ORIGIN_TX`  | Tx hash already bound to another intent.            |
| 410  | `INTENT_EXPIRED`       | Passed the 30 min window.                           |
| 403  | `SENDER_MISMATCH`      | `senderAddress` does not match the original signer. |

Retry transient errors with exponential backoff. Stop on terminal states (`INTENT_EXPIRED`, `SENDER_MISMATCH`, `INTENT_ALREADY_BOUND`).

***

## `GET /health`

Public liveness / readiness probe. Safe to call at the start of your session to decide whether to enable the Bridge UI.

### Response 200 (healthy)

```json
{
  "status":   "healthy",
  "version":  "1.0.0",
  "uptime":   86400,
  "chains": {
    "ethereum": { "connected": true },
    "goliath":  { "connected": true }
  },
  "database": { "connected": true },
  "relayer": {
    "pendingOperations": 3,
    "lastProcessedAt": "2026-04-19T10:02:40.000Z"
  }
}
```

### Response 503 (unhealthy)

Same shape with `"status": "unhealthy"` and one or more `connected: false`. Treat as "bridge temporarily unavailable — prompt the user to try again shortly" rather than a hard error.

***

## Client Polling Pattern

Canonical polling loop used by the Onyx App reference client:

```ts
async function pollBridgeStatus(originTxHash: string) {
  const url = `https://bridge.goliath.net/api/v1/bridge/status` +
              `?originTxHash=${originTxHash}`;

  for (;;) {
    const res = await fetch(url);

    if (res.status === 404) {
      await sleep(5000);                     // relayer hasn't indexed yet
      continue;
    }

    const data = await res.json();
    if (["COMPLETED", "EXPIRED", "FAILED"].includes(data.status)) {
      return data;                           // terminal
    }

    await sleep(5000);                       // poll every 5s
  }
}
```

Back off politely on errors — the endpoint has a public rate limit. For mobile apps, pause polling while the app is in the background and resume on foreground with a single fresh fetch.

## Next

* [Ethereum → Goliath](/developer-guide/bridge/ethereum-to-goliath.md) — how and when to call these endpoints.
* [Goliath → Ethereum](/developer-guide/bridge/goliath-to-ethereum.md) — including the XCN withdraw intent flow.
* [Contracts & Events](/developer-guide/bridge/contracts-and-events.md) — identifiers to feed into `/bridge/status`.


---

# 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.goliath.net/developer-guide/bridge/api-reference.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.
