Introduction
Limbo exposes public marketplace reads and authenticated account routes for sellers, checkout integrations, and dashboard data. This reference documents the routes and request shapes implemented by the current application. Some actions still require a signed-in buyer in Limbo's hosted checkout.
Browse the public catalog without a key. Use a server-side credential for account routes. Keep buyer authorization inside hosted checkout.
Authentication
Authentication is route-specific. Public marketplace reads need no credential. Protected seller and dashboard routes accept a Limbo session; routes using the shared account authenticator can also accept an account API key as a Bearer token. The lm_ key is generated in Dashboard / API Access, shown when issued, and stored as a hash. It acts with the permissions of its owner, so keep it on a trusted server and rotate it if exposed.
const key = process.env.LIMBO_API_KEY;
if (!key) throw new Error('Set LIMBO_API_KEY on your server.');
const request = await fetch('https://limbo.ink/api/dashboard/stats', {
headers: {
'Authorization': `Bearer ${key}`,
'Content-Type': 'application/json'
}
});
if (!request.ok) throw new Error('Request failed: ' + request.status);
const data = await request.json();Marketplace (public reads)
The catalog, listing details, and marketplace totals are public reads. Reporting a listing requires a signed-in account. Public responses omit seller-only fields such as serial counts and per-listing volume. Browse with GET /api/marketplace, open one listing with GET /api/marketplace/{id-or-slug}, and read platform aggregates with GET /api/marketplace/stats. Your own listings and seller-only data live under the Seller API below and require authentication.
/api/marketplace
List public listings from every seller, newest first and paginated. Public here means a listing that is ACTIVE and PUBLIC and approved, so the response is the whole marketplace rather than your own shelf, and a listing a moderator removed never appears. Query: search, category, page, limit (at most 100). Each entry carries display fields plus a server-derived soldOut boolean. Stock levels, settled revenue, serial counts and moderation flags are withheld by design.
View response example
{
"products": [
{
"id": "clx_9f2k1m",
"name": "Elite Access Key",
"slug": "elite-access-key",
"description": "Delivered after payment confirmation.",
"price": 49.99,
"createdAt": "2026-09-01T10:15:00.000Z",
"ownerId": "clx_owner_1",
"productType": "SERIAL",
"requiresUserInput": false,
"serviceDescription": null,
"userInputLabel": null,
"acceptedChains": ["BTC", "LTC"],
"images": [],
"category": null,
"tags": ["accounts"],
"saleMode": "STOCK",
"soldOut": false,
"owner": { "name": "Shadow_Admin", "image": null, "customAvatar": null }
}
],
"total": 1,
"page": 1,
"limit": 50,
"totalPages": 1
}/api/marketplace/stats
Public aggregate totals: activeListings, sellerCount, and totalVolumeUsd. totalVolumeUsd sums the amount of escrows marked RELEASED or DELIVERED for public listings; it is transaction volume, not Limbo's commission revenue.
View response example
{
"activeListings": 128,
"sellerCount": 34,
"totalVolumeUsd": 45210
}/api/marketplace/{id-or-slug}
One listing with similar items. Accepts a product id or a listing link slug. Listings that are not public, active and approved answer 404 unless the caller owns them. Response: { product, similar, resolvedBy, slugMatchCount }.
/api/marketplace/report
Report a listing for review. Needs a session. Body: productId plus reason (10-1000 characters). Rate-limited. You cannot report your own product.
View request body example
{
"productId": "clx_9f2k1m",
"reason": "This listing promises a lifetime key but delivers a trial."
}/api/payments/create-escrow
Start the escrow for one listing as the signed-in buyer. Hosted checkout collects the buyer signing details and explicit terms and immediate-performance acknowledgements, including the current policyVersion, termsAccepted and immediatePerformance fields. It answers 409 with { "error": "This listing has sold out.", "code": "SOLD_OUT" } for a ONE_TIME listing that has already sold and for a STOCK listing at zero units. This is an advisory pre-check that keeps a buyer from being sent to pay for a dead listing. The claim that actually binds is made inside the delivery transaction once the on-chain payment is verified, so two buyers racing for the last unit can never both be served, and a buyer who does pay is held for refund rather than silently receiving nothing.
View response example
{
"error": "This listing has sold out.",
"code": "SOLD_OUT"
}Seller API
Everything about your own shelf. Every route here needs your session or your account key, and every route answers only the authenticated account's own data: its products with its own revenue and stock figures, its serial keys. One account can never read another's, and the product ids in the path are checked against the caller on every call.
/api/seller/products
Your shelf at any status and visibility, newest first and paginated. Query: search, page, limit (at most 100). Entries include soldVolumeUsd, a tracked escrow volume that can include in-progress orders, plus your unclaimed serial count.
/api/seller/products
Create a listing with a browser session or account API key. Serial listings can include keys separated by newlines. Publishing requires a verified email, linked Discord account, and two-factor authentication. ETH, USDC, USDT, and WBTC require Premium; use payment methods enabled for the platform and listing. Active listing caps are 10 on Free, 200 on Pro, and unlimited on Business. Prices must be positive and at most 1,000,000, except a profile preset may be free at 0. productType accepts SERIAL, SERVICE, or PRESET. saleMode defaults to UNLIMITED; ONE_TIME limits a listing to one sale, and STOCK requires stockCount from 1 to 1,000,000. A stock count is rejected for other sale modes. When stock is depleted, the listing remains published as sold out and can be restocked.
View request body example
{
"name": "Automated Asset #1",
"description": "Delivered on payment.",
"price": 10.00,
"productType": "SERIAL",
"saleMode": "STOCK",
"stockCount": 25,
"serialKeys": "KEY-AAAA\nKEY-BBBB",
"acceptedChains": ["BTC", "LTC"]
}/api/seller/products/{id}
Edit your listing: name, description, price, images, tags, visibility, chains, sale mode and stock. Owner only. Editing a rejected listing sends it back into the review queue and clears the reviewer's note.
/api/seller/products/{id}
Delete your listing with its serial keys. Refused with 409 while escrows are still active on it. Owner only.
/api/seller/products/{id}/serials
Read serial keys for one of your listings. Owner only. Keys are not exposed in public marketplace responses; delivered buyers see their own order's keys on the purchase page.
/api/seller/products/{id}/serials
Add serial keys, up to 1000 per call. Owner only.
View request body example
{
"serials": ["KEY-CCCC", "KEY-DDDD"]
}/api/seller/products/{id}/serials
Delete given serial ids, or all unclaimed keys when no ids are sent. Owner only.
/api/seller/products/slug-check?slug=&excludeId=
Whether a listing link is taken, for the slug field in the product form. Sellers only: link availability reveals which links exist, so it is not served anonymously.
Checkout From Your Site
Keep your product page on your site and show Limbo checkout in an iframe. Add the storefront origin to the embed key's allowlist, then have your server start an order with that key; the browser uses the short-lived, origin-bound embedUrl. checkoutUrl remains available as a hosted fallback. The checkout and payment APIs still run on Limbo.
- Create the key in the Dashboard under 'Embed Keys'. It starts with em_, is shown exactly once because only a hash is stored, and it can start orders for products its owner sells and nothing else. It cannot read account data, list orders, or act on another account's data.
- Send it from your server and nowhere else. A key in page source, a script tag, a form action or any request a visitor's browser makes is readable by everyone who opens that page, and they can start orders on your products until you revoke it.
- The optional origin allowlist is checked when a key has one: a request with no Origin header or an unlisted origin is refused with 403 EMBED_ORIGIN_NOT_ALLOWED. A server-to-server caller must forward an allowed Origin value if the key has an allowlist. This list is an anti-abuse control, not an authentication boundary, because a server-side caller can forge the header.
- Guest checkout is not supported. The buyer signs in on Limbo, because the routes that release funds authorize the order's own buyer, and a key cannot name a buyer for that order.
- Some browsers ask the buyer to allow Limbo session access inside a cross-site frame. If they decline or the browser blocks it, open checkoutUrl as a regular Limbo page instead.
- Each key has its own order budget, answered with 429 EMBED_ORDER_RATE_LIMITED, and revoking a key takes effect on its next call. The response includes a short-lived embedUrl only when the Origin is explicitly in that key's allowlist, plus checkoutUrl as a hosted fallback. Nothing is charged and no escrow exists until the buyer opens checkout and pays.
/api/embed/order
Start an order for a product owned by the embed key's account. The response includes a hosted checkout URL and, when the Origin is explicitly allowlisted, a signed embed URL limited to that exact origin and valid for 15 minutes. This endpoint does not create an escrow or accept a buyer identity. It returns 401 for an invalid key, 403 for a disallowed origin, 404 for a product the key owner cannot sell, and 409 if the listing is sold out or has no payment method. Accepted orders are subject to the per-key rate limit.
View request and response example
{
"productId": "clx_9f2k1m"
}{
"order": {
"productId": "clx_9f2k1m",
"productName": "Elite Access Key",
"priceUsd": 49.99,
"productType": "SERIAL",
"requiresUserInput": false,
"userInputLabel": null,
"acceptedChains": ["BTC", "LTC"],
"sellerId": "clx_owner_1"
},
"checkoutUrl": "https://limbo.example/embed/checkout/clx_9f2k1m",
"embedUrl": "https://limbo.example/embed/checkout/clx_9f2k1m?frame=SIGNED_SHORT_LIVED_TOKEN"
}Revenue & Analytics
Read account-scoped dashboard totals and recent activity. Earned totals include the seller's applicable fee adjustment; spent totals describe purchases. The activity list combines orders where you are the buyer or seller.
/api/dashboard/stats
GET /api/dashboard/stats accepts optional take (1-200, default 50) and skip (default 0). The response includes account totals, a combined transaction page, and a seven-day seller revenue chart. Earned and chart amounts are fee-adjusted; spent is the buyer's total spend. The chart groups released seller escrows by their creation date.
View response example
{
"stats": { "earned": 0, "spent": 0, "escrows": 0, "products": 0 },
"transactions": [],
"revenueChart": [
{ "date": "Mon", "amount": 0 }
]
}Escrow Operations
Interact with active transactions. Sellers can confirm delivery for 'SERVICE' type products to trigger the buyer's confirmation window.
const key = process.env.LIMBO_API_KEY;
if (!key) throw new Error('Set LIMBO_API_KEY on your server.');
const response = await fetch('https://limbo.ink/api/escrow/confirm', {
method: 'POST',
headers: { 'Authorization': `Bearer ${key}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
escrowId: 'esc_9921',
action: 'seller_deliver'
})
});
if (!response.ok) throw new Error('Delivery update failed: ' + response.status);Purchase Webhooks
Limbo queues purchase events durably when confirmed payments are processed. Generate your own signing key in Dashboard / API Access before enabling delivery. Verify X-Webhook-Signature using HMAC-SHA256 over X-Webhook-Timestamp, a period, and the exact raw request body; reject timestamps older than five minutes and deduplicate X-Webhook-Idempotency-Key. Events may be retried up to twelve times. Inspect the payload status: HELD is not proof of delivery. Each attempt appears in Webhook logs. Unsigned delivery is never sent.
{
"event": "purchase.completed",
"timestamp": "2026-04-23T18:45:00Z",
"data": {
"orderId": "clx_9921j882b",
"productName": "Lifetime Subscription",
"amountUsd": 99.00,
"buyerId": "user_123",
"status": "DELIVERED",
"productType": "SERIAL"
}
}Example values are illustrative. Availability, credentials and permissions depend on the route and your account.