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

# Reference

> Gateway endpoints, intent schema, status enum

This page is the wire-format reference for the execution gateway. The SDK abstracts everything here; read it when you're building tooling that doesn't use the SDK or you need to understand a payload on the wire.

## Endpoints

All paths are relative to the gateway's `gatewayUrl` (e.g. `https://gateway.flashnet.xyz`).

| Method | Path                             | Auth   | Purpose                                                       |
| ------ | -------------------------------- | ------ | ------------------------------------------------------------- |
| `POST` | `/api/v1/auth/challenge`         | none   | Request a challenge string for the given public key.          |
| `POST` | `/api/v1/auth/verify`            | none   | Submit `{ publicKey, signature }`. Returns `{ accessToken }`. |
| `GET`  | `/api/v1/network/info`           | none   | Runtime discovery (deposit address, chain id, paused flag).   |
| `GET`  | `/api/v1/health`                 | none   | Liveness check.                                               |
| `POST` | `/api/v1/execute`                | bearer | Submit a signed intent.                                       |
| `GET`  | `/api/v1/intents/{submissionId}` | bearer | Look up intent status by submission id.                       |

The bearer token returned from `/auth/verify` goes in `Authorization: Bearer <token>` for `/execute` and any other authenticated endpoint.

## Network info

```json theme={null}
GET /api/v1/network/info

{
  "spark": {
    "depositAddress": "sparkrt1...",
    "network": "REGTEST"
  },
  "execution": {
    "contractAddress": "0x...",
    "chainId": 21022
  },
  "paused": false,
  "minDepositSats": 1000
}
```

Cached client-side for 60 seconds. Pass `?forceRefresh=1` (or `getNetworkInfo({ forceRefresh: true })` in the SDK) to bypass after a suspected gateway re-key.

## Submit intent

The body is flat camelCase. Either `recipient` (deposit-only) or `evmTransaction` (deposit-and-execute) is set, never both. The signer is identified by the bearer token; there's no `publicKey` field on the body.

```json theme={null}
POST /api/v1/execute
Authorization: Bearer <token>

{
  "chainId": 21022,
  "deposits": [
    {
      "sparkTransferId": "a1b2c3d4e5f60718a1b2c3d4e5f60718",
      "asset": { "type": "btc" },
      "amount": 100000
    }
  ],
  "evmTransaction": "0xf86c...",
  "signature": "<DER-encoded secp256k1 ECDSA hex>",
  "nonce": "d4e5f607-1234-5678-9abc-def012345678",
  "expiresAt": 1735689600000
}
```

| Field            | Required          | Notes                                                                                                                                                                                             |
| ---------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `chainId`        | yes               | Execution chain id. Must match `getNetworkInfo().execution.chainId`.                                                                                                                              |
| `deposits`       | yes (may be `[]`) | Spark transfers funding the intent. Each entry has `sparkTransferId` (hex), `asset` (`{ "type": "btc" }` or `{ "type": "token", "tokenId": "<32-byte hex>" }`), and `amount` (u64 in base units). |
| `recipient`      | one of these two  | EVM address (`0x` + 20 bytes) credited for deposit-only intents. Mutually exclusive with `evmTransaction`.                                                                                        |
| `evmTransaction` | one of these two  | Hex-encoded RLP-serialized signed EIP-1559 transaction. The deposit recipient is the recovered signer. Mutually exclusive with `recipient`.                                                       |
| `signature`      | yes               | Hex-encoded secp256k1 ECDSA signature over the canonical intent message.                                                                                                                          |
| `nonce`          | yes               | Unique per signer. Replay-protected.                                                                                                                                                              |
| `expiresAt`      | yes               | Absolute unix-milliseconds. Past or >24h-future timestamps are rejected at admission.                                                                                                             |

The response:

```json theme={null}
{
  "submissionId": "7189726361600000001",
  "intentId": "0x6f9d...",
  "status": "ACCEPTED"
}
```

## Look up intent status

```json theme={null}
GET /api/v1/intents/{submissionId}
Authorization: Bearer <token>

{
  "submissionId": "7189726361600000001",
  "intentId": "0x6f9d...",
  "status": "INCLUDED_PENDING_FINALITY",
  "statusMessage": null,
  "executionTxHash": "0x...",
  "createdAt": "2026-05-04T12:00:00Z",
  "updatedAt": "2026-05-04T12:00:02Z"
}
```

