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

# Funnel Pages

> Page types, capabilities, and three ways to create them: native, custom HTML, or Plugin SDK

# Funnel Pages

A funnel is a sequence of **pages** (steps). Each page has a `type` that determines its capabilities — whether it can accept payments, show upsells, fire conversion events, etc.

***

## Page Types

Every node in a funnel has a `type`. Here are all the available types and what they unlock:

| Type                     | Purpose                        | Payment Form | Order Bumps | Upsell Offers | Can be Entry | Conversion |
| ------------------------ | ------------------------------ | ------------ | ----------- | ------------- | ------------ | ---------- |
| `checkout`               | Payment page                   | Yes          | Yes         | —             | Yes          | —          |
| `thankyou`               | Order confirmation             | —            | —           | —             | —            | Yes        |
| `offer`                  | Upsell / downsell / cross-sell | Optional     | —           | Yes           | —            | —          |
| `landing`                | Landing page / hero with CTA   | —            | —           | —             | Yes          | —          |
| `custom`                 | Any custom page                | Optional     | Optional    | Optional      | Yes          | —          |
| `error`                  | Error state page               | —            | —           | —             | —            | —          |
| `external`               | Redirect to external URL       | —            | —           | —             | Yes          | —          |
| `storefront_shopify`     | Shopify product page           | —            | —           | —             | Yes          | —          |
| `storefront_woocommerce` | WooCommerce product page       | —            | —           | —             | Yes          | —          |

### Why types matter

The step `type` controls what **stepConfig** options are available:

* **`checkout`** steps can have `paymentSetupConfig` (card, Apple Pay, Google Pay routing), `orderBumps`, and `payment` config
* **`offer`** steps can have `upsellOffers` for post-purchase offers
* **`thankyou`** steps are marked as the conversion endpoint — analytics events like `Purchase` fire here
* **`landing`** and **`custom`** steps are general-purpose — they have pixels and scripts but no payment-specific config

```ts theme={null}
// Checkout step — full payment config
{
  id: 'step_checkout',
  type: 'checkout',
  config: {
    stepConfig: {
      paymentSetupConfig: { card: { enabled: true, paymentFlowId: 'flow_xxx' } },
      orderBumps: { mode: 'custom', enabledOfferIds: ['offer_1'] },
      pixels: { facebook: [...] },
    },
  },
}

// Offer step — upsell config
{
  id: 'step_upsell',
  type: 'offer',
  config: {
    stepConfig: {
      upsellOffers: [{ offerId: 'offer_2', type: 'one_click' }],
      pixels: { facebook: [...] },
    },
  },
}

// Landing step — pixels and scripts only
{
  id: 'step_landing',
  type: 'landing',
  isEntry: true,
  config: {
    stepConfig: {
      pixels: { facebook: [...] },
      scripts: [{ name: 'Hotjar', content: '...', position: 'head-end', enabled: true }],
    },
  },
}
```

### Common funnel patterns

```
Landing → Checkout → Thank You                    (simple)
Landing → Checkout → Offer → Thank You            (with upsell)
Storefront (Shopify) → Checkout → Thank You       (Shopify integration)
Checkout → Offer (EU) / Offer (US) → Thank You    (geo-conditional)
Landing → Checkout → Offer → Offer → Thank You    (multi-offer)
```

<Tip>
  You can combine any types in any order. The only requirement is that a funnel with payments needs at least one `checkout` step, and a complete flow should end with a `thankyou` step for conversion tracking.
</Tip>

***

## Three Approaches

TagadaPay offers three ways to create funnel pages, depending on how much control you need:

|                 | Native Checkout              | Custom HTML                     | Plugin SDK                              |
| --------------- | ---------------------------- | ------------------------------- | --------------------------------------- |
| **Setup**       | Zero code — auto-injected    | Deploy raw HTML/CSS/JS          | Build a React or JS app                 |
| **Page types**  | `checkout` + `thankyou` only | Any type                        | Any type                                |
| **Control**     | TagadaPay handles the UI     | Full control, no framework      | Full control + SDK hooks                |
| **Best for**    | Getting live fast            | Quick prototypes, simple pages  | Production-grade branded experiences    |
| **Payments**    | Automatic                    | You wire up tokenization        | SDK handles tokenization, 3DS, payments |
| **Funnels**     | Automatic navigation         | Manual routing                  | SDK handles step navigation             |
| **Deployment**  | 2 steps                      | 4 steps (inline deploy via API) | CLI: `tgd deploy` from your repo        |
| **A/B testing** | No                           | Via API split config            | Via API or CLI                          |

