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

# Quickstart

> Get a deposit address from a quote, accept a payment, and receive the settlement — end to end.

This guide takes you from zero to a working cross-chain payment: get a deposit address from a quote,
let a user deposit, and get notified when funds are delivered.

## Prerequisites

* An **API key** (UUID) from the Mayan team — see [Getting access](/payment-service/overview#getting-access).
* The base URL: `https://mps-api.mayan.finance`.

All authenticated requests send your key in the `x-api-key` header.

## 1. Get a deposit address from a quote

Request a [Mayan quote](/integration/quote-api#mps-deposit-address-mpsdeposit) with `mpsDeposit: true`
and a `destinationAddress` (where the funds settle), then read `mpsDepositAddress` from an eligible
quote. It's a deterministic address on the quote's **source** chain — in the example below the payer
funds on Arbitrum and the merchant receives USDC on Base. The address is minted under your API key, so
MPS must be enabled for it.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST "https://price-api.mayan.finance/v3/quote?apiKey=$MAYAN_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "amountIn64": "2000000",
      "fromChain": "arbitrum",
      "fromToken": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
      "toChain": "base",
      "toToken": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
      "slippageBps": "auto",
      "destinationAddress": "0xEab4Fb2De5a05Ba392eB2614bd2293592d455A4f",
      "mpsDeposit": true,
      "swift": true, "mctp": true, "fastMctp": true, "monoChain": true, "fullList": true,
      "sdkVersion": "15_0_0"
    }'
  ```

  ```typescript TypeScript theme={null}
  const PRICE_URL = "https://price-api.mayan.finance";
  const API_KEY = process.env.MAYAN_API_KEY!;

  const res = await fetch(`${PRICE_URL}/v3/quote?apiKey=${API_KEY}`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      amountIn64: "2000000", // ~2 USDC (6 decimals)
      fromChain: "arbitrum",
      fromToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC on Arbitrum (source)
      toChain: "base",
      toToken: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", // USDC on Base (destination)
      slippageBps: "auto",
      destinationAddress: "0xEab4Fb2De5a05Ba392eB2614bd2293592d455A4f",
      mpsDeposit: true,
      swift: true, mctp: true, fastMctp: true, monoChain: true, fullList: true,
      sdkVersion: "15_0_0",
    }),
  });

  const { quotes } = await res.json();
  // All eligible quotes carry the same address; take the first non-null one.
  const depositAddress = quotes.map((q) => q.mpsDepositAddress).find(Boolean);
  console.log(depositAddress); // address on Arbitrum (the source chain)
  ```

  ```python Python theme={null}
  import os, requests

  PRICE_URL = "https://price-api.mayan.finance"
  API_KEY = os.environ["MAYAN_API_KEY"]

  res = requests.post(
      f"{PRICE_URL}/v3/quote",
      params={"apiKey": API_KEY},
      json={
          "amountIn64": "2000000",  # ~2 USDC (6 decimals)
          "fromChain": "arbitrum",
          "fromToken": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",  # USDC on Arbitrum (source)
          "toChain": "base",
          "toToken": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",  # USDC on Base (destination)
          "slippageBps": "auto",
          "destinationAddress": "0xEab4Fb2De5a05Ba392eB2614bd2293592d455A4f",
          "mpsDeposit": True,
          "swift": True, "mctp": True, "fastMctp": True, "monoChain": True, "fullList": True,
          "sdkVersion": "15_0_0",
      },
  )
  quotes = res.json()["quotes"]
  deposit_address = next((q["mpsDepositAddress"] for q in quotes if q.get("mpsDepositAddress")), None)
  print(deposit_address)  # address on Arbitrum (the source chain)
  ```
</CodeGroup>

**Response** (abridged — one quote shown):

```json theme={null}
{
  "quotes": [
    {
      "type": "SWIFT",
      "mpsDepositAddress": "0xafb7e27de13e435a7f1d1a21d33b26edff17bc8b",
      "mpsIntegratorId": "12",
      "mpsUserId": "0x3a1f…",
      "expectedAmountOut": 1.999
    }
  ]
}
```

Read `mpsDepositAddress` from any eligible quote (`SWIFT`, `MONO_CHAIN`, or a direct `FAST_MCTP`) —
they all share the same address. Quotes that aren't eligible return `mpsDepositAddress: null`.

<Info>
  The address is deterministic and permanent for a given recipient + `mpsUserId` — requesting another
  quote returns the same address, so it's safe to call on every checkout. It's `null` unless MPS is
  enabled for your key, `destinationAddress` is set, and the amount clears the token's
  [minimum](/payment-service/api-reference#source-config).
</Info>

## 2. Let the user deposit

Show `mpsDepositAddress` to your user and have them send the input token on the **source** chain
(Arbitrum, in the example above). MPS detects the deposit and settles it to your destination token
automatically — no signature, no transaction to build.

* A quote from an **EVM** source chain returns an EVM address (valid on that chain).
* A quote from **Solana** returns a Solana vault for SPL / Token-2022 deposits (USDC, USDT, WETH).

<Note>
  Whitelisted tokens — the native coin and **USDC** on EVM, and **USDC / USDT / WETH** on Solana
  — are detected automatically. For any other token, trigger indexing yourself with
  [`POST /request-index`](/payment-service/api-reference#request-index). See
  [Supported tokens](/payment-service/overview#supported-tokens).
</Note>

## 3. Receive the settlement

You have two ways to track a payment — use whichever fits your stack:

<Tabs>
  <Tab title="WebSocket (real-time)">
    The stream is served over **Socket.IO** (`bun add socket.io-client`). Connect and receive
    `deposit_detected` and `status_changed` events as they happen.

    ```typescript theme={null}
    import * as io from "socket.io-client";

    const socket = io.connect("https://mps-api.mayan.finance", {
      transports: ["websocket"],
      auth: { apiKey: API_KEY },
    });

    socket.on("event", (evt) => {
      if (evt.type === "status_changed" && evt.data.terminal) {
        // completed = success, dead = gave up
        console.log(evt.data.status, evt.data.queueItemId, evt.data.txHash);
      }
    });
    ```

    See [Events](/payment-service/events) for the full payloads and reconnection/replay guidance.
  </Tab>

  <Tab title="Polling">
    Poll [`GET /swaps`](/payment-service/api-reference#list-swaps) and read each item's `status`,
    `terminal`, and `error`. A payment is delivered when `status` is `completed`.

    ```bash theme={null}
    curl "https://mps-api.mayan.finance/swaps?limit=20" -H "x-api-key: $MAYAN_API_KEY"
    ```
  </Tab>
</Tabs>

A payment is **done** when a swap reaches `status: "completed"` (`terminal: true`, `category: "success"`).
Most transfers settle in seconds. See [Swap Statuses](/payment-service/statuses) for the full lifecycle.

## Full example: e-commerce checkout

Map your order ID to a stable `mpsUserId` so the same order always maps to the same deposit address,
then watch for the terminal event.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { createHash } from "node:crypto";

  const PRICE_URL = "https://price-api.mayan.finance";
  const API_KEY = process.env.MAYAN_API_KEY!;
  const USDC_ARB = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831"; // payer deposits USDC on Arbitrum
  const USDC_BASE = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"; // merchant receives USDC on Base

  // Map any order id to a stable 20-byte mpsUserId (sha256 → first 20 bytes).
  const orderIdToUserId = (orderId: string) =>
    "0x" + createHash("sha256").update(orderId).digest("hex").slice(0, 40);

  export async function createPaymentAddress(orderId: string, merchantWallet: string) {
    const res = await fetch(`${PRICE_URL}/v3/quote?apiKey=${API_KEY}`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        amountIn64: "2000000", // ~2 USDC
        fromChain: "arbitrum",
        fromToken: USDC_ARB,
        toChain: "base",
        toToken: USDC_BASE,
        slippageBps: "auto",
        destinationAddress: merchantWallet,
        mpsDeposit: true,
        mpsUserId: orderIdToUserId(orderId),
        swift: true, mctp: true, fastMctp: true, monoChain: true, fullList: true,
        sdkVersion: "15_0_0",
      }),
    });
    const { quotes } = await res.json();
    const depositAddress = quotes.map((q: any) => q.mpsDepositAddress).find(Boolean);
    if (!depositAddress) throw new Error("MPS deposit address unavailable — enable MPS on your key and check the token minimum.");
    return { depositAddress };
  }
  ```

  ```python Python theme={null}
  import os, hashlib, requests

  PRICE_URL = "https://price-api.mayan.finance"
  API_KEY = os.environ["MAYAN_API_KEY"]
  USDC_ARB = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831"   # payer deposits USDC on Arbitrum
  USDC_BASE = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"  # merchant receives USDC on Base

  # Map any order id to a stable 20-byte mpsUserId (sha256 → first 20 bytes).
  def order_id_to_user_id(order_id: str) -> str:
      return "0x" + hashlib.sha256(order_id.encode()).hexdigest()[:40]

  def create_payment_address(order_id: str, merchant_wallet: str):
      res = requests.post(
          f"{PRICE_URL}/v3/quote",
          params={"apiKey": API_KEY},
          json={
              "amountIn64": "2000000",  # ~2 USDC
              "fromChain": "arbitrum",
              "fromToken": USDC_ARB,
              "toChain": "base",
              "toToken": USDC_BASE,
              "slippageBps": "auto",
              "destinationAddress": merchant_wallet,
              "mpsDeposit": True,
              "mpsUserId": order_id_to_user_id(order_id),
              "swift": True, "mctp": True, "fastMctp": True, "monoChain": True, "fullList": True,
              "sdkVersion": "15_0_0",
          },
      )
      quotes = res.json()["quotes"]
      deposit_address = next((q["mpsDepositAddress"] for q in quotes if q.get("mpsDepositAddress")), None)
      if not deposit_address:
          raise RuntimeError("MPS deposit address unavailable")
      return {"depositAddress": deposit_address}
  ```
