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

# Quickstart

> Send your first crosschain intent via the Rhinestone REST API.

Send your first crosschain intent straight through the REST API. This guide walks the full flow for **EOA users**.

<Info>
  **Using the Rhinestone SDK?** The [wallet quickstart](/smart-wallet/quickstart) already covers crosschain intents. Come back here when you need API-specific configuration.

  **Using an existing smart account (non-Rhinestone SDK)?** You'll need to [install the Intent Executor](/intents/guides/installing-intent-executor) on each account before using Warp, then follow the [smart account signing guide](/intents/guides/signing) for the signing step.
</Info>

## Prerequisites

* A Rhinestone API key ([request one here](https://tally.so/r/wg22x4)) — not required for testnets
* An EOA with funds on Ethereum, Optimism, Base, or Arbitrum — the source chains this quickstart handles below (extend the `accountAccessList` and wrap map for any other [supported chain](/home/resources/supported-chains))

## Steps

<Steps>
  <Step title="Get a quote">
    Submit a meta intent to the `/quotes` endpoint. Specify your destination chain, the token and amount you want on that chain, and your account:

    ```ts theme={null}
    import { createWalletClient, extractChain, http, type Hex } from "viem";
    import { privateKeyToAccount } from "viem/accounts";
    import * as chains from "viem/chains";

    const BASE_URL = "https://v1.orchestrator.rhinestone.dev";
    const API_KEY = process.env.RHINESTONE_API_KEY;

    // EOA signer — swap in any viem-compatible signer.
    const account = privateKeyToAccount(process.env.PRIVATE_KEY as Hex);
    const EOA_ADDRESS = account.address;

    // A route can pull source funds from any chain, so token requirements can land
    // on any chain. Build a wallet client for whichever chain a requirement targets.
    const walletClientFor = (chainId: string) =>
      createWalletClient({
        account,
        chain: extractChain({
          chains: Object.values(chains),
          id: Number(chainId.split(":")[1]),
        }),
        transport: http(),
      });

    const headers = {
      "Content-Type": "application/json",
      "x-api-key": API_KEY,
      "x-api-version": "2026-04.blanc",
    };

    const res = await fetch(`${BASE_URL}/quotes`, {
      method: "POST",
      headers,
      body: JSON.stringify({
        account: {
          address: EOA_ADDRESS,
          accountType: "EOA",
        },
        destinationChainId: "eip155:8453", // Base
        tokenRequests: [
          {
            tokenAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC on Base
            amount: "5000000", // 5 USDC (6 decimals)
          },
        ],
        // Limit source funds to the chains this quickstart handles below.
        accountAccessList: {
          chainIds: ["eip155:1", "eip155:10", "eip155:8453", "eip155:42161"],
        },
      }),
    });

    const { routes } = await res.json();
    const route = routes[0];
    const { intentId, signData, tokenRequirements } = route;
    ```

    The response is a server-ranked `routes` array. Use `routes[0]` unless you have your own ranking. Each route carries:

    * `intentId`: server-stored handle, used to submit
    * `cost`: input/output amounts and fee breakdown
    * `signData`: EIP-712 typed data to sign
    * `tokenRequirements`: approvals or wrapping the user must complete before signing
  </Step>

  <Step title="Fulfill token requirements">
    Before signing, the user must fulfill any `tokenRequirements` returned in the quote. Keys are CAIP-2 chain ids (`eip155:8453`):

    **ERC-20 approvals** — approve tokens to the Permit2 contract:

    ```ts theme={null}
    import { erc20Abi, maxUint256 } from "viem";

    for (const [chainId, tokens] of Object.entries(tokenRequirements)) {
      for (const [tokenAddress, requirement] of Object.entries(tokens)) {
        if (requirement.type === "approval") {
          await walletClientFor(chainId).writeContract({
            address: tokenAddress,
            abi: erc20Abi,
            functionName: "approve",
            args: [requirement.spender, maxUint256],
          });
        }
      }
    }
    ```

    **ETH wrapping** — wrap native ETH to WETH:

    ```ts theme={null}
    // Wrapped-native token for each chain in the accountAccessList above
    // (only needed when the source token is ETH).
    const WETH: Record<string, `0x${string}`> = {
      "eip155:1": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // Ethereum
      "eip155:10": "0x4200000000000000000000000000000000000006", // Optimism
      "eip155:8453": "0x4200000000000000000000000000000000000006", // Base
      "eip155:42161": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", // Arbitrum
    };
    const wethAbi = [
      {
        name: "deposit",
        type: "function",
        stateMutability: "payable",
        inputs: [],
        outputs: [],
      },
    ] as const;

    for (const [chainId, tokens] of Object.entries(tokenRequirements)) {
      for (const [tokenAddress, requirement] of Object.entries(tokens)) {
        if (requirement.type === "wrap") {
          await walletClientFor(chainId).writeContract({
            address: WETH[chainId],
            abi: wethAbi,
            functionName: "deposit",
            value: BigInt(requirement.amount),
          });
        }
      }
    }
    ```

    <Info>
      Use max approvals to the [Permit2](https://github.com/Uniswap/permit2) contract. This is the only contract you ever approve — future intents won't need a new approval.
    </Info>
  </Step>

  <Step title="Sign the intent">
    Forward `signData.origin[]` and `signData.destination` directly to `signTypedData`. One signature per source chain, plus the destination signature:

    ```ts theme={null}
    const originSignatures = await Promise.all(
      signData.origin.map((typedData) =>
        account.signTypedData(typedData),
      ),
    );
    const destinationSignature = await account.signTypedData(
      signData.destination,
    );
    ```

    <Card title="Signing guide" icon="signature" href="./guides/signing">
      Smart account signing and validator wrapping.
    </Card>
  </Step>

  <Step title="Submit the intent">
    Post the signed intent to `/intents`:

    ```ts theme={null}
    const submitRes = await fetch(`${BASE_URL}/intents`, {
      method: "POST",
      headers,
      body: JSON.stringify({
        intentId,
        signatures: {
          origin: originSignatures,
          destination: destinationSignature,
        },
      }),
    });

    const { intentId: submittedId } = await submitRes.json();
    ```

    If submit returns 404, the quote TTL elapsed — re-quote and re-sign.
  </Step>

  <Step title="Poll for completion">
    Track execution status using the `intentId`:

    ```ts theme={null}
    async function pollStatus(intentId: string) {
      while (true) {
        const res = await fetch(`${BASE_URL}/intents/${intentId}`, { headers });
        const { status } = await res.json();

        if (["COMPLETED", "FAILED", "EXPIRED"].includes(status)) {
          console.log("Final status:", status);
          return status;
        }

        await new Promise((resolve) => setTimeout(resolve, 2000));
      }
    }

    await pollStatus(submittedId);
    ```

    Once execution finishes you'll see the terminal status:

    ```txt theme={null}
    Final status: COMPLETED
    ```

    Typical execution time is under 2 seconds. See [Tracking intents](/intents/guides/tracking-intents) for the full lifecycle.
  </Step>
</Steps>

Your tokens moved across chains from a single set of signatures — no bridge, no manual settlement.

## Next steps

<CardGroup cols={3}>
  <Card title="Getting a Quote" icon="route" href="./guides/getting-a-quote">
    Advanced quote options: sponsorship, source chain filtering, destination executions.
  </Card>

  <Card title="Token Requirements" icon="list-check" href="./guides/token-requirements">
    Full details on approvals and ETH wrapping.
  </Card>

  <Card title="Error Handling" icon="triangle-alert" href="./guides/error-handling">
    Common Orchestrator errors and how to fix them.
  </Card>
</CardGroup>