<Tabs>
  <Tab title="Native Checkout">
    ## Native Checkout (Recommended)

    When you create a funnel with `checkout` and `thankyou` step types, TagadaPay **automatically injects** its built-in checkout UI. No plugin deployment, no HTML — just define the flow.

    ### Create and activate

    ```ts theme={null}
    import Tagada from '@tagadapay/node-sdk';
    const tagada = new Tagada('your-api-key');

    // Create funnel — native checkout is auto-injected
    const funnel = await tagada.funnels.create({
      storeId: 'store_...',
      config: {
        id: 'my-checkout',
        name: 'My Checkout',
        version: '1.0.0',
        nodes: [
          {
            id: 'step_checkout',
            name: 'Checkout',
            type: 'checkout',
            kind: 'step',
            isEntry: true,
            position: { x: 0, y: 0 },
            config: { path: '/checkout' },
          },
          {
            id: 'step_thankyou',
            name: 'Thank You',
            type: 'thankyou',
            kind: 'step',
            position: { x: 300, y: 0 },
            config: { path: '/thankyou' },
          },
        ],
        edges: [
          { id: 'e1', source: 'step_checkout', target: 'step_thankyou' },
        ],
      },
      isDefault: true,
    });

    // Activate — triggers routing, mounts pages to CDN
    const result = await tagada.funnels.update(funnel.id, {
      storeId: 'store_...',
      config: funnel.config,
    });

    const funnelCheckoutUrl = result.funnel.config.nodes
      .find(n => n.id === 'step_checkout').config.url;
    console.log('Funnel live at:', funnelCheckoutUrl);

    // Generate a checkout link with a pre-loaded cart
    const session = await tagada.checkout.createSession({
      storeId: 'store_...',
      items: [{ variantId: 'variant_...', quantity: 1 }],
      currency: 'USD',
      checkoutUrl: funnelCheckoutUrl,
    });
    console.log('Checkout link:', session.redirectUrl);
    ```

    <Info>
      No `pluginId` or `instanceId` needed. TagadaPay detects that `checkout` and `thankyou` nodes have no plugin assigned and injects the native checkout automatically.

      The `checkoutUrl` parameter in `createSession` is critical — it tells TagadaPay to redirect customers to your funnel's deployed page instead of the default CMS URL.
    </Info>

    ### What you get

    The native checkout includes:

    * Card payment form with real-time validation
    * Order summary with product details and pricing
    * Multi-currency support based on your store config
    * Mobile-responsive layout
    * Built-in thank you page with order confirmation

    For the full setup including processor, store, and product creation, see the [Merchant Quick Start](/developer-tools/node-sdk/merchant-quickstart).
  </Tab>

  <Tab title="Custom HTML">
    ## Custom HTML Checkout

    Deploy your own checkout and thank you pages as raw HTML. You have full control over the design, layout, and behavior.

    ### Step 1: Define your pages

    **Checkout page** — a styled payment form:

    ```html theme={null}
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Checkout</title>
      <style>
        * { box-sizing: border-box; margin: 0; padding: 0; }
        body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #f5f5f7; color: #1d1d1f; }
        .container { max-width: 480px; margin: 40px auto; padding: 0 20px; }
        .card { background: #fff; border-radius: 12px; padding: 32px; box-shadow: 0 2px 12px rgba(0,0,0,0.08); }
        h1 { font-size: 22px; font-weight: 600; margin-bottom: 24px; }
        .field { margin-bottom: 16px; }
        .field label { display: block; font-size: 13px; font-weight: 500; color: #6e6e73; margin-bottom: 6px; }
        .field input { width: 100%; padding: 12px; border: 1px solid #d2d2d7; border-radius: 8px; font-size: 15px; outline: none; transition: border-color 0.2s; }
        .field input:focus { border-color: #0071e3; box-shadow: 0 0 0 3px rgba(0,113,227,0.1); }
        .row { display: flex; gap: 12px; }
        .row .field { flex: 1; }
        .divider { height: 1px; background: #e5e5ea; margin: 24px 0; }
        .summary { display: flex; justify-content: space-between; font-size: 15px; margin-bottom: 8px; }
        .summary.total { font-weight: 600; font-size: 17px; margin-top: 12px; }
        .btn { width: 100%; padding: 14px; background: #0071e3; color: #fff; border: none; border-radius: 10px; font-size: 16px; font-weight: 600; cursor: pointer; margin-top: 24px; transition: background 0.2s; }
        .btn:hover { background: #0077ed; }
        .secure { text-align: center; font-size: 12px; color: #86868b; margin-top: 16px; }
      </style>
    </head>
    <body>
      <div class="container">
        <div class="card">
          <h1>Checkout</h1>
          <div class="field">
            <label>Email</label>
            <input type="email" placeholder="you@example.com">
          </div>
          <div class="field">
            <label>Card number</label>
            <input type="text" placeholder="4242 4242 4242 4242">
          </div>
          <div class="row">
            <div class="field"><label>Expiry</label><input type="text" placeholder="MM / YY"></div>
            <div class="field"><label>CVC</label><input type="text" placeholder="123"></div>
          </div>
          <div class="divider"></div>
          <div class="summary"><span>Premium Plan</span><span>$29.99</span></div>
          <div class="summary"><span>Tax</span><span>$0.00</span></div>
          <div class="summary total"><span>Total</span><span>$29.99</span></div>
          <button class="btn">Pay $29.99</button>
          <p class="secure">Secured by TagadaPay</p>
        </div>
      </div>
    </body>
    </html>
    ```

    **Thank you page** — order confirmation with a green checkmark:

    ```html theme={null}
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Thank You</title>
      <style>
        * { box-sizing: border-box; margin: 0; padding: 0; }
        body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #f5f5f7; color: #1d1d1f; }
        .container { max-width: 480px; margin: 80px auto; padding: 0 20px; text-align: center; }
        .card { background: #fff; border-radius: 12px; padding: 48px 32px; box-shadow: 0 2px 12px rgba(0,0,0,0.08); }
        .checkmark { width: 64px; height: 64px; background: #34c759; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; margin-bottom: 24px; }
        .checkmark svg { width: 32px; height: 32px; }
        h1 { font-size: 24px; font-weight: 600; margin-bottom: 12px; }
        p { font-size: 15px; color: #6e6e73; line-height: 1.5; }
        .order-id { display: inline-block; background: #f5f5f7; padding: 8px 16px; border-radius: 8px; font-size: 14px; font-weight: 500; margin-top: 24px; color: #1d1d1f; }
      </style>
    </head>
    <body>
      <div class="container">
        <div class="card">
          <div class="checkmark">
            <svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="3"
                 stroke-linecap="round" stroke-linejoin="round">
              <polyline points="20 6 9 17 4 12"/>
            </svg>
          </div>
          <h1>Thank you!</h1>
          <p>Your order has been confirmed.<br>You will receive a confirmation email shortly.</p>
          <span class="order-id">Order #TGD-10042</span>
        </div>
      </div>
    </body>
    </html>
    ```

    ### Step 2: Deploy as a plugin

    Upload both pages as base64-encoded inline assets:

    ```ts theme={null}
    import Tagada from '@tagadapay/node-sdk';
    import { readFileSync } from 'fs';
    const tagada = new Tagada('your-api-key');

    // If loading from files:
    // const checkoutHtml = readFileSync('checkout.html', 'utf-8');
    // const thankyouHtml = readFileSync('thankyou.html', 'utf-8');

    const deployment = await tagada.plugins.deploy({
      storeId: 'store_...',
      manifest: {
        name: 'my-custom-checkout',
        description: 'Custom branded checkout',
        mode: 'spa-mode',
        version: '1.0.0',
        pages: [
          { id: 'checkout', path: '/checkout', name: 'Checkout', isDefault: true },
          { id: 'thankyou', path: '/thankyou', name: 'Thank You' },
        ],
      },
      inlineAssets: [
        { path: 'checkout/index.html', content: btoa(checkoutHtml), contentType: 'text/html' },
        { path: 'thankyou/index.html', content: btoa(thankyouHtml), contentType: 'text/html' },
      ],
      autoInstantiate: {
        stepType: 'checkout',
        isPreview: false,
      },
    });

    const { pluginId, deploymentId, instanceId } = deployment;
    console.log('Deployed:', { pluginId, deploymentId, instanceId });
    ```

    <Note>
      `autoInstantiate` creates a plugin instance automatically. Without it, you'd need a separate `tagada.plugins.instantiate()` call.
    </Note>

    ### Step 3: Create a funnel using your plugin

    Reference `pluginId` and `instanceId` in each funnel node:

    ```ts theme={null}
    const funnel = await tagada.funnels.create({
      storeId: 'store_...',
      config: {
        id: 'custom-checkout',
        name: 'Custom Checkout',
        version: '1.0.0',
        nodes: [
          {
            id: 'step_checkout',
            name: 'Checkout',
            type: 'checkout',
            kind: 'step',
            isEntry: true,
            position: { x: 0, y: 0 },
            config: {
              pluginId, instanceId,
              instanceVersion: '1.0.0',
              variant: 'Default',
              path: '/checkout',
              metadata: { internalPath: '/checkout', mountPoint: 'default' },
            },
          },
          {
            id: 'step_thankyou',
            name: 'Thank You',
            type: 'thankyou',
            kind: 'step',
            position: { x: 300, y: 0 },
            config: {
              pluginId, instanceId,
              instanceVersion: '1.0.0',
              variant: 'Default',
              path: '/thankyou',
              metadata: { internalPath: '/thankyou', mountPoint: 'default' },
            },
          },
        ],
        edges: [
          { id: 'e1', source: 'step_checkout', target: 'step_thankyou' },
        ],
      },
      isDefault: true,
    });
    ```

    ### Step 4: Activate

    ```ts theme={null}
    const result = await tagada.funnels.update(funnel.id, {
      storeId: 'store_...',
      config: funnel.config,
    });

    const url = result.funnel.config.nodes[0].config.url;
    console.log('Custom checkout live at:', url);
    ```
  </Tab>

  <Tab title="Plugin SDK (Recommended for Production)">
    ## Plugin SDK Checkout

    The **Plugin SDK** (`@tagadapay/plugin-sdk`) is the professional approach for teams with a codebase. You build a React (or vanilla JS) SPA that uses TagadaPay's SDK hooks for session management, card tokenization, 3DS, payment processing, funnel navigation, and analytics — then deploy it from your repo with the CLI.

    <Info>
      **This is different from Custom HTML.** With raw HTML you handle everything yourself. With the Plugin SDK, you get React hooks (or a standalone JS API) that connect your UI to TagadaPay's backend — sessions, checkout data, payments, redirects — out of the box.
    </Info>

    ### What you get from the SDK

    * `useCheckoutSession()` — cart items, prices, customer data
    * `useCardTokenization()` — PCI-compliant card form, Apple Pay, Google Pay
    * `usePaymentProcessors()` — process payments through your PSP routing
    * `useThreeds()` — 3DS authentication with automatic challenge handling
    * `useFunnelNavigation()` — step-to-step navigation with data passing
    * `useFunnelData()` — shared state across funnel steps
    * Full TypeScript types

    ### Step 1: Scaffold your project

    ```bash theme={null}
    npm install -g @tagadapay/plugin-cli
    tgd login
    tgd init my-checkout --template react-checkout
    cd my-checkout
    ```

    This generates a project with:

    ```
    my-checkout/
    ├── src/
    │   ├── pages/
    │   │   ├── checkout/     # Checkout page component
    │   │   └── thankyou/     # Thank you page component
    │   └── App.tsx
    ├── plugin.manifest.json  # Plugin metadata + page definitions
    ├── package.json
    └── vite.config.ts        # Pre-configured build
    ```

    ### Step 2: Build your checkout page

    ```tsx theme={null}
    import {
      useCheckoutSession,
      useCardTokenization,
      usePaymentProcessors,
      useFunnelNavigation,
    } from '@tagadapay/plugin-sdk/react';

    export default function CheckoutPage() {
      const { session, cart, totals } = useCheckoutSession();
      const { tokenizeCard, CardForm } = useCardTokenization();
      const { processPayment, isProcessing } = usePaymentProcessors();
      const { navigateToNext } = useFunnelNavigation();

      async function handlePay() {
        const { tagadaToken } = await tokenizeCard();
        const result = await processPayment({ tagadaToken });

        if (result.status === 'succeeded') {
          navigateToNext({ orderId: result.orderId });
        }
      }

      return (
        <div className="checkout">
          <h1>Checkout</h1>

          {/* Cart summary — pulled from session automatically */}
          <div className="order-summary">
            {cart.items.map(item => (
              <div key={item.id}>
                <span>{item.name}</span>
                <span>{item.formattedPrice}</span>
              </div>
            ))}
            <div className="total">{totals.formattedTotal}</div>
          </div>

          {/* PCI-compliant card form — SDK handles tokenization */}
          <CardForm />

          <button onClick={handlePay} disabled={isProcessing}>
            {isProcessing ? 'Processing...' : `Pay ${totals.formattedTotal}`}
          </button>
        </div>
      );
    }
    ```

    ### Step 3: Build the thank you page

    ```tsx theme={null}
    import { useFunnelData } from '@tagadapay/plugin-sdk/react';

    export default function ThankYouPage() {
      const { orderId } = useFunnelData();

      return (
        <div className="thankyou">
          <h1>Thank you!</h1>
          <p>Your order <strong>{orderId}</strong> has been confirmed.</p>
        </div>
      );
    }
    ```

    ### Step 4: Configure the manifest

    ```json theme={null}
    {
      "name": "my-branded-checkout",
      "description": "Production checkout with React",
      "version": "1.0.0",
      "mode": "spa-mode",
      "pages": [
        { "id": "checkout", "path": "/checkout", "name": "Checkout", "isDefault": true },
        { "id": "thankyou", "path": "/thankyou", "name": "Thank You" }
      ]
    }
    ```

    ### Step 5: Deploy

    ```bash theme={null}
    npm run build
    tgd deploy --store store_xxx
    ```

    ```
    ✅ Deployed my-branded-checkout@1.0.0
       Plugin:     plugin_abc123
       Instance:   inst_xyz789
       Checkout:   https://my-branded-checkout--store_xxx.cdn.tagadapay.com/checkout
       Thank You:  https://my-branded-checkout--store_xxx.cdn.tagadapay.com/thankyou
    ```

    ### Step 6: Create a funnel and activate

    Use the returned `pluginId` and `instanceId` from deployment:

    ```ts theme={null}
    import Tagada from '@tagadapay/node-sdk';
    const tagada = new Tagada('your-api-key');

    const funnel = await tagada.funnels.create({
      storeId: 'store_xxx',
      config: {
        id: 'branded-checkout',
        name: 'Branded Checkout',
        version: '1.0.0',
        nodes: [
          {
            id: 'step_checkout',
            name: 'Checkout',
            type: 'checkout',
            kind: 'step',
            isEntry: true,
            position: { x: 0, y: 0 },
            config: {
              pluginId: 'plugin_abc123',
              instanceId: 'inst_xyz789',
              instanceVersion: '1.0.0',
              variant: 'Default',
              path: '/checkout',
              metadata: { internalPath: '/checkout', mountPoint: 'default' },
            },
          },
          {
            id: 'step_thankyou',
            name: 'Thank You',
            type: 'thankyou',
            kind: 'step',
            position: { x: 300, y: 0 },
            config: {
              pluginId: 'plugin_abc123',
              instanceId: 'inst_xyz789',
              instanceVersion: '1.0.0',
              variant: 'Default',
              path: '/thankyou',
              metadata: { internalPath: '/thankyou', mountPoint: 'default' },
            },
          },
        ],
        edges: [
          { id: 'e1', source: 'step_checkout', target: 'step_thankyou' },
        ],
      },
      isDefault: true,
    });

    const result = await tagada.funnels.update(funnel.id, {
      storeId: 'store_xxx',
      config: funnel.config,
    });

    console.log('Live at:', result.funnel.config.nodes[0].config.url);
    ```

    ### Vanilla JS (no React)

    The Plugin SDK also works without React:

    ```js theme={null}
    import { TagadaPlugin } from '@tagadapay/plugin-sdk';

    const plugin = new TagadaPlugin();
    await plugin.initialize();

    const session = plugin.getCheckoutSession();
    const tokenizer = plugin.getCardTokenizer();

    document.getElementById('pay-btn').addEventListener('click', async () => {
      const { tagadaToken } = await tokenizer.tokenize({
        cardNumber: document.getElementById('card').value,
        expiryDate: document.getElementById('expiry').value,
        cvc: document.getElementById('cvc').value,
      });

      const result = await plugin.processPayment({ tagadaToken });

      if (result.status === 'succeeded') {
        plugin.navigateToNext({ orderId: result.orderId });
      }
    });
    ```

    ### Updating a deployed plugin

    Deploy a new version — zero downtime:

    ```bash theme={null}
    # Bump version in plugin.manifest.json
    tgd deploy --store store_xxx
    ```

    The CLI handles versioning and instance creation. Then update the funnel to point to the new instance version.

    <Tip>
      For the full Plugin SDK reference, see the [Plugin SDK documentation](/developer-tools/sdk/introduction). For CLI commands and configuration, see the [Plugin CLI Guide](/developer-tools/cli/introduction).
    </Tip>
  </Tab>
