# Cria um pedido de venda aberto, com a reserva feita

**POST** `/v1/objects/sales-order`

Duas formas, o mesmo pedido. Com `csv`, é a planilha de alguém: falta de saldo recusa o pedido inteiro. Com `lines`, é um sistema (o ERP ou o programa do cliente): o pedido entra mesmo com falta, e cada linha diz quanto faltou em `shortage`. Sem `depot_code`, vale o CD ativo mais antigo.

> **Ação da chave: `sales_order.import`** Quem cria a chave escolhe as ações que ela abre; sem esta, a rota recusa com `api_key_action_not_granted`.

> **Toda escrita pede `Idempotency-Key`** Um UUID por escrita. Repetida com o mesmo corpo, devolve a mesma resposta sem escrever de novo. Os SDKs mandam sozinhos.

## Corpo

Mande **uma** destas formas:

### Planilha

- `csv` (string, obrigatório): Um pedido por arquivo, cabeçalho `order_ref;customer;product;qty;carrier_cnpj`. O produto é o EAN ou o SKU.
- `depot_code` (string): O código do CD. Sem ele, o CD ativo mais antigo.

### Sistema

- `carrier_cnpj` (string): O CNPJ da transportadora, com 14 dígitos.
- `channel` (string): O canal da venda, pelo código do catálogo (`shopee`, `mercado_livre`…).
- `channel_ref` (string): O número do pedido no canal.
- `customer` (string)
- `customer_area` (string): O bairro da entrega.
- `depot_code` (string): O código do CD. Sem ele, o CD ativo mais antigo.
- `lines` (lista de objeto, obrigatório)
  - `product` (string, obrigatório): O EAN ou o SKU do produto.
  - `qty` (integer, obrigatório)
  - `unit_price` (number)
- `order_ref` (string, obrigatório): O número do pedido no sistema de origem. Repetido, recusa com `order_already_imported`.
- `origin` (string): O parceiro que manda o pedido (`bling`, `tiny`, `shopify`…). Sem ele, `custom_api`.
- `paid_note` (string): A frase do pagamento, como o canal escreveu.

## Devolve

O `data` do envelope `{ ok, data, error, error_code, field, trace_id }`:

- `sales_order` (objeto, obrigatório)
  - `carrier_cnpj` (string, ou nulo)
  - `channel` (string, ou nulo)
  - `channel_ref` (string, ou nulo)
  - `customer` (string, ou nulo)
  - `customer_area` (string, ou nulo)
  - `id` (uuid)
  - `lines` (integer): Quantas linhas o pedido tem.
  - `minted` (integer): 1 quando o código curto do pedido foi criado.
  - `order_ref` (string)
  - `origin` (string): `orders_csv` para a planilha; o parceiro, ou `custom_api`, para o sistema.
  - `outcome` ("ok")
  - `paid_note` (string, ou nulo)
  - `short_units` (integer): Quantas unidades entraram sem saldo (só no pedido de sistema).
  - `shortage` (lista de objeto)
    - `product_id` (uuid)
    - `qty` (integer)
    - `seq` (integer)
    - `short_qty` (integer)
  - `status` ("open")
  - `units` (integer): Quantas unidades, somadas.

## Recusas

### HTTP 400 · Pedido mal formado

- [`invalid_idempotency_key`](/erros/invalid_idempotency_key.md) (HTTP 400): Toda escrita pede o cabeçalho `Idempotency-Key` com um UUID.
- [`version_unknown`](/erros/version_unknown.md) (HTTP 400): A `Kernos-Version` pedida não existe. Use uma das versões declaradas no cabeçalho, ou não o mande.

### HTTP 401 · Sem credencial válida

- [`api_key_expired`](/erros/api_key_expired.md) (HTTP 401): A chave venceu. Gire a chave no painel e use a nova.
- [`api_key_wrong_environment`](/erros/api_key_wrong_environment.md) (HTTP 401): A chave é do outro ambiente: `ksk_test_` vai em https://stag.kernos.com.br, `ksk_live_` em https://api.kernos.com.br.
- [`unauthenticated`](/erros/unauthenticated.md) (HTTP 401): Credencial ausente, inválida ou expirada.

