JavaScript SDK

The SDK creates a session via the v1 API and opens checkout in an iframe on splitante.com. Card entry happens there (Stripe). Your page stays visible behind a modal overlay. All browser paths share the same credentials: publishable key + signing secret — not the secret API key.

Copy this page as a setup prompt for your coding assistant.

Full implementation example

Try the live sandbox store at ante-demo-store.vercel.appa complete Next.js shop with cart signing, @splitante/react-sdk, webhooks, and test/live credential switching.

Clone the source on GitHub: plurel-company/ante-demo-store. The repository is public (anyone can clone or fork); only Plurel maintainers can push to main.

Choose an integration path

PathBest forInstallCredentials
CDN script tagWordPress, static HTML, no bundler<script src="https://splitante.com/sdk/v1/ante.js">Publishable key + signing route
npm — vanillaVite, webpack, bundled JS without Reactnpm install @splitante/sdkSame as CDN
npm — ReactNext.js, Remix, React SPAsnpm install @splitante/react-sdkSame as CDN (react-sdk wraps core)
REST API onlyCustom checkout UI, no modal embedcurl or @splitante/sdk AnteApiClientSecret API key (+ headless approval)

Prerequisite: cart signing

Every browser path above uses a publishable key. You must deploy POST /api/cart/sign on your domain and set ANTE_SIGNING_SECRET. Full spec: Cart signing. Credential cheat sheet: Credentials.

Use @splitante/sdk and @splitante/react-sdk at version 0.1.7 or later for cart.fees and byte-compatible HMAC signing with the Ante API.

Why two npm packages?

@splitante/sdk is the core library: hosted modal, session client, TypeScript types, cart validation, and the server-only @splitante/sdk/signing subpath. @splitante/react-sdk is a thin React layer on top — AnteProvider, AnteButton, and polling hooks. It depends on @splitante/sdk and adds no payment logic of its own.

PackageInstall whenMain exports
@splitante/sdkAny integration (required for signing on the server too)Ante, AnteApiClient, AnteSessionClient, types; signing via @splitante/sdk/signing
@splitante/react-sdkReact / Next.js apps onlyAnteProvider, AnteButton, useAnteSession, useGroupStatus

The CDN bundle at /sdk/v1/ante.js is the same core compiled to window.Ante — use CDN if you do not have a JavaScript bundler. You do not need both npm packages unless you use React; non-React sites use CDN or @splitante/sdk alone.

1. CDN script tag (vanilla HTML)

No build step. Loads window.Ante globally. Best for quick pilots and legacy stacks.

index.html
<script src="https://splitante.com/sdk/v1/ante.js" async></script>
<script>
  const cart = {
    total: 12800,
    currency: "usd",
    items: [{ id: "sku_1", name: "Ticket", quantity: 2, unit_price: 5000 }],
    tax: 1000,
    shipping: 1000,
    fees: [
      { id: "cleaning", label: "Cleaning fee", amount: 500 },
      { id: "processing", label: "Ticket processing", amount: 300 },
    ],
    metadata: { order_ref: "ORD-1042" },
  };

  // From your logged-in shopper when available:
  const customer = { name: "Alex", email: "alex@example.com" };

  Ante.init({
    merchantId: "ante_merch_YOUR_ID",
    publishableKey: "ante_pk_test_YOUR_KEY",
    getSignature: async (c) => {
      const res = await fetch("/api/cart/sign", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ cart: c }),
      });
      if (!res.ok) throw new Error("Cart signing failed");
      return (await res.json()).signature;
    },
  });

  Ante.createButton({
    cart,
    group: { minSize: 2, maxSize: 6 },
    customer,
    success_url: "https://your-store.com/checkout/success",
    cancel_url: "https://your-store.com/checkout",
    callbacks: {
      onGroupFunded: (id, ref) => console.log("Funded", id, ref),
    },
  }).mount("#ante-button");
</script>

2. npm — bundled JavaScript (no React)

Import Ante from @splitante/sdk when you have a bundler but are not using React. API is identical to the CDN global.

checkout.ts
import { Ante } from "@splitante/sdk";

Ante.init({
  merchantId: import.meta.env.VITE_ANTE_MERCHANT_ID,
  publishableKey: import.meta.env.VITE_ANTE_PUBLISHABLE_KEY,
  getSignature: async (cart) => {
    const res = await fetch("/api/cart/sign", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ cart }),
    });
    if (!res.ok) throw new Error("Cart signing failed");
    return (await res.json()).signature;
  },
});

Ante.createButton({ cart, group: { minSize: 2, maxSize: 6 } }).mount("#ante-button");

Server-side signing uses a separate import — never bundle this in client code:

app/api/cart/sign/route.ts
import { createCartSignature } from "@splitante/sdk/signing";

export async function POST(req: Request) {
  const { cart } = await req.json();
  const signature = createCartSignature(cart, process.env.ANTE_SIGNING_SECRET!);
  return Response.json({ signature });
}

3. npm — React

Install @splitante/react-sdk (pulls in @splitante/sdk automatically). Wrap checkout in AnteProvider; mount AnteButton where the shopper pays.

AnteProvider + AnteButton
"use client";

import { AnteProvider, AnteButton } from "@splitante/react-sdk";

const cart = {
  total: 12000,
  currency: "usd",
  items: [{ id: "sku_1", name: "Ticket", quantity: 2, unit_price: 5000 }],
  metadata: { order_ref: "ORD-1042" },
};