</Tabs>

***

## Updating a Live Plugin

To update your custom checkout without downtime, deploy a new version with the same plugin name:

```ts theme={null}
const updated = await tagada.plugins.deploy({
  storeId: 'store_...',
  manifest: {
    name: 'my-custom-checkout',
    version: '1.1.0',
    mode: 'spa-mode',
    pages: [
      { id: 'checkout', path: '/checkout', name: 'Checkout', isDefault: true },
      { id: 'thankyou', path: '/thankyou', name: 'Thank You' },
    ],
  },
  inlineAssets: [/* updated HTML/CSS/JS */],
  autoInstantiate: { stepType: 'checkout', isPreview: false },
});

// Point the funnel to the new instance
const config = JSON.parse(JSON.stringify(funnel.config));
for (const node of config.nodes) {
  node.config.instanceId = updated.instanceId;
  node.config.instanceVersion = '1.1.0';
}

await tagada.funnels.update(funnel.id, {
  storeId: 'store_...',
  config,
});
```

***

## A/B Testing

Split traffic between two checkout designs:

```ts theme={null}
const variantB = await tagada.plugins.deploy({
  storeId: 'store_...',
  manifest: {
    name: 'checkout-variant-b',
    version: '1.0.0',
    mode: 'spa-mode',
    pages: [
      { id: 'checkout', path: '/checkout', name: 'Checkout B', isDefault: true },
    ],
  },
  inlineAssets: [/* variant B HTML */],
  autoInstantiate: { stepType: 'checkout', isPreview: false },
});

// Get routeId from the activated funnel
const checkoutNode = result.funnel.config.nodes
  .find(n => n.id === 'step_checkout');
const routeId = checkoutNode.config.routeId;

// 50/50 split
await tagada.plugins.configureSplit({
  routeId,
  storeId: 'store_...',
  splitConfig: {
    type: 'weighted',
    instances: [
      { instanceId: deployment.instanceId, deploymentId: deployment.deploymentId, weight: 50 },
      { instanceId: variantB.instanceId, deploymentId: variantB.deploymentId, weight: 50 },
    ],
    stickyDuration: 3600,
  },
});
```

