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

# Widget Quickstart

> Embed Maven in your chatbot and collect your first payment in 10 minutes

<Steps>
  <Step title="Set up your project">
    If you haven't already: [create an account](https://app.trymaven.com), create an app, [connect a gateway](/quickstart), and grab a test API key (`mvn_test_*`). Same setup as voice — gateway connections and API keys are shared.
  </Step>

  <Step title="Customize the widget (optional)">
    Open your app → **Chat Payments** tab in the dashboard. Set your colors, labels, and which fields to show (cardholder name, billing ZIP). Hit **Save**.

    These settings apply automatically to every session from this project. You can still override per-session via the API, but most merchants just set it once.
  </Step>

  <Step title="Server — create a session">
    When your chatbot is ready to collect payment, your backend calls:

    <CodeGroup>
      ```bash cURL theme={"dark"}
      curl https://api.trymaven.com/v1/widget-sessions \
        -H "Authorization: Bearer $MAVEN_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "project": "my-app",
          "gateway": "stripe",
          "amount_cents": 10000,
          "description": "Order #1234"
        }'
      ```

      ```javascript Node.js theme={"dark"}
      const res = await fetch("https://api.trymaven.com/v1/widget-sessions", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.MAVEN_API_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          project: "my-app",
          gateway: "stripe",
          amount_cents: 10000,
          description: "Order #1234",
        }),
      });
      const { session_id } = await res.json();
      ```

      ```python Python theme={"dark"}
      import os, requests

      r = requests.post(
          "https://api.trymaven.com/v1/widget-sessions",
          headers={"Authorization": f"Bearer {os.environ['MAVEN_API_KEY']}"},
          json={
              "project": "my-app",
              "gateway": "stripe",
              "amount_cents": 10000,
              "description": "Order #1234",
          },
      )
      session_id = r.json()["session_id"]
      ```
    </CodeGroup>

    Response:

    ```json theme={"dark"}
    {
      "session_id": "8a7f3d2b-...",
      "widget_url": "https://api.trymaven.com/widget/8a7f3d2b-...",
      "expires_at": "2026-04-14T19:15:00Z"
    }
    ```

    Hand the `session_id` to your frontend — via WebSocket message, HTTP response, whatever your chatbot uses.
  </Step>

  <Step title="Frontend — load the SDK once">
    Add this once to your page, ideally before your chatbot renders:

    ```html theme={"dark"}
    <script src="https://api.trymaven.com/widget/v1/widget.js"></script>
    ```

    This exposes a global `Maven` object with one method: `createPayment()`.
  </Step>

  <Step title="Frontend — mount the widget when ready">
    When your chatbot receives the `session_id`, mount the widget into a `<div>` inside the chat message:

    ```javascript theme={"dark"}
    const payment = Maven.createPayment({
      sessionId: "<SESSION_FROM_YOUR_SERVER>",
      onSuccess: (result) => {
        // result = { transaction_id, card_brand, card_last4, amount_cents, currency, status }

        // Typical things to do here (all optional — Maven already shows
        // a green "Payment confirmed" screen inside the iframe):
        chatbot.sendMessage(`Paid — ${result.card_brand} •• ${result.card_last4}`);
        await db.orders.markPaid(orderId, result.transaction_id);
        // or: window.location = `/thanks?txn=${result.transaction_id}`;
      },
      onFailure: (error) => {
        // error = { error_code, error_message }
        chatbot.sendMessage(`Payment didn't go through: ${error.error_message}`);
      },
    });

    payment.mount("#chat-payment-slot");
    ```

    The iframe appears inline. Customer types their card, hits pay. Your callbacks fire — `onSuccess` and `onFailure` are **hooks into your app** so you can continue the chatbot conversation, update your database, or redirect the customer. The widget itself handles the card form, charging, and the success/failure UI inside the iframe.
  </Step>

  <Step title="Handle webhook (recommended)">
    Configure a webhook URL on the project. Maven fires the same `payment-success` / `payment-failed` event for widget payments as it does for voice — with an additional `source: "chat"` field.

    See [Webhooks](/integrations/webhooks) for the full payload.
  </Step>
</Steps>

## Common errors

`POST /v1/widget-sessions` can return these if something's off in your setup:

| Status | Code                    | What it means                                  | Fix                                                                                                                    |
| ------ | ----------------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `401`  | —                       | Bad or missing API key                         | Check the `Authorization: Bearer mvn_test_...` header                                                                  |
| `404`  | `project_not_found`     | Project slug doesn't exist in your org         | Double-check the `project` value matches a slug in your dashboard                                                      |
| `422`  | `gateway_not_connected` | Gateway isn't connected for this env           | Connect the gateway in the dashboard (Gateways tab). Test keys need test credentials; live keys need live credentials. |
| `422`  | `invalid_amount`        | `amount_cents` is 0 or negative in charge mode | Pass `amount_cents > 0`, or use `mode: "tokenize"` to save a card without charging                                     |
| `429`  | —                       | Rate limited                                   | Back off; defaults are 20 session creates/minute per IP                                                                |

If the widget mounts but doesn't render, check your browser console. The usual culprit is `Maven is not defined` — that means the `<script src="…">` tag didn't load. Hard-refresh and check the URL is reachable.

## Test cards

Use these with your `mvn_test_*` key against a gateway in sandbox mode:

| Outcome  | Number                | CVV          | Expiry          |
| -------- | --------------------- | ------------ | --------------- |
| Success  | `4242 4242 4242 4242` | Any 3 digits | Any future date |
| Declined | `4000 0000 0000 0002` | Any 3 digits | Any future date |
| Amex     | `3782 822463 10005`   | Any 4 digits | Any future date |

## Full example — vanilla JS chatbot

```html theme={"dark"}
<!doctype html>
<html>
<body>
  <div id="chat">
    <!-- messages appended here -->
  </div>

  <script src="https://api.trymaven.com/widget/v1/widget.js"></script>
  <script>
    async function collectPayment(amount) {
      // 1. Ask your server to create the session
      const { sessionId } = await fetch("/api/create-payment-session", {
        method: "POST",
        body: JSON.stringify({ amount }),
      }).then(r => r.json());

      // 2. Add a chat bubble that will host the widget
      const bubble = document.createElement("div");
      bubble.className = "chat-message bot";
      bubble.innerHTML = `<div class="slot"></div>`;
      document.getElementById("chat").appendChild(bubble);

      // 3. Mount the widget into that bubble
      Maven.createPayment({
        sessionId,
        onSuccess: (result) => {
          addBotMessage(`Paid — ${result.card_brand} •• ${result.card_last4}`);
        },
        onFailure: (error) => {
          addBotMessage(`Payment failed: ${error.error_message}`);
        },
      }).mount(bubble.querySelector(".slot"));
    }
  </script>
</body>
</html>
```

## Next

<CardGroup cols={2}>
  <Card title="SDK Reference" icon="code" href="/widget/sdk">
    Complete options, callbacks, and controller methods.
  </Card>

  <Card title="Customization" icon="palette" href="/widget/customization">
    Theme, labels, fields, and sizing options.
  </Card>
</CardGroup>