export default function Checkout() {
  const customer = { name: "Alex", email: "alex@example.com" };

  return (
    <AnteProvider
      merchantId={process.env.NEXT_PUBLIC_ANTE_MERCHANT_ID!}
      publishableKey={process.env.NEXT_PUBLIC_ANTE_PUBLISHABLE_KEY!}
      environment="sandbox"
      getSignature={async (c) => {
        const res = await fetch("/api/cart/sign", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ cart: c }),
        });
        if (!res.ok) throw new Error("Cart signing failed");
        return (await res.json()).signature;
      }}
    >
      <AnteButton
        cart={cart}
        group={{ minSize: 2, maxSize: 6 }}
        customer={customer}
        success_url="https://your-store.com/checkout/success"
        cancel_url="https://your-store.com/checkout"
        appearance={{ size: "md", variant: "primary" }}
      />
    </AnteProvider>
  );
}

Hooks for status outside the modal: useAnteSession(sessionId), useGroupStatus(sessionId) (polls GET /api/v1/sessions/:id every 3s).

Full implementation example: ante-demo-store.vercel.app (live sandbox) · clone github.com/plurel-company/ante-demo-store — public read/clone; Plurel maintainers only push to main.

4. REST API only (no SDK button)

Skip the modal embed entirely. Your server creates sessions with a secret API key (ante_sk_*). Requires headless_api_enabled on your merchant to omit cart signatures. See REST API and Credentials.

Node — AnteApiClient
import { AnteApiClient } from "@splitante/sdk";

const client = new AnteApiClient({
  apiKey: process.env.ANTE_SECRET_KEY!,
  merchantId: "ante_merch_YOUR_ID",
});

const session = await client.createSession({
  cart,
  group_config: { min_size: 2, max_size: 6 },
});

Ante.init(options)

Call once per page (CDN / npm vanilla) or via AnteProvider (React) before opening checkout.

OptionRequiredNotes
merchantIdYesante_merch_* from dashboard
publishableKeyYesante_pk_test_* or ante_pk_live_*
getSignatureYes*async (cart) => hex string from your /api/cart/sign
environmentNosandbox | production — keys control sandbox vs live mode
themeNoauto | light | dark
apiBaseUrlNoDefault https://splitante.com/api/v1
payBaseUrlNoDefault https://splitante.com (iframe origin)

*Omit if every button passes signature directly.

Button appearance

Layout follows the Apple Pay button pattern: flat fill, rounded rectangle, 44px default height, mark + wordmark centered. The label is always Split with Ante. Merchants may adjust layout and variant only — do not recolor or recompose the lockup (see terms).

appearance fieldValuesNotes
fullWidthboolean (default true)Span container width
sizesm | md | lgHeight and typography scale
variantprimary | outline | darkprimary = terra (default) · dark = black, pairs with Apple Pay · outline = white + hairline

Static SVG pack and dynamic endpoint for theme editors: /brand/buttons/ and /api/button?variant=primary&size=md&logo=1 — also in the merchant integration wizard.

Ante.createButton(options)

Recommended session fields

Pass these on every createSession or Ante.createButton call when your storefront already knows the values. The API accepts sessions without them, but production integrations are smoother when you send organizer context up front.

FieldPass whenWhy
customer.nameYou know the shopper starting the groupPre-fills the organizer in group setup; avoids blank “Person 1” labels.
customer.emailYou have a logged-in or checkout emailSupport and correlation; required for POST /sessions/:id/remind to reach pending members.
customer.phoneYou already collect itOptional support contact — only send if you use it elsewhere.
success_urlHosted modal / SDK embedRedirects the organizer back to your store after the group is fully funded.
cancel_urlHosted modal / SDK embedRedirects the organizer after they close the modal or the group is cancelled (refunds run automatically).
cart.metadata.order_refAlways (your order or cart id)Echoes as order_ref on webhooks and GET /sessions — tie fulfillment to your system.

Guest payers (share links)

Do not collect payer email or address in your storefront before Ante opens. Friends who open /s/{shareId} pay through Stripe; Ante stores payer email from Stripe when provided (receipt / billing details). Your integration only needs to pass organizer customer at session create.

Privacy

If you pass customer.email or customer.phone, disclose that in your privacy policy and only send fields you already collect for checkout. See Go live and your obligations under Ante's privacy policy for participant data.

OptionNotes
carttotal, currency, items; optional tax, shipping, fees[], metadata (use metadata.order_ref)
groupminSize, maxSize, defaultMode, allowedModes, expiryHours
signaturePre-computed HMAC; skips getSignature for this button
getSignatureOverrides init-level signer
customerOrganizer only — name, email, phone when your storefront knows them
success_urlHTTPS URL — redirect organizer after group funded (recommended in embed)
cancel_urlHTTPS URL — redirect organizer after cancel/abandon (recommended in embed)
callbacksonGroupCreated, onGroupFunded, onGroupExpired, onGroupCancelled, onError
appearancefullWidth, size (sm | md | lg), variant (primary | outline | dark) — label is always Split with Ante

Instance methods: mount(selector | element), unmount(), open(), destroy().

Split modes

ModeMeaning
equalEven split of cart total
itemizedEach member takes line items
customOrganizer sets amounts
percentagePercentage split
host-paysOrganizer absorbs remainder

Modal URL and postMessage

After session create, the SDK opens {payBaseUrl}/session/{id}?access={token}&embed=1&parent_origin={your origin}. Callbacks fire from postMessage; the SDK also polls GET /api/v1/sessions/:id every 3 seconds as a fallback. Fulfill orders on group.funded webhooks, not callbacks alone — see Webhooks.

Fees on create

POST /sessions returns merchant_fee_rate (default 0.0201) and consumer_fee_rate (0.01 per share). The modal shows these to payers.