***

## Direct Mounting (Without Funnel)

For standalone pages (landing pages, portals) outside of a checkout flow:

```ts theme={null}
await tagada.plugins.mount({
  deploymentId,
  deploymentInstanceId: instanceId,
  storeId: 'store_...',
  alias: 'my-landing-page',
  matcher: '.*',
  force: true,
});
// → my-landing-page--store_xxx.cdn.tagadapay.com
```

***

## Custom Domains

By default, pages are served on `*.cdn.tagadapay.com`. To use your own domain (e.g. `checkout.yourstore.com`), use `tagada.domains`.

### Step 1 — Register the domain

```ts theme={null}
const { domain } = await tagada.domains.add({
  domain: 'checkout.yourstore.com',
});

console.log(domain.id);               // cdm_abc123
console.log(domain.isVerified);        // false
console.log(domain.verificationToken); // vrf_token_xyz
```

### Step 2 — Configure DNS

Add a **CNAME** record at your registrar:

| Type  | Name       | Value                  |
| ----- | ---------- | ---------------------- |
| CNAME | `checkout` | `cname.vercel-dns.com` |

<Tip>
  Use `tagada.domains.getConfig()` and `tagada.domains.getDnsLookup()` to check DNS propagation programmatically before verifying.
</Tip>

```ts theme={null}
const config = await tagada.domains.getConfig({
  domain: 'checkout.yourstore.com',
});
console.log(config.misconfigured);     // true (DNS not propagated yet)
console.log(config.recommendedCNAME);  // cname.vercel-dns.com

const dns = await tagada.domains.getDnsLookup({
  domain: 'checkout.yourstore.com',
  recordType: 'CNAME',
});
console.log(dns.records); // ['cname.vercel-dns.com'] when ready
```

