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

# Handle errors and retries

> Tell gateway errors from API errors, retry only what is safe to repeat, sign every attempt again, reconcile failed creates, and log the request ID.

Every failed request returns one of two bodies: the Olympex error envelope, or a short gateway body with only a `message`. This guide shows how to tell them apart, which failures a retry can fix, and a retry wrapper that signs every attempt again, because the API rejects a nonce it has already seen. It also shows how to check whether an order you tried to create exists before you send the create again.

<Info>
  The examples wrap `olympexRequest` from `sign-request.ts` and `olympex_request` from `sign_request.py`. Get them from [Sign requests](/authentication/sign-requests).
</Info>

## Prerequisites

* The signing helper for your language, and API credentials in your server's environment. TypeScript examples run on Node.js 22.18 or later, in an ES module project (`npm pkg set type=module`); Python examples need 3.8 or later with `requests`.
* A structured logger. The examples write JSON lines to the console or use Python's `logging`.
* The list of error codes on [Errors and retries](/authentication/errors-and-retries).

## Steps

<Steps>
  <Step title="Tell the two error shapes apart">
    Olympex returns errors in its envelope, with a stable `error.code` and a `meta.requestId`. For validation errors, each item in `error.details` names a `field` and a `message`:

    ```json 400 VALIDATION_ERROR theme={null}
    {
      "success": false,
      "error": {
        "code": "VALIDATION_ERROR",
        "message": "Invalid request body",
        "details": [
          {
            "field": "params.chainId",
            "message": "Invalid input: expected number, received string"
          },
          {
            "field": "params.inTokenAddress",
            "message": "Must be a valid EVM address"
          },
          {
            "field": "params.amount",
            "message": "Invalid input: expected string, received number"
          },
          {
            "field": "params.gasPrice",
            "message": "Invalid input: expected string, received undefined"
          }
        ]
      },
      "meta": {
        "requestId": "ENUlmg3dIAMEMEw=",
        "version": "v1",
        "accountType": "integrator",
        "apiKeyId": "00000000-0000-4000-8000-000000000000"
      }
    }
    ```

    For errors raised while handling the request, `details` items carry only a `message`, and the list can be empty:

    ```json 500 SWAP_ERROR theme={null}
    {
      "success": false,
      "error": {
        "code": "SWAP_ERROR",
        "message": "Error fetching single-chain swap",
        "details": [
          {
            "message": "Request failed with status code 400"
          }
        ]
      },
      "meta": {
        "requestId": "E7egOiScoAMEZWw=",
        "version": "v1",
        "accountType": "integrator",
        "apiKeyId": "00000000-0000-4000-8000-000000000000"
      }
    }
    ```

    The API gateway answers some requests before they reach Olympex. Its body has only a `message`, with no `meta.requestId`; the request ID is in the `apigw-requestid` response header instead:

    ```json 403 Gateway theme={null}
    {
      "message": "Forbidden"
    }
    ```

    The helpers turn both shapes into one error type, `OlympexApiError`, with the HTTP `status`, the `code`, the `details` and the request ID (`requestId` in TypeScript, `request_id` in Python). For a gateway body, `code` is `HTTP_<status>`, for example `HTTP_403`, and the request ID is empty: the helpers read it from the body only, not from the `apigw-requestid` header. For gateway `401` and `403` responses, log that header from your HTTP client, or reproduce the call with `curl -i` to see it.

    Every `404` is an Olympex envelope with `NOT_FOUND`. On an `{id}` path, the order, strategy or DCA order ID doesn't exist or belongs to another API key. For an unknown path or method, the message names them, for example `"No route for GET /api/v1/quote"`.

    Branch on `error.code` and the HTTP status, never on `error.message`: messages are for people and can change. Olympex can add codes, so handle a code you don't recognize by its HTTP status.
  </Step>

  <Step title="Decide what to retry">
    A retry helps only when the failure is transient, and it's safe only when repeating the request can't create something twice:

    * **Safe to repeat:** every `GET`, `PATCH` and `DELETE`, and `POST /quotes`, `POST /swap`, `POST /support-chain` and `POST /tx-status`, which only read or build data. `POST /swap` returns calldata and never broadcasts it; after a retry, use the calldata from the last response you received. A `DELETE /limit-order/{id}` repeated after the first one succeeded returns `409 CONFLICT`, because the order is no longer `pending`: read the order to confirm it is `cancelled`.
    * **Never retried automatically:** `POST /limit-order`, `POST /dca-order/strategies` and `POST /accounts`. Each creates a new resource every time it succeeds, and Olympex ignores any `id` you send. A timeout or a 5xx doesn't tell you whether the first call succeeded, and a duplicate order or strategy is a second live one that draws on the same token allowance. List your orders or strategies and match on your own fields before you send the create again, as the reconcile step below shows. No endpoint lists accounts, so after a `POST /accounts` that failed this way, decide by hand whether to create another.

    | Response | From | Retry? | What to do |
    | - | - | - | - |
    | `400 VALIDATION_ERROR` | Olympex | No | Fix the fields listed in `error.details`. |
    | `401 {"message": "Unauthorized"}` | Gateway | Once, signed again with a new nonce | A signing header is missing. Send all four signed headers. If the new attempt fails too, stop and check your signer and credentials. |
    | `401 UNAUTHORIZED` | Olympex | No | The request reached Olympex without its authentication context, which signing again can't fix. Contact [partners@olympex.io](mailto:partners@olympex.io) with `meta.requestId`. |
    | `403 {"message": "Forbidden"}` | Gateway | Once, signed again with a new nonce | Authentication failed: unknown API key ID, wrong passphrase, bad signature (including a signature over another method, path or query), a nonce that isn't 24 hexadecimal characters, timestamp outside ±300 s of server time, reused nonce, inactive account, or signature verification temporarily unavailable. A new timestamp and nonce fix a request that waited too long or reused a nonce. If the new attempt fails too, stop and check your credentials and clock. If correctly signed requests keep failing, contact [partners@olympex.io](mailto:partners@olympex.io) with the `apigw-requestid` response header. |
    | `403 FORBIDDEN` | Olympex | No | `"Invalid body hash"`: send exactly the canonical bytes you signed, with `Content-Type: application/json`. |
    | `404 NOT_FOUND` | Olympex | No | The order, strategy or DCA order ID doesn't exist, or it belongs to another API key: check the ID, and call with the API key that created it. Or the path or method doesn't exist (`"No route for …"`). |
    | `409 CONFLICT` | Olympex | No | The limit order is no longer `pending`, so it can't be updated or cancelled. Read it with [`GET /limit-order/{id}`](/api-reference/limit-orders/get-limit-order). |
    | `422 NO_ROUTE` | Olympex | Yes, with backoff | No source returned a route for this pair and amount, or every source failed or timed out. If it persists, try another amount or pair. From `POST /swap`, switch to the next source first. |
    | `500 QUOTE_ERROR`, `CROSS_CHAIN_QUOTE_ERROR` | Olympex | Yes | An unexpected error while quoting. |
    | `500 SWAP_ERROR`, `CROSS_CHAIN_SWAP_ERROR` | Olympex | Once | Then switch to the next liquidity source, or request a fresh quote. |
    | `500 SUPPORT_CHAIN_ERROR`, `ENABLED_CHAINS_ERROR`, `TOKEN_LIST_ERROR` | Olympex | Yes | From `POST /support-chain`, `GET /chains` and `GET /tokens`. Keep serving your cached chain and token lists meanwhile. If it persists, contact support with `meta.requestId`. |
    | `500 INTERNAL_ERROR` | Olympex | Yes, except on a create | Order and DCA failures surface as this code. After `POST /limit-order` or `POST /dca-order/strategies`, reconcile instead of retrying. If it persists, contact support with `meta.requestId`. |
    | `500 TX_STATUS_ERROR` | Olympex | Yes | The transfer status is unknown for now. Keep polling [`POST /tx-status`](/api-reference/transactions/get-transaction-status) every 15 to 30 seconds. From [`GET /transactions/{hash}`](/api-reference/transactions/get-transaction), Olympex couldn't read the chain: retry with backoff. |
    | `500 {"message": "Internal Server Error"}` | Gateway | Yes, except on a create | The first request after a quiet period can get this response. Sign again and retry once, then back off. The gateway also returns it when it gets no response from Olympex in time. |
    | `503 {"message": "Service Unavailable"}` | Gateway | Yes, except on a create | The request ran past the gateway timeout of about 30 seconds. |
    | Connection failure or client timeout | Your HTTP client | Yes, except on a create | Nothing reached you, or the response was too slow. |
    | Any code you don't recognize | Olympex | By status | Retry 5xx except on a create, and don't retry 4xx. |

    A gateway `401` or `403` is returned before the request reaches Olympex, so nothing was created: signing again once is safe on every endpoint, creates included.
  </Step>

  <Step title="Retry with backoff, and sign every attempt">
    `olympexRequest` and `olympex_request` sign the body each time you call them, with a new timestamp and nonce, so a retry is a new call, never a resend of the same headers. Wait longer after each failure, and add random jitter so many clients don't retry in step.

    <CodeGroup>
      ```ts olympex-retry.ts theme={null}
      // Retry transient failures. Every attempt is signed again.
      import { OlympexApiError, olympexRequest, type HttpMethod, type Json } from "./sign-request.ts"; // /authentication/sign-requests

      /** 5xx responses (envelope or gateway), NO_ROUTE and network failures are worth another attempt with backoff. */
      export const isRetryable = (error: unknown): boolean => {
        if (error instanceof OlympexApiError) return error.status >= 500 || error.code === "NO_ROUTE";
        // fetch() rejects with a TypeError when the network fails, and AbortSignal.timeout() with a TimeoutError.
        return error instanceof TypeError || (error instanceof Error && error.name === "TimeoutError");
      };

      const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));

      /** These create a resource each time they succeed. A blind retry after a timeout can create a duplicate. */
      const CREATES = new Set(["/accounts", "/limit-order", "/dca-order/strategies"]);
      export const isSafeToRepeat = (method: HttpMethod, path: string): boolean =>
        method !== "POST" || !CREATES.has(path.split("?")[0]);

      export async function olympexRequestWithRetry<T>(
        method: HttpMethod,
        path: string,
        body?: Json,
        { attempts = 4, baseDelayMs = 1_000, maxDelayMs = 20_000 } = {},
      ): Promise<T> {
        let reSigned = false;
        for (let attempt = 1; ; attempt++) {
          try {
            return await olympexRequest<T>(method, path, body); // signs now: new timestamp, new nonce
          } catch (error) {
            const api = error instanceof OlympexApiError ? error : undefined;
            // One freshly signed attempt fixes a gateway 401 or 403 from a stale timestamp or a reused nonce.
            // Gateway 401 and 403 are returned before the request reaches Olympex, so nothing was created.
            const reSign = !reSigned && (api?.code === "HTTP_401" || api?.code === "HTTP_403");
            if (reSign) reSigned = true;
            const retry = attempt < attempts && (reSign || (isRetryable(error) && isSafeToRepeat(method, path)));
            console.warn(
              JSON.stringify({
                event: "olympex_request_failed",
                method,
                path,
                attempt,
                retry,
                status: api?.status,
                code: api?.code ?? (error instanceof Error ? error.name : "unknown"),
                requestId: api?.requestId, // envelope errors only; gateway errors carry it in the apigw-requestid header
                at: new Date().toISOString(),
              }),
            );
            if (!retry) throw error;
            // Exponential backoff with full jitter: 0 to 1 s, 0 to 2 s, 0 to 4 s, ... capped at maxDelayMs.
            await sleep(Math.random() * Math.min(maxDelayMs, baseDelayMs * 2 ** (attempt - 1)));
          }
        }
      }
      ```

      ```python olympex_retry.py theme={null}
      """Retry transient Olympex failures. Every attempt is signed again."""
      import logging
      import random
      import time

      import requests

      from sign_request import OlympexApiError, olympex_request  # /authentication/sign-requests

      log = logging.getLogger("olympex")
      TRANSPORT_ERRORS = (requests.ConnectionError, requests.Timeout)


      def is_retryable(error):
          """5xx responses (envelope or gateway), NO_ROUTE and network failures are worth another attempt with backoff."""
          if isinstance(error, OlympexApiError):
              return error.status >= 500 or error.code == "NO_ROUTE"
          return isinstance(error, TRANSPORT_ERRORS)


      # These create a resource each time they succeed. A blind retry after a timeout can create a duplicate.
      CREATES = {"/accounts", "/limit-order", "/dca-order/strategies"}


      def is_safe_to_repeat(method, path):
          return method.upper() != "POST" or path.split("?")[0] not in CREATES


      def olympex_request_with_retry(method, path, body=None, attempts=4, base_delay=1.0, max_delay=20.0):
          re_signed = False
          for attempt in range(1, attempts + 1):
              try:
                  return olympex_request(method, path, body)  # signs now: new timestamp, new nonce
              except (OlympexApiError, *TRANSPORT_ERRORS) as error:
                  # One freshly signed attempt fixes a gateway 401 or 403 from a stale timestamp or a reused nonce.
                  # Those are returned before the request reaches Olympex, so nothing was created.
                  re_sign = not re_signed and getattr(error, "code", None) in ("HTTP_401", "HTTP_403")
                  if re_sign:
                      re_signed = True
                  retry = attempt < attempts and (re_sign or (is_retryable(error) and is_safe_to_repeat(method, path)))
                  log.warning(
                      "olympex_request_failed method=%s path=%s attempt=%d retry=%s status=%s code=%s requestId=%s",
                      method,
                      path,
                      attempt,
                      retry,
                      getattr(error, "status", None),
                      getattr(error, "code", type(error).__name__),
                      getattr(error, "request_id", None),  # envelope errors only; gateway errors carry it in the apigw-requestid header
                  )
                  if not retry:
                      raise
                  # Exponential backoff with full jitter: 0 to 1 s, 0 to 2 s, 0 to 4 s, ... capped at max_delay.
                  time.sleep(random.uniform(0, min(max_delay, base_delay * 2 ** (attempt - 1))))
      ```
    </CodeGroup>

    Use it wherever you'd call the helper directly:

    <CodeGroup>
      ```ts TypeScript theme={null}
      import { olympexRequestWithRetry } from "./olympex-retry.ts";

      const supported = await olympexRequestWithRetry<boolean>("POST", "/support-chain", { chainId: 137 });
      ```

      ```python Python theme={null}
      from olympex_retry import olympex_request_with_retry

      supported = olympex_request_with_retry("POST", "/support-chain", {"chainId": 137})
      ```
    </CodeGroup>

    The wrapper follows the table above. It signs a gateway `401` or `403` again once on any request. It retries 5xx responses, `422 NO_ROUTE` and network failures with backoff, except on `POST /limit-order`, `POST /dca-order/strategies` and `POST /accounts`: for those it logs `"retry": false` and throws, so you can reconcile.

    Size `attempts` for the caller. Each attempt can take up to the gateway timeout of about 30 seconds, so a user waiting on a quote needs fewer attempts than a background job. Keep your client timeout above 30 seconds; the helpers use 35.
  </Step>

  <Step title="Reconcile a create that failed">
    When `POST /limit-order` fails with a timeout, a network error or a 5xx, the order may exist anyway. Look for it with [`GET /limit-order`](/api-reference/limit-orders/list-limit-orders) before you create it again. Filter by maker and chain, and match on fields you chose: `expired` is a good key, because your code generates it for each order.

    <CodeGroup>
      ```ts TypeScript theme={null}
      import { olympexRequestWithRetry } from "./olympex-retry.ts";

      type SentOrder = { accountTo: string; chainId: number; inTokenAddress: string; outTokenAddress: string; amount: string; priceTrigger: string; expired: string };
      type LimitOrder = { id: string; status: string; inTokenAddress: string; outTokenAddress: string; amount: number; priceTrigger: number; expired: string };

      /** Returns the order a failed POST /limit-order created, or undefined if it created none. */
      export async function findLimitOrder(sent: SentOrder): Promise<LimitOrder | undefined> {
        // GET is safe to repeat. accountTo must be the exact form you sent: the filter is case-sensitive.
        const orders = await olympexRequestWithRetry<LimitOrder[]>("GET", `/limit-order?accountTo=${sent.accountTo}&chainId=${sent.chainId}`);
        return orders.find(
          (o) =>
            o.expired === sent.expired &&
            o.inTokenAddress.toLowerCase() === sent.inTokenAddress.toLowerCase() &&
            o.outTokenAddress.toLowerCase() === sent.outTokenAddress.toLowerCase() &&
            o.amount === Number(sent.amount) && // responses return amounts as numbers
            o.priceTrigger === Number(sent.priceTrigger),
        );
      }
      ```

      ```python Python theme={null}
      from olympex_retry import olympex_request_with_retry


      def find_limit_order(sent):
          """Return the order a failed POST /limit-order created, or None if it created none."""
          # GET is safe to repeat. accountTo must be the exact form you sent: the filter is case-sensitive.
          orders = olympex_request_with_retry("GET", f"/limit-order?accountTo={sent['accountTo']}&chainId={sent['chainId']}")
          for order in orders:
              if (
                  order["expired"] == sent["expired"]
                  and order["inTokenAddress"].lower() == sent["inTokenAddress"].lower()
                  and order["outTokenAddress"].lower() == sent["outTokenAddress"].lower()
                  and order["amount"] == float(sent["amount"])  # responses return amounts as numbers
                  and order["priceTrigger"] == float(sent["priceTrigger"])
              ):
                  return order
          return None
      ```
    </CodeGroup>

    If it finds the order, use it. If not, send the create again. Spell the filters exactly and send each once: the list ignores unknown query parameters, so a misspelt filter returns every order, and a repeated parameter matches nothing. For `POST /dca-order/strategies`, list [`GET /dca-order/strategies`](/api-reference/dca/list-dca-strategies) with `?accountTo=`, newest first, and match on the tokens, `totalAmount`, `frequency`, `iterations` and a `createdAt` after you sent the request.
  </Step>

  <Step title="Switch liquidity source on SWAP_ERROR or NO_ROUTE">
    When [`POST /swap`](/api-reference/swap/build-swap) fails for one source, the quote's `aggregatorOrder` lists the others that quoted the same pair, best first ([Aggregation and routing](/concepts/aggregation-and-routing#falling-back-with-aggregatororder)). Give each source one retry, then move on:

    ```ts theme={null}
    import { OlympexApiError, type Json } from "./sign-request.ts";
    import { olympexRequestWithRetry } from "./olympex-retry.ts";

    type SingleChainSwap = { to: string; data: string; value: string; contractToApprove: string; minOutAmount: string };

    export async function buildSwapWithFallback(
      params: Record<string, Json>, // the /swap params without aggregatorId
      quote: { aggregatorId: string; aggregatorOrder: string[] | null },
    ): Promise<SingleChainSwap> {
      const sources = [quote.aggregatorId, ...(quote.aggregatorOrder ?? []).filter((id) => id !== quote.aggregatorId)];
      let lastError: unknown;
      for (const aggregatorId of sources) {
        try {
          const { swap } = await olympexRequestWithRetry<{ swap: SingleChainSwap }>("POST", "/swap",
            { mode: "single-chain", params: { ...params, aggregatorId } },
            { attempts: 2 }, // calldata should be fresh: don't spend long on one source
          );
          return swap;
        } catch (error) {
          if (!(error instanceof OlympexApiError && (error.code === "SWAP_ERROR" || error.code === "NO_ROUTE"))) throw error;
          lastError = error; // this source can't build the route right now: try the next one
        }
      }
      throw lastError;
    }
    ```

    A fallback source can return less than the quote's winner. Show the user the `outAmount` and `minOutAmount` from the `POST /swap` response you use. Cross-chain swaps have no fallback list: on a persistent `CROSS_CHAIN_SWAP_ERROR` or `NO_ROUTE`, request a new cross-chain quote.

    A `200` doesn't guarantee that the calldata executes: a source can build calldata that reverts, and no error code tells you. Run `eth_estimateGas` on every transaction before you send it. If it reverts once the balance and allowance are in place, move to the next source the same way, or request a new quote for a cross-chain swap. [Execute a swap](/guides/execute-a-swap) shows the loop.
  </Step>

  <Step title="Log the request ID and show a safe message">
    Log every failure as structured data: endpoint, HTTP status, `error.code`, `meta.requestId`, attempt number and your own correlation ID. Gateway errors have no `meta.requestId`: their ID is in the `apigw-requestid` response header, which the helpers don't read, so log the UTC time and path too, and the header if your HTTP client exposes it. Support needs the request ID to find a request.

    Never log the `x-passphrase` or `x-signature` headers, the secret key or the passphrase. Many HTTP clients and APM agents record headers by default: redact them.

    Show users a message that matches what they can do, and keep codes and request IDs in your logs:

    | Situation | Message for the user |
    | - | - |
    | `VALIDATION_ERROR` on user input | "Check the amount and tokens, then try again." |
    | `NO_ROUTE` from `POST /quotes` after retries | "No route is available for this pair and amount right now. Try a different amount." |
    | `SWAP_ERROR` or `NO_ROUTE` after every source | "We couldn't build this swap. Refresh the quote and try again." |
    | A `401` or `403` that persists after one freshly signed attempt | "Something went wrong on our side." Alert your team. Usually this is a credential, clock or signing problem, but the gateway also returns `403` when Olympex can't complete its own authentication checks. If credentials and clock check out, contact [partners@olympex.io](mailto:partners@olympex.io) with the UTC time. |
    | 5xx or timeouts after retries | "The service is busy. Try again in a moment." |
    | `TX_STATUS_ERROR` while tracking | "Your transfer is in progress." |
    | A failed order or strategy create, before you reconcile | "We're checking whether your order was placed." |
  </Step>
