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

# Quickstart

> Accept your first x402 payment in minutes

This guide takes you from zero to accepting USDC payments through Meridian on
any supported chain. The only dependency is `viem` — your server returns an
x402 challenge, the buyer signs a payment authorization off-chain, and your
server settles it through Meridian's API. No transaction is submitted by the
buyer and no gas is paid by either side.

```text theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
1. Your server returns 402 + paymentRequirements
2. Buyer signs an EIP-3009 authorization (off-chain, gasless)
3. Buyer sends the signed payload to your server
4. Your server settles via Meridian POST /v1/settle
5. Meridian's facilitator executes the transfer on-chain
```

The code below is identical on every chain whose payment token supports
EIP-3009 (Base, Optimism, Arbitrum, Polygon, Ink, and more) — only the network
constants change. For chains without an EIP-3009 token (MegaETH, BSC, BOT
chain, Tempo), the buyer signs a Permit2 witness instead; see the
[non-EIP-3009 guide](/ai-skills/non-eip-3009).

<Steps>
  <Step title="Create an API key">
    Sign in with your wallet at [mrdn.finance/dev/api-keys](https://mrdn.finance/dev/api-keys) and create a key. Use a `pk_test_` key for development and a `pk_` key for production. Store it server-side:

    ```bash theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
    MERIDIAN_API_KEY=pk_test_...
    ```

    See [API Keys](/essentials/api-keys) for details.
  </Step>

  <Step title="Set your recipient wallet">
    In your organization settings, configure the wallet that receives settled
    funds. The recipient is tied to your API key — in the payment requirements,
    `payTo` always stays pointed at the Meridian facilitator contract, never at
    your own wallet.
  </Step>

  <Step title="Pick a network">
    Take the payment token and facilitator address for your chain from
    [Supported Networks](/api-reference/supported-networks) and keep them in one
    config object — everything below reads from it:

    ```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
    // Example: Base mainnet. Swap in any supported network's values.
    const NETWORK = {
      id: "base",
      chainId: 8453,
      token: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC
      tokenName: "USD Coin", // EIP-712 domain name of the token
      tokenVersion: "2",     // EIP-712 domain version of the token
      facilitator: "0x8E7769D440b3460b92159Dd9C6D17302b036e2d6",
    };
    ```

    For development, use a testnet (`pk_test_` key), e.g. Base Sepolia:
    `chainId: 84532`, token `0x036CbD53842c5426634e7929541eC2318f3dCF7e`
    (`tokenName: "USDC"`), facilitator
    `0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A`. Test USDC is available from
    [Circle's faucet](https://faucet.circle.com).

    A minimal Base Sepolia test should use:

    * `MERIDIAN_API_KEY=pk_test_...`
    * a buyer wallet connected to Base Sepolia (`84532`)
    * Base Sepolia USDC from Circle's faucet
    * `paymentRequirements.network: "base-sepolia"`
    * `paymentRequirements.payTo: "0x8e633dBf31adCc7D41BE3e95B7c8DD3526B5235A"`
  </Step>

  <Step title="Return a 402 challenge from your server">
    When an unpaid request hits your paid endpoint, respond with HTTP 402 and
    the payment requirements:

    ```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
    const paymentRequirements = {
      scheme: "exact",
      network: NETWORK.id,
      asset: NETWORK.token,
      payTo: NETWORK.facilitator,
      maxAmountRequired: "10000", // $0.01 in USDC base units (6 decimals)
      resource: "https://your-api.example.com/api/premium",
      description: "Access to premium content",
      mimeType: "application/json",
      maxTimeoutSeconds: 300,
      extra: { name: NETWORK.tokenName, version: NETWORK.tokenVersion },
    };

    return Response.json(
      { x402Version: 1, accepts: [paymentRequirements] },
      { status: 402 },
    );
    ```

    To accept payments from more than one chain, return one requirement per
    source chain in `accepts` — see [Payment
    Types](/api-reference/payment-types/overview).
  </Step>

  <Step title="Sign the payment in the buyer's browser">
    The buyer signs an EIP-3009 `TransferWithAuthorization` as typed data —
    no approval, no transaction:

    ```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
    import { bytesToHex } from "viem";

    const now = BigInt(Math.floor(Date.now() / 1000));
    const authorization = {
      from: account.address,
      to: paymentRequirements.payTo as `0x${string}`,
      value: BigInt(paymentRequirements.maxAmountRequired),
      validAfter: 0n,
      validBefore: now + BigInt(paymentRequirements.maxTimeoutSeconds),
      nonce: bytesToHex(crypto.getRandomValues(new Uint8Array(32))),
    };

    const signature = await walletClient.signTypedData({
      account,
      domain: {
        name: paymentRequirements.extra.name,
        version: paymentRequirements.extra.version,
        chainId: NETWORK.chainId,
        verifyingContract: paymentRequirements.asset as `0x${string}`,
      },
      types: {
        TransferWithAuthorization: [
          { name: "from", type: "address" },
          { name: "to", type: "address" },
          { name: "value", type: "uint256" },
          { name: "validAfter", type: "uint256" },
          { name: "validBefore", type: "uint256" },
          { name: "nonce", type: "bytes32" },
        ],
      },
      primaryType: "TransferWithAuthorization",
      message: authorization,
    });
    ```

    Then send the payload to your server (bigints as decimal strings):

    ```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
    const paymentPayload = {
      x402Version: 1,
      scheme: "exact",
      network: paymentRequirements.network,
      payload: {
        signature,
        authorization: {
          from: authorization.from,
          to: authorization.to,
          value: authorization.value.toString(),
          validAfter: authorization.validAfter.toString(),
          validBefore: authorization.validBefore.toString(),
          nonce: authorization.nonce,
        },
      },
    };
    ```
  </Step>

  <Step title="Settle server-side">
    Your server settles the payment through Meridian, then serves the paid
    response. Never expose the API key to the browser, and do not trust
    buyer-supplied pricing, recipient, token, or network values. Rebuild or
    look up the matching `paymentRequirements` on the server before settlement:

    ```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
    if (paymentPayload.network !== paymentRequirements.network) {
      throw new Error("Payment network does not match the server requirement");
    }

    if (
      paymentPayload.payload.authorization.to.toLowerCase() !==
      paymentRequirements.payTo.toLowerCase()
    ) {
      throw new Error("Payment must authorize the Meridian facilitator");
    }

    if (
      paymentPayload.payload.authorization.value !==
      paymentRequirements.maxAmountRequired
    ) {
      throw new Error("Payment amount does not match the server price");
    }
    ```

    ```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
    const response = await fetch("https://api.mrdn.finance/v1/settle", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.MERIDIAN_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ paymentPayload, paymentRequirements }),
    });

    const result = await response.json();
    if (!response.ok || !result.success) {
      throw new Error(result.errorReason ?? "Settlement failed");
    }
    ```
  </Step>

  <Step title="Verify in Command Centre">
    Open [Command Centre](/command-centre/transactions) to see the payment, its
    settlement status, and your balance.
  </Step>
</Steps>

## Next steps

<CardGroup cols={2}>
  <Card title="Manual Integration" icon="code" href="/api-reference/manual-integration">
    The full client-side flow: multi-chain challenges, requirement selection,
    and error handling.
  </Card>

  <Card title="Payment Types" icon="layer-group" href="/api-reference/payment-types/overview">
    EIP-3009, Permit2, and Circle Gateway — and when to use which.
  </Card>

  <Card title="Supported Networks" icon="globe" href="/api-reference/supported-networks">
    Chains, tokens, and contract addresses.
  </Card>

  <Card title="AI Skills" icon="robot" href="/ai-skills/eip-3009">
    Self-contained integration instructions for AI coding agents.
  </Card>
</CardGroup>