### Step 3 — Verify

Once DNS has propagated:

```ts theme={null}
const result = await tagada.domains.verify({
  domainId: domain.id,
});

console.log(result.status);          // 'Valid Configuration'
console.log(result.ownershipStatus); // 'Verified'
```

Possible `status` values: `Valid Configuration`, `Invalid Configuration`, `Pending Verification`, `Domain Not Found`, `Error`.

### Step 4 — Mount on the custom domain

Use `customDomain` instead of `hostname` or `alias` when mounting:

```ts theme={null}
await tagada.plugins.mount({
  deploymentId,
  deploymentInstanceId: instanceId,
  storeId: 'store_...',
  customDomain: 'checkout.yourstore.com',
  basePath: '/',
  matcher: '/',
});
// → https://checkout.yourstore.com/
```

HTTPS is provisioned automatically. A/B testing and sticky sessions work identically on custom domains.

### Managing domains

```ts theme={null}
const { domains } = await tagada.domains.list();

await tagada.domains.remove({ domainId: 'cdm_abc123' });
```

***

## Promote Staging to Production

After testing on the staging CDN URL (`*.cdn.tagadapay.com`), promote the funnel to your custom domain in one call. The server handles instance cloning, route setup, and prebuilds automatically.

```ts theme={null}
const result = await tagada.funnels.promote(funnel.id, {
  storeId: 'store_...',
  customDomain: 'checkout.yourstore.com',
});

console.log(result.success); // true
console.log(result.message); // 'Funnel promoted to checkout.yourstore.com'
```

