Endpoints
All paths are relative to the gateway’sgatewayUrl (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. |
/auth/verify goes in Authorization: Bearer <token> for /execute and any other authenticated endpoint.
Network info
?forceRefresh=1 (or getNetworkInfo({ forceRefresh: true }) in the SDK) to bypass after a suspected gateway re-key.
Submit intent
The body is flat camelCase. Eitherrecipient (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.
| 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. |
Look up intent status
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.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
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:
decodeRevertReason() parses this format.
Auth
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
SparkGatewayare monotonic and reconciled on the settlement side, so a finalized withdrawal never settles twice on Spark.