### HTTP 403 · Sem permissão

- [`agent_token_out_of_scope`](/erros/agent_token_out_of_scope.md) (HTTP 403): O token de agente (`kat_`) só vale na porta MCP. Para a API, use o token de sessão ou uma chave de integração.
- [`api_key_action_not_granted`](/erros/api_key_action_not_granted.md) (HTTP 403): A chave não tem esta ação. Edite as ações da chave no painel.
- [`api_key_out_of_scope`](/erros/api_key_out_of_scope.md) (HTTP 403): A chave só vale nas rotas do contrato público.
- [`depot_out_of_reach`](/erros/depot_out_of_reach.md) (HTTP 403): Quem chama não alcança o CD deste objeto. Peça alcance ao CD ou use outra credencial.
- [`forbidden_role`](/erros/forbidden_role.md) (HTTP 403): O papel de quem chama nesta empresa não permite esta ação.
- [`module_not_subscribed`](/erros/module_not_subscribed.md) (HTTP 403): A empresa não assina o módulo desta rota.
- [`tenant_mismatch`](/erros/tenant_mismatch.md) (HTTP 403): A credencial não pertence à empresa do endereço chamado.
- [`tenant_unavailable`](/erros/tenant_unavailable.md) (HTTP 403): A empresa está indisponível no momento.

### HTTP 404 · Não encontrado

- [`depot_not_found`](/erros/depot_not_found.md) (HTTP 404): Não existe CD com esse `depot_code` nesta empresa.

### HTTP 409 · Conflito

- [`depot_inactive`](/erros/depot_inactive.md) (HTTP 409): O CD está fechado. Reabra o CD antes de mexer no que está dentro dele.
- [`idempotency_key_reused`](/erros/idempotency_key_reused.md) (HTTP 409): Esta `Idempotency-Key` já foi usada com outro corpo. Gere uma chave nova para uma escrita nova.
- [`no_active_depot`](/erros/no_active_depot.md) (HTTP 409): A empresa não tem CD ativo; sem `depot_code`, não há CD a usar.
- [`order_already_imported`](/erros/order_already_imported.md) (HTTP 409): Este pedido (`order_ref` da mesma origem) já foi importado.
- [`stock_not_reservable`](/erros/stock_not_reservable.md) (HTTP 409): O disponível do CD (saldo menos o já reservado) não cobre alguma linha do pedido.

### HTTP 422 · Não processável

- [`invalid_channel`](/erros/invalid_channel.md) (HTTP 422): O canal não está no catálogo da Kernos: use o código de um canal (`shopee`, `mercado_livre`…) ou omita.
- [`invalid_order_csv`](/erros/invalid_order_csv.md) (HTTP 422): O arquivo não está no layout do pedido: cabeçalho `order_ref;customer;product;qty;carrier_cnpj`, um pedido por arquivo.
- [`invalid_order_json`](/erros/invalid_order_json.md) (HTTP 422): O pedido não está no formato declarado no corpo da rota; o `field` diz qual campo.
- [`invalid_origin`](/erros/invalid_origin.md) (HTTP 422): A origem não está no catálogo da Kernos: use o código do parceiro que manda o pedido (`bling`, `tiny`…) ou omita.
- [`invalid_qty`](/erros/invalid_qty.md) (HTTP 422): Quantidade inválida: um inteiro maior que zero.
- [`order_line_unknown_product`](/erros/order_line_unknown_product.md) (HTTP 422): Há linha com produto fora do catálogo (nem pelo EAN, nem pelo SKU). Cadastre o produto antes de importar.

### HTTP 429 · Muitos pedidos

- [`rate_limited`](/erros/rate_limited.md) (HTTP 429): Muitos pedidos por segundo com esta chave. Espere o `Retry-After`, em segundos, e tente de novo.

#### curl

**Cria um pedido de venda aberto, com a reserva feita**

