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

# Checkout Quickstart

> Accept your first card payment.

<Info>
  Slash Payment Processing is in beta and not enabled for all accounts. Contact
  [support@joinslash.com](mailto:support@joinslash.com) to get access.
</Info>

## 1. Create a session

Call `POST /checkout-session` with your API key. `amount` is in cents;
`currency` supports `usd`. The `X-Idempotency-Key` header is required —
retrying with the same key returns the existing session instead of
creating a duplicate.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.slash.com/checkout-session \
    -X POST \
    -H "X-API-Key: $SLASH_API_KEY" \
    -H "X-Idempotency-Key: order_1234" \
    -H "Content-Type: application/json" \
    -d '{ "amount": 5000, "currency": "usd", "customMetadata": { "orderId": "ord_1234" } }'
  ```

  ```ts Node.js theme={null}
  const res = await fetch("https://api.slash.com/checkout-session", {
    method: "POST",
    headers: {
      "X-API-Key": process.env.SLASH_API_KEY,
      "X-Idempotency-Key": "order_1234",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      amount: 5000,
      currency: "usd",
      customMetadata: { orderId: "ord_1234" },
      // Optional: card stays enabled; wallet fields default to true.
      config: { paymentMethods: { applePay: true, googlePay: true } },
    }),
  });
  const { checkoutSession } = await res.json();
  ```
</CodeGroup>

The response includes a hosted `url`:

```json theme={null}
{
  "checkoutSession": {
    "id": "acq_checkout_...",
    "url": "https://app.slash.com/checkout/acq_checkout_...",
    "status": "open",
    "amount": 5000,
    "currency": "usd"
  }
}
```

## 2. Embed it

Install the SDK and mount the `url`. Point `fetchUrl` at your own backend
endpoint that creates the session, so your API key stays server-side.

```bash theme={null}
npm install @slashfi/checkout-js
```

```ts theme={null}
import { loadSlashCheckout } from "@slashfi/checkout-js";

const slashCheckout = await loadSlashCheckout();

const checkout = await slashCheckout.createEmbeddedCheckoutPage({
  fetchUrl: () =>
    fetch("/create-checkout-session", { method: "POST" })
      .then((r) => r.json())
      .then((s) => s.url),
  onComplete: () => showConfirmation(),
});

checkout.mount("#checkout");
```

## 3. Fulfill on the webhook

Fulfill the order when `checkout_session.completed` arrives. Your
`customMetadata` is echoed back on the event body:

```json theme={null}
{
  "event": "checkout_session.completed",
  "eventId": "public_webhook_notification_...",
  "entityId": "acq_checkout_...",
  "eventTimestamp": "2026-07-22T06:00:00.000Z",
  "customMetadata": { "orderId": "ord_1234" }
}
```

Anyone who discovers your webhook URL can POST to it, so **verify the
`slash-webhook-signature` header before fulfilling**. Every delivery is
signed over the raw request body with Slash's
[public RSA key](/api-reference/public-rsa-key) — see
[webhook signing](/api-reference/webhook-overview) for details:

```ts theme={null}
import crypto from "node:crypto";

// From /api-reference/public-rsa-key
const SLASH_WEBHOOK_PUBLIC_KEY = process.env.SLASH_WEBHOOK_PUBLIC_KEY;

// The signature covers the raw bytes, so read the body unparsed.
app.post("/webhooks/slash", express.text({ type: "*/*" }), (req, res) => {
  const signature = req.headers["slash-webhook-signature"];
  const isAuthentic =
    typeof signature === "string" &&
    crypto.verify(
      "sha256",
      Buffer.from(req.body),
      SLASH_WEBHOOK_PUBLIC_KEY,
      Buffer.from(signature, "base64")
    );
  if (!isAuthentic) return res.sendStatus(400);

  const event = JSON.parse(req.body);
  if (event.event === "checkout_session.completed") {
    fulfillOrder(event.customMetadata.orderId);
  }
  res.sendStatus(200);
});
```

Deliveries that aren't acknowledged with a 2xx are retried a limited
number of times before the notification is marked failed, so make
fulfillment idempotent — dedupe on `eventId` (or your own
`customMetadata` key) — and don't rely on webhooks alone:
`GET /checkout-session/{id}` returning `status: "complete"` is
equivalent to the webhook and catches any missed deliveries.

Full SDK options are in the [checkout-js reference](/docs/checkout/embedded-checkout).
