An API, considered.
The Ante REST API lets merchants create group checkout sessions, invite participants, and reconcile settlements. All requests are made over HTTPS to api.ante.dev. What follows is the complete reference — endpoints, objects, and events — written with the same care you would hope for in a payment system.
“Money should move the way trust does — quietly, reliably, and with both parties made whole.”
— From the editor
# All API requests use the same base URL
https://api.ante.dev/v1Authentication
Ante uses API keys to authenticate requests. You can view and manage your keys in the Dashboard.
Key model
Each merchant account has four keys. Use publishable keys in the browser and secret keys only from your server.
| Attribute | Type | Description |
|---|---|---|
| pk_test_… | Publishable, test mode | Create embed tokens, redirect to hosted flow |
| sk_test_… | Secret, test mode | Full API access in test mode |
| pk_live_… | Publishable, live mode | Same as test, against real money |
| sk_live_… | Secret, live mode | Full API access in live mode |
Authentication header
All server-side requests use HTTP Bearer auth.
curl https://api.ante.dev/v1/checkout/sessions \
-H "Authorization: Bearer sk_live_51ABC...xyz"import { Ante } from "@ante/node";
const ante = new Ante(process.env.ANTE_SECRET_KEY);
const sessions = await ante.checkout.sessions.list();#5676EA">import ante
ante.api_key = os.environ["ANTE_SECRET_KEY"]
sessions = ante.CheckoutSession.list()Webhook signing secret
A separate whsec_… value is issued per endpoint, used to sign outbound webhook payloads with HMAC-SHA256. Included in the Ante-Signature request header.
Key rotation
curl -X POST https://api.ante.dev/v1/keys/rotate \
-H "Authorization: Bearer sk_live_..."Issues a new key pair. The old key stays valid for 24 hours to allow rolling deploys.
Core resources
The CheckoutSession object
The primary resource. Represents a single group purchase in progress.
| Attribute | Type | Description |
|---|---|---|
| id | string | Unique identifier prefixed with cs_ |
| amount_total | integer | Total purchase amount in the currency's smallest unit |
| amount_collected | integer | Total paid across all participants |
| amount_remaining | integer | Outstanding amount still to be funded |
| platform_fee | integer | 1% service fee, charged on top of amount_total to payers |
| status | enum | Current lifecycle state (see below) |
| split_config | object | Equal, custom, or by_item split configuration |
| participants | array | One entry per share |
| expires_at | timestamp | Auto-refund fires at this time if not fully funded |
{
"id": "cs_01HN4XQW8K2P9R5TZBVGMJ7FDC",
"object": "checkout_session",
"created": 1713456000,
"expires_at": 1713628800,
"status": "pending_participants",
"currency": "usd",
"amount_total": 240000,
"amount_collected": 60000,
"amount_remaining": 180000,
"platform_fee": 6000,
"merchant_id": "merch_01HN4V...",
"initiator": { "email": "alex@example.com", "name": "Alex Chen" },
"line_items": [
{ "id": "li_01HN4Y...", "description": "Joshua Tree Airbnb — 3 nights", "quantity": 1, "unit_amount": 240000 }
],
"split_config": { "mode": "equal", "participant_count": 4 },
"participants": [
{ "id": "pt_01HN4Z...", "email": "alex@example.com", "name": "Alex Chen", "share_amount": 60000, "status": "paid", "claimed_at": 1713456120, "paid_at": 1713456180 }
],
"urls": {
"hosted_checkout": "https://checkout.ante.dev/cs_01HN4X...",
"success_url": "https://merchant.com/orders/123/success",
"cancel_url": "https://merchant.com/orders/123/cancel"
},
"invite_code": "JOSHUA-TREE-4X",
"embed_token": "ct_01HN4X...",
"metadata": { "order_id": "ord_abc123", "booking_ref": "JT-2026-0418" }
}Status states
| Attribute | Type | Description |
|---|---|---|
| open | State | Created but no participants claimed yet |
| pending_participants | State | At least one share claimed, not all paid |
| partially_funded | State | Some participants paid, others outstanding |
| fully_funded | State | All shares collected, pending settlement |
| settled | Terminal | Funds transferred to merchant |
| expired | State | Deadline passed before full funding |
| refunded | Terminal | All collected shares returned to participants |
Split modes
Three configurations. Use equal for the simplest flow.
{
"mode": "equal",
"participant_count": 4
}{
"mode": "custom",
"shares": [
{ "email": "alex@example.com", "amount": 100000 },
{ "email": "jordan@example.com","amount": 80000 },
{ "email": "sam@example.com", "amount": 60000 }
]
}{
"mode": "by_item",
"assignments": [
{ "line_item_id": "li_...", "email": "alex@example.com" },
{ "line_item_id": "li_...", "email": "jordan@example.com" }
]
}API endpoints
Checkout Sessions
curl https://api.ante.dev/v1/checkout/sessions \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"amount_total": 240000,
"currency": "usd",
"split_config": { "mode": "equal", "participant_count": 4 },
"initiator": { "email": "alex@example.com", "name": "Alex Chen" },
"success_url": "https://merchant.com/orders/123/success",
"cancel_url": "https://merchant.com/orders/123/cancel"
}'const session = await ante.checkout.sessions.create({
amount_total: 240000,
currency: "usd",
split_config: { mode: "equal", participant_count: 4 },
initiator: { email: "alex@example.com", name: "Alex Chen" },
success_url: "https://merchant.com/orders/123/success",
cancel_url: "https://merchant.com/orders/123/cancel",
});session = Ante.CheckoutSession.create(
amount_total=240000,
currency="usd",
split_config={"mode": "equal", "participant_count": 4},
initiator={"email": "alex@example.com", "name": "Alex Chen"},
success_url="https://merchant.com/orders/123/success",
cancel_url="https://merchant.com/orders/123/cancel",
){
"id": "cs_01HN4XQW8K2P9R5TZBVGMJ7FDC",
"object": "checkout_session",
"status": "open",
"amount_total": 240000,
"amount_collected": 0,
"currency": "usd",
"split_config": { "mode": "equal", "participant_count": 4 },
"urls": {
"hosted_checkout": "https://checkout.ante.dev/cs_01HN4X..."
},
"created": 1713456000,
"expires_at": 1713628800
}curl https://api.ante.dev/v1/checkout/sessions/cs_01HN4X... \
-H "Authorization: Bearer sk_live_..."curl "https://api.ante.dev/v1/checkout/sessions?status=fully_funded&limit=20" \
-H "Authorization: Bearer sk_live_..."Forces session into expired state and triggers the refund flow.
Participants
curl https://api.ante.dev/v1/checkout/sessions/cs_01HN4X.../participants \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"email": "newfriend@example.com",
"name": "Taylor Kim",
"share_amount": 60000
}'await ante.checkout.sessions.participants.create(sessionId, {
email: "newfriend@example.com",
name: "Taylor Kim",
share_amount: 60000,
});Ante.CheckoutSession.add_participant(
session_id,
email="newfriend@example.com",
name="Taylor Kim",
share_amount=60000,
)For equal split mode, omit share_amount — it's recalculated automatically across all participants.
If the participant has already paid, their share is refunded and remaining shares recalculated.
Rate-limited to 3 per 24 hours per participant.
Settlements
{
"id": "stl_01HN5...",
"object": "settlement",
"session_id": "cs_01HN4X...",
"gross_amount": 240000,
"platform_fee": 6000,
"net_amount": 234000,
"currency": "usd",
"status": "paid",
"destination": { "type": "bank_account", "last4": "4242", "bank_name": "Chase" },
"arrival_date": 1713715200,
"created": 1713542400
}{
"reason": "merchant_requested",
"notify_participants": true
}Webhook endpoints
{
"url": "https://merchant.com/webhooks/ante",
"events": ["session.fully_funded", "session.settled", "session.expired"],
"description": "Production order fulfillment"
}{
"id": "we_01HN6...",
"url": "https://merchant.com/webhooks/ante",
"secret": "whsec_01HN6...",
"events": ["session.fully_funded", "session.settled", "session.expired"],
"status": "active",
"created": 1713456000
}Webhook events
All events share the same envelope. Your endpoint receives the full resource snapshot in data.object.
{
"id": "evt_01HN7...",
"object": "event",
"type": "session.fully_funded",
"created": 1713542400,
"data": {
"object": { /* full resource snapshot */ }
},
"livemode": true,
"api_version": "2026-04-18"
}Event types
| Attribute | Type | Description |
|---|---|---|
| session.created | Session | Merchant creates a new CheckoutSession |
| session.opened | Session | Initiator opens the hosted checkout for the first time |
| participant.joined | Participant | New participant added via initiator or invite link |
| participant.claimed | Participant | Participant clicks their invite link |
| participant.paid | Participant | Participant successfully pays their share |
| participant.declined | Participant | Participant opts out |
| participant.reminder_sent | Participant | Reminder email/SMS dispatched |
| session.partially_funded | Session | First payment collected but not all |
| session.fully_funded | Session | All participants have paid — ready to fulfill |
| session.settled | Session | Funds transferred to merchant bank account |
| session.expired | Session | Deadline passed, session closed |
| session.refunded | Session | All collected shares refunded to participants |
Signature verification
Ante-Signature: t=1713542400,v1=a6c9b7d...import crypto from "crypto";
function verify(rawBody, header, secret) {
const [tPart, v1Part] = header.split(",");
const timestamp = tPart.slice(2);
const signature = v1Part.slice(3);
const payload = `${timestamp}.${rawBody}`;
const expected = crypto.createHmac("sha256", secret).update(payload).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}#5676EA">import hmac, hashlib
#5676EA">def verify(raw_body: bytes, header: str, secret: str) -> bool:
t, v1 = header.split(",")
timestamp = t.split("=")[1]
signature = v1.split("=")[1]
payload = f"{timestamp}.{raw_body.decode()}".encode()
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
#5676EA">return hmac.compare_digest(expected, signature)Embed SDK
Vanilla JavaScript
<script src="https://js.ante.dev/v1"></script>
<button id="split-button">Split this purchase</button>
<script>
const ante = Ante('pk_live_...');
document.getElementById('split-button').addEventListener('click', async () => {
const res = await fetch('/api/create-split-session', { method: 'POST' });
const { embed_token } = await res.json();
ante.open({
embed_token,
onSuccess: (session) => {
window.location.href = '/order-confirmed';
},
onClose: () => console.log('Modal closed'),
});
});
</script>React
npm install @ante/reactimport { SplitCheckoutButton, AnteProvider } from '@ante/react';
export function Checkout() {
return (
<AnteProvider publishableKey="pk_live_...">
<SplitCheckoutButton
createSession={async () => {
const res = await fetch('/api/create-split-session', { method: 'POST' });
return res.json();
}}
onSuccess={(session) => navigate('/order-confirmed')}
appearance={{ theme: 'light', accent: '#0B5FFF' }}
>
Split this purchase
</SplitCheckoutButton>
</AnteProvider>
);
}Hosted checkout
For merchants who can't embed JS, redirect to the hosted urls.hosted_checkout returned by session creation. Pattern identical to Stripe Checkout.
const session = await ante.checkout.sessions.create({ /* ... */ });
res.redirect(303, session.urls.hosted_checkout);Error handling
HTTP status codes
| Attribute | Type | Description |
|---|---|---|
| 200 / 201 | Success | |
| 400 | Bad request | Invalid parameters |
| 401 | Unauthenticated | |
| 402 | Payment failed | |
| 403 | Forbidden | Key lacks permission |
| 404 | Resource not found | |
| 409 | Conflict | e.g. modifying a settled session |
| 429 | Rate limit exceeded | |
| 500 / 503 | Server error |
Error object
{
"error": {
"type": "invalid_request_error",
"code": "parameter_missing",
"message": "Missing required parameter: amount_total.",
"param": "amount_total",
"request_id": "req_01HN7..."
}
}Error types
| Attribute | Type | Description |
|---|---|---|
| authentication_error | Auth | Bad or missing API key |
| invalid_request_error | Client | Malformed parameters |
| payment_error | Payment | Card declined, insufficient funds |
| rate_limit_error | Client | Too many requests |
| session_state_error | Client | Operation invalid for current status |
| api_error | Server | Internal platform error |