</CodeGroup>

Show the deposit address to the customer. They send \~2 USDC on Arbitrum; the merchant receives USDC on
Base, and you get a `status_changed` → `completed` event to fulfil the order.

<Note>
  Deposits below the per-chain minimum USD value are detected but not settled. You still receive a
  `deposit_detected` event with `queued: false` and `ignoredReason: "below_min"`, so it's never a
  silent gap.
</Note>

## Watch a payment end-to-end

Get an address from a quote (step 1), then subscribe to the event stream (**Socket.IO**) and wait
until the settlement is terminal. This is the snippet most integrations start from — it prints each
event and resolves on `completed` (success) or `dead` (gave up).

<CodeGroup>
  ```typescript TypeScript theme={null}
  import * as io from "socket.io-client"; // bun add socket.io-client

  const MPS_URL = "https://mps-api.mayan.finance";
  const API_KEY = process.env.MAYAN_API_KEY!;

  function watchSettlement(walletAddress: string) {
    const target = walletAddress.toLowerCase();
    return new Promise<{ status: string; txHash: string | null }>((resolve) => {
      const socket = io.connect(MPS_URL, { transports: ["websocket"], auth: { apiKey: API_KEY } });
      socket.on("event", (evt) => {
        if (evt.type === "deposit_detected") console.log("deposit detected, queued:", evt.data.queued);
        if (evt.type === "status_changed" && evt.data.walletAddress.toLowerCase() === target) {
          console.log("→", evt.data.status, evt.data.error?.code ?? "");
          if (evt.data.terminal) {
            socket.disconnect();
            resolve({ status: evt.data.status, txHash: evt.data.txHash });
          }
        }
      });
    });
  }

  // const { depositAddress } = await createPaymentAddress(orderId, merchantWallet); // from the checkout example
  const result = await watchSettlement(depositAddress);
  console.log(result.status === "completed" ? `✅ delivered: ${result.txHash}` : `❌ ${result.status}`);
  ```

  ```python Python theme={null}
  # pip install "python-socketio[client]"
  import os, socketio

  MPS_URL = "https://mps-api.mayan.finance"
  API_KEY = os.environ["MAYAN_API_KEY"]

  def watch_settlement(wallet_address: str):
      target = wallet_address.lower()
      sio = socketio.Client()
      result = {}

      @sio.on("event")
      def on_event(evt):
          if evt["type"] == "deposit_detected":
              print("deposit detected, queued:", evt["data"]["queued"])
          if evt["type"] == "status_changed" and evt["data"]["walletAddress"].lower() == target:
              print("→", evt["data"]["status"], (evt["data"].get("error") or {}).get("code", ""))
              if evt["data"]["terminal"]:
                  result.update(status=evt["data"]["status"], txHash=evt["data"].get("txHash"))
                  sio.disconnect()

      sio.connect(MPS_URL, transports=["websocket"], auth={"apiKey": API_KEY})
      sio.wait()
      return result

  # deposit_address = create_payment_address(order_id, merchant_wallet)["depositAddress"]  # from checkout
  result = watch_settlement(deposit_address)
  print("✅" if result["status"] == "completed" else "❌", result["status"], result.get("txHash"))
  ```
</CodeGroup>

<Tip>
  Full runnable examples (deposit **\~2 USDC** on Base or Solana → receive USDC on Arbitrum) are on the
  [Examples](/payment-service/examples) page.
</Tip>
