# Gas Station: API and wallet authorization

> Original page: https://docs.blockvectra.com/en/guides/gas-station/

## Access and current availability

Gas Station uses a separate HTTPS service origin and a tenant-scoped access token supplied by the Gas Station operator through the existing access arrangement. Use `Authorization: Bearer <apiToken>` on every endpoint below. A BlockVectra RPC API key is not this token; no self-service token-issuance endpoint is documented here. Keep the token and wallet private keys private. The tenant comes from the token, never from a request body or URL; do not put tokens or idempotency keys in URLs.

As of 21 October 2026, the development service is paused, execution is not enabled on any of the nine listed networks, and service-fee collection is not enabled. The directory is a readiness report, not a promise that a listed network can execute a transfer. Read `GET /v1/chains` before planning an execution; do not quote, sign, or submit while execution remains unavailable.

## Chain directory

`GET /v1/chains` returns `{ "chains": [...] }`. Each row has `slug`, `id`, `configured`, `runtimeVerified`, `readinessReasons`, and `executionStrategy`. `configured` describes configuration, not successful execution or finality verification. All nine rows currently have `configured=false` and `runtimeVerified=false`.

| Chain slug          | Chain ID |
| ------------------- | -------- |
| `arb_mainnet`       | 42161    |
| `base_mainnet`      | 8453     |
| `bsc_mainnet`       | 56       |
| `eth_mainnet`       | 1        |
| `eth_sepolia`       | 11155111 |
| `hyperevm_mainnet`  | 999      |
| `polygon_mainnet`   | 137      |
| `robinhood_mainnet` | 4663     |
| `robinhood_testnet` | 46630    |

## HTTP endpoints

Paths are relative to the HTTPS Gas Station origin supplied with access. They are not BlockVectra RPC routes. JSON POST bodies require `Content-Type: application/json`; the reconcile request has no body. Successful responses use HTTP 200.

| Method and path                | Input                                             | Response                             |
| ------------------------------ | ------------------------------------------------- | ------------------------------------ |
| `GET /v1/chains`               | No body                                           | Chain readiness directory            |
| `POST /v1/quotes`              | Quote request described below                     | `Quote`                              |
| `POST /v1/tasks`               | `quoteId`, `safeSignatures`, `envelopeSignatures` | `TaskView`                           |
| `GET /v1/quotes/:quoteId/task` | Previously saved quote ID; no body                | Associated `TaskView`, or 404        |
| `GET /v1/tasks/:id`            | Known task ID; no body                            | `TaskView`, or 404                   |
| `POST /v1/tasks/:id/reconcile` | Known task ID; no body                            | Observed `TaskView`; never resubmits |

The quote request contains exactly `chain`, `safe`, `sponsor`, `token`, `recipient`, `amount`, and `idempotencyKey`. Use a listed chain slug and the sponsor, asset and recipient permissions assigned to your access. `safe`, `token`, and `recipient` are nonzero EVM addresses. `amount` is a positive integer string in the token's smallest unit, below `2^256`; do not use floating-point token amounts. `chain`, `sponsor`, and `idempotencyKey` use 1–128 ASCII characters from `[A-Za-z0-9_.:-]`. Quote IDs and task IDs instead use 1–128 characters from `[A-Za-z0-9_-]`.

## Review and authorize a quote

The execution path is `SAFE_141_VIA_AA_V07`: a service SimpleAccount calls a customer Safe 1.4.1 through EntryPoint 0.7 to execute ERC-20 `transfer(recipient, amount)`. This is a compatible Safe workflow, not gas sponsorship for arbitrary EOAs. The EntryPoint address is `0x0000000071727De22E5E9d8BAf0edAc6f37da032`; the address alone does not prove a deployment or chain readiness. There is no direct-relay fallback.

A `Quote` contains `id`, `chain`, `chainId`, `safe`, `request`, `requestDigest`, `safeTypedData`, `envelopeTypedData`, `owners`, `threshold`, and `expiresAt`. Review the asset, recipient, amount, policy, actual service relayer, fee caps, units, and validity before signing. `request.execution.sender` and `nonce` are the customer Safe and its Safe nonce; the service account and UserOperation nonce are separate identities and must not replace them. Request amounts and caps are nonnegative decimal integer strings; validity timestamps are milliseconds since the Unix epoch.

The request digest is the lowercase 64-hex SHA-256 of canonical `{ domain: "gas-station-request-v2", ...request }`, without `0x`. Canonical JSON sorts object keys recursively, preserves array order, uses UTF-8 and has no whitespace. Recompute the normalized request as returned; the envelope uses the same digest with a `0x` prefix. The envelope also binds the SafeTx hash, actual relayer, and validity window. SafeTx itself has no expiry or relayer field; the envelope does not change Safe's on-chain signature semantics.

Owners sign SafeTx and the envelope locally. Submit `quoteId` and the two signature arrays, each with `{ owner, signature }` entries. Both arrays contain the same `threshold` owners, strictly ascending by owner address. Each signature is 65-byte `r || s || v`, with `v` equal to 27 or 28. Private keys are never submitted. The SDK checks digest and typed-data binding before signing, including `feeCaps.gas >= gasLimit * maxFeePerGas` and `0 <= maxPriorityFeePerGas <= maxFeePerGas`. SDK validation does not independently verify chain deployments or service-account ownership.