```sh
curl -X POST "https://stag.kernos.com.br/v1/objects/sales-order" \
  -H "Authorization: Bearer ksk_test_…" \
  -H "Kernos-Version: 2026-10-03" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "order_ref": "PED-1001",
    "customer": "Loja Centro",
    "lines": [{ "product": "7891234567895", "qty": 2 }]
  }'
```

#### Node

**Cria um pedido de venda aberto, com a reserva feita**

```js
import { Kernos } from '@kernosbr/kernos'

const kernos = new Kernos(process.env.KERNOS_API_KEY)
const result = await kernos.salesOrder.import({
  order_ref: 'PED-1001',
  customer: 'Loja Centro',
  lines: [{ product: '7891234567895', qty: 2 }]
})
```

#### Python

**Cria um pedido de venda aberto, com a reserva feita**

```python
import os
from kernos import Kernos

kernos = Kernos(os.environ["KERNOS_API_KEY"])
result = kernos.sales_order.import_({
  "order_ref": "PED-1001",
  "customer": "Loja Centro",
  "lines": [{"product": "7891234567895", "qty": 2}]
})
```

#### PHP

**Cria um pedido de venda aberto, com a reserva feita**

```php
<?php
$kernos = new Kernos\Kernos(getenv('KERNOS_API_KEY'));
$result = $kernos->salesOrder->import([
  'order_ref' => 'PED-1001',
  'customer' => 'Loja Centro',
  'lines' => [['product' => '7891234567895', 'qty' => 2]]
]);
```

#### Java

**Cria um pedido de venda aberto, com a reserva feita**

```java
Kernos kernos = new Kernos(System.getenv("KERNOS_API_KEY"));
JsonNode result = kernos.salesOrder().importOrder(Map.of(
    "order_ref", "PED-1001",
    "customer", "Loja Centro",
    "lines", List.of(Map.of("product", "7891234567895", "qty", 2))
));
```

#### C#

**Cria um pedido de venda aberto, com a reserva feita**

```csharp
var kernos = new KernosClient(Environment.GetEnvironmentVariable("KERNOS_API_KEY"));
var result = await kernos.SalesOrder.ImportAsync(new {
  order_ref = "PED-1001",
  customer = "Loja Centro",
  lines = new[] {new { product = "7891234567895", qty = 2 }}
});
```

#### Go

**Cria um pedido de venda aberto, com a reserva feita**

```go
client, err := kernos.New(os.Getenv("KERNOS_API_KEY"))
result, err := client.SalesOrder.Import(ctx, map[string]any{
  "order_ref": "PED-1001",
  "customer": "Loja Centro",
  "lines": []any{map[string]any{"product": "7891234567895", "qty": 2}},
})
```

#### Rust

**Cria um pedido de venda aberto, com a reserva feita**

```rust
let kernos = Kernos::new(&std::env::var("KERNOS_API_KEY")?)?;
let result = kernos.sales_order().import(&json!({
  "order_ref": "PED-1001",
  "customer": "Loja Centro",
  "lines": [{ "product": "7891234567895", "qty": 2 }]
}), None)?;
```

**Resposta · 200 (a forma, sem dado real)**

```json
{
  "ok": true,
  "data": {
    "sales_order": {
      "carrier_cnpj": "…",
      "channel": "…",
      "channel_ref": "…",
      "customer": "…",
      "customer_area": "…",
      "id": "3f1e0c8a-5b7d-4c21-9a3e-0b1c2d3e4f50",
      "lines": 0,
      "minted": 0,
      "order_ref": "…",
      "origin": "…",
      "outcome": "ok",
      "paid_note": "…",
      "short_units": 0,
      "shortage": [
        {
          "product_id": "3f1e0c8a-5b7d-4c21-9a3e-0b1c2d3e4f50",
          "qty": 0,
          "seq": 0,
          "short_qty": 0
        }
      ],
      "status": "open",
      "units": 0
    }
  },
  "error": null,
  "error_code": null,
  "field": null,
  "trace_id": null
}
```

> **Ambiente** `ksk_test_` fala com `https://stag.kernos.com.br`; `ksk_live_`, com `https://api.kernos.com.br`. O prefixo da chave escolhe o endereço.
