Commerce API reference
Catalog, Stripe Connect, Checkout, discounts, auctions. All endpoints are tenant-scoped and require Authorization: Bearer kl_live_*.
No-code option: Manage products, prices, stock, images and Stripe Connect onboarding from the customer portal at
/app/catalog. Use this API when you need to script catalog sync from your own backend.
Endpoint index
| Method + path | Purpose |
|---|---|
GET /v1/me/catalog/products | List products (filterable) |
GET /v1/me/catalog/products/:id | Get one product |
POST /v1/me/catalog/products | Upsert a single product |
POST /v1/me/catalog/sync | Bulk upsert (up to 500 items) |
DELETE /v1/me/catalog/products/:id | Archive (soft-delete) |
GET /v1/me/commerce/connect/status | Stripe Connect onboarding status |
POST /v1/me/commerce/connect/onboarding-link | Get a Stripe Express onboarding URL |
POST /v1/me/commerce/checkout | Mint a Stripe Checkout session |
GET /v1/me/commerce/discounts | List discount codes |
POST /v1/me/commerce/discounts | Create a discount code |
POST /v1/me/commerce/discounts/drop/:streamId | Drop a time-limited code during a stream |
GET /v1/me/commerce/discounts/check/:code | Verify a code is still redeemable |
GET /v1/me/commerce/auctions | List auctions (filter by status) |
POST /v1/me/commerce/auctions | Create an auction |
POST /v1/me/commerce/auctions/:id/bid | Place a bid (atomic) |
Product object
{
"id": "prod_xxx",
"externalId": "SKU-1234", // your own SKU — used for idempotent upserts
"name": "Linen Dress",
"description": "100% European linen",
"imageUrl": "https://cdn.example.com/sku-1234.webp",
"priceCents": 4900, // 49.00 USD
"compareAtCents": 7900, // MSRP for "X% off" display
"currency": "USD",
"stock": 17, // -1 = unlimited / don't track
"lowStockAt": 5, // emits inventory.low when crossed
"active": true,
"stripePriceId": "price_xxx", // cached after first sale (read-only)
"metadata": { "color": "linen-natural" },
"createdAt": "...",
"updatedAt": "..."
}
Discount object
{
"id": "disc_xxx",
"code": "FLASH50", // case-insensitive at redeem
"percentOff": 50, // OR amountOffCents — not both
"amountOffCents": null,
"startsAt": null,
"endsAt": "2026-05-21T19:00:00.000Z",
"maxRedemptions": 100,
"redemptions": 23,
"maxPerCustomer": 1,
"streamId": "stream_abc", // optional: locks code to a stream
"active": true,
"createdAt": "..."
}
Auction object
{
"id": "auc_xxx",
"productId": "prod_xxx",
"streamId": "stream_abc",
"startingBidCents": 5000,
"reservePriceCents": 8000, // optional; if top bid is below this, no winner
"bidIncrementCents": 100,
"endsAt": "2026-05-21T19:03:00.000Z",
"antiSnipeSec": 10, // bid in last N sec → endsAt extended by N
"status": "LIVE", // LIVE | ENDED | SETTLED | CANCELLED
"winningBidId": null,
"bids": [ /* most recent bid */ ]
}
Order object
{
"id": "ord_xxx",
"stripeCheckoutSessionId": "cs_test_...",
"stripePaymentIntentId": "pi_test_...",
"customerExternalId": "user_42",
"customerEmail": "buyer@example.com",
"streamId": "stream_abc",
"subtotalCents": 9800,
"discountCents": 4900,
"totalCents": 4900,
"currency": "USD",
"status": "PAID", // PENDING | PAID | FULFILLED | REFUNDED | ...
"discountCode": "FLASH50",
"applicationFeeCents": 142, // 2.9% platform fee Kardolive took
"items": [
{ "productId": "prod_xxx", "nameSnapshot": "Linen Dress", "priceCents": 4900, "quantity": 2 }
],
"paidAt": "2026-05-21T18:30:00.000Z"
}
Stripe Connect setup
To accept money, sellers need a Stripe Express account linked to their Kardolive tenant.
- Seller (or admin on their behalf) calls
POST /v1/me/commerce/connect/onboarding-linkwithreturnUrl+refreshUrl. - Kardolive creates a Stripe account (Express, type=express, capabilities=card_payments + transfers) and returns a one-time onboarding URL.
- Redirect the seller to that URL. Stripe walks them through bank account + identity verification.
- When done, Stripe redirects back to your
returnUrl. - Stripe fires
account.updatedto Kardolive's webhook endpoint. Kardolive flipsstripeChargesEnabled = trueon the tenant. - Now you can call
/checkout.
Platform fee (optional)
Set STRIPE_PLATFORM_FEE_PCT on the API server (e.g. 2.9 for 2.9%). Kardolive automatically attaches an application_fee_amount on every Checkout — the fee is routed to your platform Stripe account, the rest to the seller. Default: 0% (seller keeps everything).
Checkout flow (full sequence)
1. Your client sends cart items to your backend
POST /api/cart/checkout { items, streamId, discountCode? }
2. Your backend calls Kardolive
POST /v1/me/commerce/checkout → { url, sessionId }
3. Your backend returns the Stripe URL to the client
4. Client opens window.location = url (or new tab)
→ Stripe-hosted Checkout
5. Buyer pays
→ Stripe sends checkout.session.completed to Kardolive's webhook endpoint
→ Kardolive marks order PAID, decrements stock, fires purchase.completed → your endpoint
6. Your endpoint receives purchase.completed
→ Create Order row in your ERP, deduct inventory, trigger fulfillment, send email
Idempotency & ordering
- Catalog upserts: Use
externalIdfor safe retries. The same payload twice = one product. - Bids: Wrapped in
SELECT FOR UPDATE— two simultaneous bids can't tie or skip the increment. - Checkout: Stripe Checkout sessions are idempotent by their session ID. Don't retry the create call on network errors — Stripe stamps a session in their DB; just retrieve it later.
- Purchase webhooks: Kardolive may deliver the same
purchase.completedevent more than once. Idempotent-check onorderIdin your handler.
Pricing & currency
All monetary fields are integer cents — never floats. $49.00 = 4900.
Currency is ISO 4217 lowercase at the API boundary (we normalize to uppercase in storage).
Mixed-currency carts are not supported: every item in a checkout must share the same currency.
Errors
| Code | When |
|---|---|
400 "Seller has not completed Stripe Connect onboarding" | chargesEnabled = false on the tenant |
400 "only N of X remaining" | Cart quantity exceeds stock |
400 "min bid is N cents" | Bid below current top + increment |
400 "auction has ended" | Bid arrived after endsAt (no anti-snipe extension because remaining > threshold) |
404 "product not found" | productId not in this tenant |