Skip to content

Create a sale ​

http
POST /sales/orders

Requires sales.orders:write and API key authentication. The creator is recorded as API · integration name; no staff session or seller configured on the credential is required.

Supported flow ​

Creates drafts only, without payments, manual discounts, fiscal issuance or inventory processing. Prices must match the applicable catalog for the company, customer, currency, channel and unit of measure. Different prices return 403, even if the seller has internal price-editing permissions.

Retries and external references ​

Send a stable external_id for each sale and reuse it when retrying after a timeout. The first creation returns 201; the same reference with the same API key returns 409 with code: "external_id_conflict", without creating or modifying the existing sale, even if the body changes. The constraint also covers concurrent requests. Another API key may use the same reference. Omitting the field or sending null allows each request to create a new sale.

Request fields ​

FieldTypeRequirement
origin_warehouse_idintegerRequired; existing warehouse.
issue_datestringRequired, YYYY-MM-DD.
document_codestringRequired; existing document type code.
currencystringRequired; existing three-character currency code.
sale_channel_codestringRequired; existing sales channel code.
payment_term_idintegerRequired; existing payment term.
include_taxbooleanRequired; whether prices include tax.
sourcestringOptional; only api, set by the server if omitted.
is_draftbooleanOptional; only true, default true.
process_inventorybooleanOptional; only false, default false.
external_idstring or nullOptional, up to 255 characters; unique per API key. Reusing it returns 409.
exchange_ratenumberOptional, positive; default 1.
seller_idinteger or nullOptional active company member; also requires sales.orders:assign-seller. Does not change the creator.
notesstring or nullOptional, up to 5000 characters.
customerobjectRequired; see below.
itemsarrayRequired; 1–200 items.

Customer requires name (up to 255 characters), tax_id (up to 50), and an existing identity_document_type_code. Optional: valid email, billing_address and shipping_address; each address is an object with an address string of up to 255 characters, or null.

Each item requires sku (existing product), name (up to 255 characters), positive qty, non-negative price, existing uom_code, tax_exemption_code and at least one existing tax code in tax_codes. Tax codes are trimmed, uppercased and deduplicated. ICBPER also requires IGV.

Optional external actor ​

Send X-External-Actor-Id to retain an external user reference for auditing (up to 255 characters, no controls). It is declared by the integration and never authenticates a user or changes permissions. See identity and traceability.

Example ​

Save as sale.json. IDs, SKU, document, unit and channel codes are illustrative and must exist in your company. The price must match your catalog.

json
{
  "origin_warehouse_id": 1,
  "external_id": "integracion-venta-0001",
  "issue_date": "2026-11-05",
  "document_code": "01",
  "currency": "PEN",
  "sale_channel_code": "DIRECT",
  "source": "api",
  "payment_term_id": 1,
  "include_tax": true,
  "process_inventory": false,
  "is_draft": true,
  "customer": {
    "name": "Empresa de ejemplo SAC",
    "identity_document_type_code": "RUC",
    "tax_id": "20123456789",
    "email": "integraciones@example.com",
    "billing_address": {
      "address": "Av. Ejemplo 123, Lima"
    },
    "shipping_address": null
  },
  "items": [
    {
      "sku": "PRODUCTO-001",
      "name": "Producto de ejemplo",
      "qty": 1,
      "uom_code": "NIU",
      "price": 118,
      "tax_exemption_code": "10",
      "tax_codes": ["IGV"]
    }
  ],
  "notes": "Venta creada desde una integración"
}
bash
curl --request POST 'https://api.simple.pe/sales/orders' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header "X-Api-Key: ${SIMPLE_API_KEY}" \
  --header "X-Api-Secret: ${SIMPLE_API_SECRET}" \
  --data-binary @sale.json

Response · 201 Created ​

Illustrative excerpt, not a captured response:

json
{
  "data": {
    "id": 123,
    "external_id": "integracion-venta-0001",
    "source": "api",
    "seller_id": null,
    "created_by": {
      "type": "api_key",
      "id": 42,
      "name": "Shopify",
      "label": "API · Shopify",
      "external_actor_id": null
    },
    "status": "draft",
    "currency": "PEN",
    "subtotal_net": "100.00",
    "tax_total": "18.00",
    "total": "118.00"
  }
}

Amounts are serialized as strings. The response also includes issue_date, customer, items, nullable seller_id, and created_by identifying the creator. The public response is independent from the internal application response.

Errors ​

401: invalid credentials. 403: access denied, insufficient scope or catalog-price mismatch. 409: external_id already exists for this API key (external_id_conflict). 422: invalid fields, invalid/inactive seller, seller from another company, or invalid external actor header. 429: rate limit exceeded.

Payments, advances, manual discounts, fiscal issuance and attachments are outside this initial contract. Do not send internal fields such as send_to_fiscal, payments or global_discount. Only documented fields are processed.

Simple · API for third-party integrations