Docs/Getting started/Quickstart

Quickstart

Go from zero to a paid, reconciled order with email or wallet login, no password, and simple merchant onboarding. Save a payout wallet, open a checkout session, and confirm payment with a webhook.

No customer account required
Customers never need an Apa account to pay a hosted checkout. Merchants sign in with email for multiple payout wallets, or with MetaMask/Phantom for a single-wallet account where the signed wallet becomes the payout wallet. Integrate the API only when you need to.
  1. 1

    Choose email or wallet login

    Use email for a full merchant account with multiple payout wallets. Use MetaMask or Phantom wallet login for a single-wallet account where the signed wallet becomes the payout wallet. Just want to get paid? Create and share a payment link right away, no code and no API key needed.

  2. 2

    Create or confirm your payout wallet

    Email accounts can save multiple payout wallets. Wallet-login accounts automatically use the signed wallet and cannot add another. Links and sessions reference one payout wallet by id, so a leaked key can never redirect funds. Read them back over the API:

    curl https://apa.app/v1/payout-wallets \
      -H "Authorization: Bearer sk_live_…"
    
    {
      "data": [ { "id": "pp_123", "asset": "USDC", "network": "solana", "is_default": true } ],
      "pagination": { "cursor": null, "has_more": false, "total": 1 }
    }
  3. 3

    Get an API key (to integrate)

    Building your own checkout? Generate a live key from the dashboard and keep the secret server-side. Skip this entirely if you're only using payment links.

  4. 4

    Create a checkout session

    From your backend, create a session for the order, referencing the payout wallet.

    curl https://apa.app/v1/checkout/sessions \
      -H "Authorization: Bearer sk_live_…" \
      -H "Content-Type: application/json" \
      -d '{
        "payout_wallet_id": "pp_123",
        "amount": "100.00",
        "currency": "USD",
        "order_id": "ord_1042"
      }'
    
    {
      "data": {
        "id": "cs_123",
        "object": "checkout_session",
        "status": "created",
        "payment_link_id": null,
        "checkout_url": "https://apa.app/checkout/cs_123"
      },
      "request_id": "req_3aF2k9Lm"
    }
  5. 5

    Redirect the customer

    Send the customer to the checkout_url. They pick any Safe-List asset and pay from their own wallet — Apa routes whenever the asset or network differs from your payout wallet; exact asset/network matches settle free.

  6. 6

    Listen for the webhook

    Apa POSTs a signed payment.paid event to your endpoint once funds settle.

    // Express — express.raw() preserves the exact bytes for signing
    app.post("/api/apa/webhook", express.raw({ type: "*/*" }), (req, res) => {
      // Apa-Signature: t=<timestamp>,v1=<hex>
      // v1 = HMAC_SHA256(secret, `${t}.${rawBody}`)
      const parts = Object.fromEntries(
        (req.headers["apa-signature"] ?? "").split(",").map((p) => p.split("="))
      );
      const expected = crypto
        .createHmac("sha256", process.env.APA_WEBHOOK_SECRET) // whsec_…
        .update(`${parts.t}.${req.body}`)
        .digest("hex");
      if (!parts.v1 || !crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected))) {
        return res.sendStatus(400);
      }
    
      const event = JSON.parse(req.body);
      if (event.type === "payment.paid") markOrderPaid(event.data.order_id);
      res.sendStatus(200);
    });
  7. 7

    Reconcile the order

    Mark the order paid and fulfil. The settled funds are already in your wallet — Apa never held them.

Test before you go live
Use signed webhook tests now, and use test-mode checkout sessions as they become available, to rehearse the flow before moving real funds. See Testing.