<Info>
  The domain must be verified before promoting. If the domain is already in use by another funnel, pass `acknowledgeConflict: true` to override:

  ```ts theme={null}
  await tagada.funnels.promote(funnel.id, {
    storeId: 'store_...',
    customDomain: 'checkout.yourstore.com',
    acknowledgeConflict: true,
  });
  ```
</Info>

### Full workflow: staging to production

```ts theme={null}
import Tagada from '@tagadapay/node-sdk';
const tagada = new Tagada('your-api-key');

// 1. Deploy + create funnel (see sections above)
// Your funnel is now live on *.cdn.tagadapay.com (staging)

// 2. Register and verify your domain
const { domain } = await tagada.domains.add({ domain: 'checkout.yourstore.com' });
// ... configure CNAME, wait for DNS ...
await tagada.domains.verify({ domainId: domain.id });

// 3. Promote — one call moves everything to production
await tagada.funnels.promote(funnel.id, {
  storeId: 'store_...',
  customDomain: 'checkout.yourstore.com',
});

// 4. Create checkout sessions pointing to the production URL
const session = await tagada.checkout.createSession({
  storeId: 'store_...',
  items: [{ variantId: 'variant_...', quantity: 1 }],
  currency: 'USD',
  checkoutUrl: 'https://checkout.yourstore.com/checkout',
});
console.log('Live checkout:', session.redirectUrl);
```

***

## Plugin Management

```ts theme={null}
const plugins = await tagada.plugins.list('store_...');
const instances = await tagada.plugins.listInstances('store_...');
const details = await tagada.plugins.retrieveDeployment(deploymentId);
await tagada.plugins.del(pluginId, 'store_...');
```

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Plugin SDK Reference" icon="react" href="/developer-tools/sdk/introduction">
    Full SDK documentation — hooks, API, examples
  </Card>

  <Card title="Plugin CLI" icon="terminal" href="/developer-tools/cli/introduction">
    Deploy plugins from your terminal with `tgd deploy`
  </Card>

  <Card title="Merchant Quick Start" icon="rocket" href="/developer-tools/node-sdk/merchant-quickstart">
    Full 7-step setup from processor to live checkout
  </Card>

  <Card title="Host & A/B Test" icon="flask-vial" href="/developer-tools/hosting/deploy-and-ab-test">
    Custom domains, A/B testing, and hosting via REST API
  </Card>
</CardGroup>
