/v1/ecommerce prefix, authenticated with a PAT and gated by scope.
Products and carts stay read-only: creating or editing a product goes through the portal or the platform integration, not this API.
Data model
Products
The catalog: price, stock, variants, images. Includes both products created in Keebai and those synced from an integration.
Carts
What a customer put together, whether or not they finished. Abandoned carts carry a recovery status and a link back to checkout.
Sales
Closed transactions with their line items, amount breakdown, and payment state.
Inventory
Stock per variant, split between the warehouse and branches, with adjustments, transfers, and the movement history.
Electronic invoicing
Boletas and facturas for your sales, with their folio, status, and PDF.
Sale events
Three webhooks fire as a sale is created, paid, and cancelled.
customer_id, which both carry when Keebai could resolve the contact.
Scopes
The read scopes are separate because they leak different things. Carts and sales carry customer contact details and money; the catalog carries neither. A pricing widget on your website needs
ecommerce:products:read and nothing else.
Products come from two places
stock is only meaningful when stock_managed is true. On integration products, stock is whatever the last sync brought over — not a live read of the platform.
Inventory
A variant’s stock has one master total. Part of it is allocated to branches and the rest is available in the warehouse.GET /v1/ecommerce/products/{id}/stock returns all three, plus the quantity per branch.
Every change leaves an entry in the stock movements, and adjustments are synced to the stores you have connected. To find the
product_id and variant_id from a scanned code, use GET /v1/ecommerce/products/find.
Recording sales and invoicing
POST /v1/ecommerce/sales records a sale from your own point of sale: it deducts stock, calculates commissions, and, with dte, issues the receipt (boleta) or invoice (factura) right after. Lines can be catalog products or free lines, such as a service outside the catalog, with their own name. Each line takes a price with VAT (price) or a net price (net_price), and the document shows that net exactly. For an invoice to a customer you invoiced before, the RUT alone is enough. A type: "internal" sale records internal consumption instead, with no payments and no DTE.
Send an Idempotency-Key header so a retry after a timeout does not create the sale twice.
A DTE that fails does not undo the sale. It comes back in dte.failed with its reason, and you issue it later with POST /v1/ecommerce/sales/{id}/dte. Check which documents the company can issue today with GET /v1/ecommerce/invoicing/types.
A receipt always needs a paid sale. An invoice can go on credit: when the company enables credit invoices in Sales › Settings, an invoice on a sale that is not fully paid is issued with a due date (dte.due_date, or today plus the company’s credit days).
Abandoned carts
A cart withabandoned_at set and completed_at absent is the recoverable case. recovery_status tracks what has been done about it:
recovery_url is the link that puts the customer back in checkout with the cart intact. Sending it over WhatsApp with a cta_url message is the intended pairing.
A cart becomes abandoned on a timer, not on an explicit action, so abandoned_at trails the customer’s last activity. Do not treat a cart as fresh just because abandoned_at is recent.
Identifiers
All ids are MongoDB ObjectIds — 24 lowercase hex characters. That covers products, carts, sales, and thecustomer_id that carts and sales carry when Keebai could resolve the buyer to a contact.
external_id is different: it is the id the record has in the source platform (Shopify, WooCommerce, Justo), in that platform’s own format. Use it to join back to your storefront; never pass it where Keebai expects an ObjectId.
Tenancy is implicit
Company comes from your token and is never sent. Sendingcompany returns 400.
As the warning above notes, reads span the whole company rather than one project — a token minted for project A lists products, carts, and sales created under project B.
Correlating with the rest of the platform
Ecommerce records are deliberately loosely coupled, which means joining them is your job:
If you need a hard link between a sale and a CRM ticket, emit a CRM event carrying your order id in a ticket-scoped custom field. That writes the id onto the ticket, which is the closest thing to a foreign key the platform offers.
Money is stored in minor units
Every amount —price, unit_price, subtotal, total, and the whole amounts block on a sale — is an integer in the currency’s smallest unit. Read currency alongside it, and never assume two decimals: JPY and CLP have none.
Pagination
List endpoints takelimit and offset and return the same envelope as the rest of the API:
limit defaults to 50 and caps at 200.
The catalog paginates by page internally, so an
offset that is not a multiple of limit is rounded down to the page containing it. Keep offset a multiple of limit — the standard offset += limit loop is always correct.Errors
Branch on
error.code, not on the message text. A 4xx keeps the business code the ecommerce service returned, such as STOCK_INSUFFICIENT_IN_BRANCH or SKU_NOT_FOUND.
Rate limits
These endpoints draw from the shared per-project quota: 1,000 requests a day across the whole public API. That is the binding constraint for anyone mirroring a catalog. A 4,000-product catalog atlimit=200 is 20 requests — fine. The same catalog re-synced every 15 minutes is 1,920 a day — not fine.
Sync on a webhook, not a timer
ecommerce.sale.created / .paid / .cancelled tell you when something changed. Polling the sales list to discover it costs a request every time nothing happened.Page at the ceiling
limit=200 is four times cheaper than the default 50 for the same data.Next steps
Browse the catalog
Start with products — the simplest of the three.
React to sales
Subscribe to
ecommerce.sale.paid and push it into your accounting system.Chase abandoned carts
Filter for
abandoned_at and send recovery_url over WhatsApp.Record sales from your POS
Create the sale, deduct stock, and issue the boleta in one call.
Keep stock in step
Adjust and transfer stock from your ERP and audit the movements.
Record it on the CRM
Emit a CRM event so the order shows up on the contact’s timeline and the agent knows about it.