`executionTxHash` is null before inclusion. `statusMessage` is non-null on failure: an `EXPIRED` intent carries the revert reason, oracle failure, or admission failure.

## Canonical intent message

The signed preimage. Order and field names are stable; the signer hashes JSON serialization in this exact shape.

```typescript theme={null}
interface CanonicalIntentMessage {
  chainId: number;
  transfers: CanonicalTransferEntry[];
  action: CanonicalIntentAction;
  nonce: string;
  expiresAt: number;
}

interface CanonicalTransferEntry {
  transferId: string;       // 32-char hex (Bitcoin, 16 bytes) or 64-char hex (token, 32 bytes)
  amountSats: number | bigint;
  assetType: "NativeSats" | "SparkToken";
  tokenId?: string;         // required when assetType is "SparkToken"
}

type CanonicalIntentAction =
  | { type: "deposit"; recipient: string }       // 0x-prefixed EVM address
  | { type: "execute"; signedTxHash: string };   // keccak256 of signedTx
```

Serialization is camelCase JSON. The signer SHA-256 hashes the UTF-8 bytes of this JSON, then signs with secp256k1 ECDSA (DER encoding). The validator side reproduces the preimage and verifies against the supplied public key.

The canonical message uses different field names than the wire format: `transfers` (not `deposits`) and `transferId` (not `sparkTransferId`). If you're constructing the signed preimage by hand, use the names above. If you're using the SDK, this is hidden from you.

## Status enum

```
ACCEPTED -> INCLUDED_PENDING_FINALITY -> FINALIZED
        \-> EXPIRED
```

Wire format: uppercase, `SCREAMING_SNAKE_CASE`. Returned in the submit response as `{ submissionId, intentId, status }` and on poll as the full `IntentStatusResponse` (see below). Poll `GET /api/v1/intents/{submissionId}` to follow transitions.

| Status                      | Meaning                                                                                                                                                                                                                                       |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ACCEPTED`                  | Gateway took the intent: signature, expiry, and replay checks passed, and it is queued for sequencer admission. A deposit-shaped intent stays here while the deposit oracle confirms every referenced transfer against the Spark operator DB. |
| `INCLUDED_PENDING_FINALITY` | The sequencer included the intent in a block. The EVM transaction ran successfully. Validators are signing the finality certificate.                                                                                                          |
| `FINALIZED`                 | Validators reached quorum. The action ran and settlement dispatched.                                                                                                                                                                          |
| `EXPIRED`                   | Terminal failure. The `expiresAt` deadline passed, gateway admission failed, the deposit oracle could not verify a transfer, or the EVM transaction reverted. `statusMessage` carries the reason.                                             |

`statusMessage` for revert cases includes the raw output bytes:

```
payload_flagged:stage=Execute outcome=Reverted intent_index=0 \
  tx_hash=0x... reason=reverted:gas_used=189179 output=0x1f2a2005
```

The SDK's `decodeRevertReason()` parses this format.

## Auth

```json theme={null}
POST /api/v1/auth/challenge
{ "publicKey": "<33-byte compressed hex>" }

→ { "challenge": "...", "challengeString": "..." }
```

```json theme={null}
POST /api/v1/auth/verify
{
  "publicKey": "<33-byte compressed hex>",
  "signature": "<DER-encoded ECDSA hex of SHA-256(challengeString)>"
}

→ { "accessToken": "..." }
```

Tokens are short-lived. The SDK handles refresh automatically; raw integrations should re-authenticate on 401.

## Replay and ordering guarantees

* **Spark transfer ids** can fund at most one finalized intent. Pre-finality, the gateway holds a soft-claim that prevents concurrent submissions but does not survive a rejection.
* **EVM nonces** are standard. The runtime rejects an out-of-order or replayed transaction even if the intent itself reaches the chain twice.
* **Withdrawal nonces** on `SparkGateway` are monotonic and reconciled on the settlement side, so a finalized withdrawal never settles twice on Spark.

## Versioning

The intent canonical-hash version is V2 (currently). Field additions land as new versions; the version is implicit in the absence of an unrecognized field. Treat this surface as semver-stable within a major version of the gateway.