</Steps>

## Verify

Send `chainId` as a string to `POST /support-chain`, where it must be an integer. The wrapper makes one attempt, logs `"retry": false` and throws:

```ts theme={null}
import { OlympexApiError } from "./sign-request.ts";
import { olympexRequestWithRetry } from "./olympex-retry.ts";

try {
  await olympexRequestWithRetry("POST", "/support-chain", { chainId: "137" }); // invalid: a string
} catch (error) {
  if (error instanceof OlympexApiError) console.log(error.status, error.code, error.details, error.requestId);
}
```

The API answers with:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request body",
    "details": [
      {
        "field": "chainId",
        "message": "Invalid input: expected number, received string"
      }
    ]
  },
  "meta": {
    "requestId": "E7ee3hLMoAMEZxg=",
    "version": "v1",
    "accountType": "integrator",
    "apiKeyId": "00000000-0000-4000-8000-000000000000"
  }
}
```

Then check the other paths:

* A `GET` to an unknown path such as `/quote` returns `404 NOT_FOUND` after one attempt, with the message `"No route for GET /api/v1/quote"`.
* `GET /limit-order/{id}` with a random UUID returns `404 NOT_FOUND` after one attempt.
* With your connection down, a `GET /chains` is retried with growing delays, then throws. A `POST /limit-order` fails after one attempt, and the log line shows `"retry": false`.
* Your logs show the request ID for envelope errors and contain no passphrase or signature.

## Common pitfalls

<Warning>
  **Resending the same signed headers.** The API rejects a nonce it has seen in the last 5 minutes, and a timestamp more than 300 seconds from server time. Replaying a captured request fails with `403`. Sign every attempt again.
</Warning>

<Warning>
  **Retrying a create.** `POST /limit-order` and `POST /dca-order/strategies` create a live order or strategy on every success, and a timeout doesn't tell you whether the first call succeeded. A blind retry can leave two of them drawing on the same allowance. Reconcile first.
</Warning>

<Warning>
  **Retrying a broadcast.** A wallet transaction can reach the chain even when sending it fails. Check whether the first transaction arrived (look up its hash or the account's nonce) before you build and send another swap.
</Warning>

<Warning>
  **Retrying 4xx responses.** A `400`, `404` or `409` fails the same way every time, and so does an Olympex `UNAUTHORIZED` or `FORBIDDEN`: fix the cause instead. A gateway `401` or `403` that repeats after one freshly signed attempt is a credential, clock or signing problem: stop and alert instead of retrying. Retrying hides the bug and turns one alert into a flood of failed requests.
</Warning>

<Warning>
  **Timing out before the gateway.** A client timeout under 30 seconds abandons requests the gateway would still answer, then retries them. Keep yours above 30 seconds.
</Warning>

## What's next

<CardGroup cols={2}>
  <Card title="Errors and retries" icon="circle-exclamation" href="/authentication/errors-and-retries">
    Every error code and gateway response.
  </Card>

  <Card title="Track a swap to finality" icon="clock" href="/guides/track-a-swap-to-finality">
    Polling `POST /tx-status` through unknown states.
  </Card>

  <Card title="Limits" icon="gauge" href="/authentication/limits">
    Signing windows, timeouts and volume.
  </Card>

  <Card title="Going to production" icon="list-check" href="/guides/going-to-production">
    The pre-launch checklist.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.