## Idempotency and uncertain submission

Within one tenant, an idempotency key binds the original quote intent. Reusing it for different content returns `409 IDEMPOTENCY_CONFLICT`; an expired quote is not renewed by replaying its key. Save `quote.id` before submitting. The SDK makes one HTTP request per method, does not automatically retry submission, and does not follow redirects or switch service origins.

If submission times out, loses its response, or returns an unreadable or invalid success response, the SDK reports `SUBMIT_OUTCOME_UNKNOWN`. Query `GET /v1/quotes/:quoteId/task` using the original quote ID. This lookup does not reserve, sign, sponsor, broadcast, or trigger reconciliation. A 404 means no associated task is visible to this tenant on this query; it does not prove the original submission was never accepted. Preserve the unknown outcome after 404 or a failed query; query explicitly again later rather than generating a new key or resubmitting. Once known, retain the task ID for subsequent reads or reconcile.

## Task state and transaction identity

`TaskView` contains `id`, `chain`, `safe`, `state`, optional `userOperationHash`, `transactionHash`, `businessSuccess`, `actualCosts`, optional `costProjection`, `blockHash`, and `errorCode`.

States are `RESERVED`, `PREPARING`, `EXPIRED`, `SIGNING`, `SIGNED`, `SUBMITTING`, `UNKNOWN`, `ACCEPTED`, `INCLUDED`, `FINALIZED_SUCCEEDED`, and `FINALIZED_FAILED`. `PREPARING` is a queryable progress state, not a server error. Unknown hashes and amounts remain null; older responses may omit `userOperationHash`. GETs do not sign or send; reconcile can observe status but never signs or rebroadcasts.

`userOperationHash` identifies the AA operation; `transactionHash` identifies the outer on-chain transaction and is never filled with the UserOperation hash. `blockHash` identifies inclusion. Hashes use `0x` plus 64 hex characters; formatting alone proves neither inclusion nor finality. Outer transaction inclusion or success does not establish business success. Use the task's verified business result and final state. A finalized business result remains available even when costs are incomplete; a failed business operation may still consume gas.

## Known and unknown costs

`actualCosts` is either a complete settled `{ gas, serviceFee, apiCu }` object or `null`; null does not mean free. A `FINALIZED_SUCCEEDED` or `FINALIZED_FAILED` task may still have `actualCosts=null` while costs remain unresolved.

`costProjection` reports each of `gas`, `serviceFee`, and `apiCu` as `{ unit, knownSubtotal, complete }`. Units come from that task's request; non-null amounts are nonnegative decimal integer strings in that unit. `knownSubtotal=null` means no trusted amount is available. A subtotal with `complete=false` may increase. `complete=true` requires a non-null amount; completeness alone does not create a settlement or prove payment. A `serviceFee` amount is a trusted accounting fact, not a payment, capture, or collection receipt.

After settlement, projection amounts equal `actualCosts` without adding CU again. Before settlement, gas and service-fee amounts use verified finalized observations. CU uses the original quote's cumulative cost evidence: unresolved-only requests remain unknown, and partially resolved requests can have a partial subtotal. CU is complete only when all attempts are resolved, its window is closed, and there is no conflicting observation. No requests and no trusted observation still means unknown, even after window closure; a trusted zero is distinct from missing evidence. Caps, HTTP status, and budgets are not actual costs. Reading the projection does not initiate another request for cost evidence or change accounting.

Older services may omit `costProjection`; the SDK and CLI preserve omission rather than inserting zeros. A malformed added projection is ignored without changing the original successful response. No prices, fee-collection endpoint, or paid-service availability are offered by this page.

## Errors and paused behavior

Errors have the shape `{ "error": { "code": "..." } }`.

| HTTP status | Code and meaning                                                                      |
| ----------- | ------------------------------------------------------------------------------------- |
| 400         | `INVALID_REQUEST` or `INVALID_SIGNATURE`: invalid input or authorization              |
| 401         | `UNAUTHORIZED`: missing or invalid Gas Station token                                  |
| 403         | `FORBIDDEN`: access does not permit the chain, sponsor, asset, recipient, or amount   |
| 404         | `NOT_FOUND`, `QUOTE_NOT_FOUND`, or `TASK_NOT_FOUND`: no visible resource              |
| 409         | `IDEMPOTENCY_CONFLICT`, `QUOTE_EXPIRED`, or `BUDGET_EXCEEDED`                         |
| 429         | `RATE_LIMITED`: respect a supplied `Retry-After`; do not replay uncertain submissions |
| 503         | `CHAIN_NOT_READY`: execution is paused or required chain capabilities are unavailable |

The readiness gate rejects new execution with `503 CHAIN_NOT_READY` while paused. Authentication, input validation, permissions, expired quotes, and existing task associations still have their own outcomes; not every request during a pause returns 503. A matching replay associated with an existing task returns its facts without sending again. Status reads and reconciliation remain available subject to authentication and permissions; a pause does not turn an unknown result into failure or authorize resubmission.
