Skip to main content
POST
Crear una venta
Creates a sale in Keebai. Stock is deducted from the branch, commissions are calculated, and, if you pass dte, the electronic tax document is issued right after. It is also the way to issue an invoice: a free line with a net price, a known customer RUT and a payment method are enough. Everything that can fail — receiver data, payment, credit, branch, the document type, SKUs — is checked before the sale is created. A type: "internal" sale records internal consumption instead: products taken out of stock for the business’s own use, with no payments and no DTE.

Endpoint

Required scope

ecommerce:sales:write Sending dte also needs ecommerce:invoicing:issue. Without it the request fails with 403 INSUFFICIENT_SCOPE before the sale is created.
The invoicing scope comes from your role. Minting a token with ecommerce:invoicing:issue requires the permission to issue DTE in your Keebai role, unless you are the company owner or a superuser. See scopes.

Headers

Body

Item

Payment

DTE

Check which documents your company can issue today, and whether it allows credit invoices, with GET /v1/ecommerce/invoicing/types.

Example requests

JavaScript
Python

Response

201 Created

sale has the same shape as GET /v1/ecommerce/sales/{id}. dte is present only when you sent dte.

400 Bad Request

The body does not match the schema, a field the endpoint does not accept, or one of these. Nothing is created.

401 Unauthorized

Missing, invalid, revoked, or expired token.

403 Forbidden

INSUFFICIENT_SCOPE — the token does not have ecommerce:sales:write, or it sent dte without ecommerce:invoicing:issue. Nothing is created.

422 Unprocessable Entity

502 Bad Gateway

ECOMMERCE_UPSTREAM_ERROR — the ecommerce service could not answer.

Operational notes

  • Send an Idempotency-Key from a point of sale. A timeout does not tell you whether the sale was created. Retrying with the same key returns 201 with the original sale and creates nothing new; without it, a retry can double the sale and the stock deduction.
  • Errors are caught before anything is created. Every 400 and 422 above leaves no sale and no stock movement behind.
  • A failed confirmation leaves nothing behind. If the sale cannot be confirmed — for example INVALID_PAYMENT or STOCK_INSUFFICIENT_IN_BRANCH — the cart is discarded and the error is returned.
  • A failed DTE does not undo the sale. The sale stays created and the failure comes back in dte.failed. Fix the cause and issue it later with POST /v1/ecommerce/sales/{id}/dte.
  • A receipt needs a paid sale. A receipt whose payments do not cover the sale fails with PAYMENT_REQUIRED before the sale is confirmed.
  • An invoice can go on credit. When the company enables credit invoices in Sales › Settings, an invoice or exempt_invoice on a sale that is not fully paid is issued with a due date (due_date, or today plus the company’s credit days). Otherwise it fails with CREDIT_NOT_ENABLED and nothing is created. Record the payment later from the Keebai portal or with the MCP tool keebai_ecommerce_sale_payment_add.
  • Net prices are exact. With net_price the document shows exactly that net. A product line without its own price in the same sale keeps the catalog price it has at that moment.
  • internal never issues a DTE. Internal consumption has no payments and no tax document; it only moves stock.
  • Lines are catalog products or free lines. Send product_id (with variant_id when it has variants) or sku for a product; without price, net_price or name, the product’s are used. A line with neither product_id nor sku is a free line, such as a service or a charge outside the catalog: it needs name and a price, and moves no stock.
  • Commissions are calculated on the sale. commissionee on a line decides who earns it.

Authorizations

Authorization
string
header
required

Personal Access Token con prefijo kbai_pk_. Generar desde el portal con permiso developer.manage_tokens.

Headers

idempotency-key
string
required
Idempotency-Key
string

Identificador único de la venta para reintentos. Sin él se genera uno

Body

application/json
items
object[]
required
type
enum<string>
default:sale

sale venta normal con pagos; internal venta interna, solo productos, sin pagos ni DTE

Available options:
sale,
internal
branch_id
string

Sucursal de la venta. Sin ella se usa la única sucursal activa de la empresa

customer
object
seller
object
payments
object[]
notes
string
Maximum string length: 1000
sold_at
string

Fecha de la venta, ISO 8601

currency
string
default:CLP
Example:

"CLP"

dte
object

Documento tributario a emitir después de confirmar la venta. Si falla, la venta queda igual y el error va en dte.failed

Response

201 - application/json
sale
object
required
dte
object

Solo cuando se pidió dte