# Abre uma OS de guarda, separação, contagem, movimento ou retirada

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

`kind` é `putaway`, `pick`, `count`, `move` ou `unplace`. Cada item diz o produto (`ean_or_sku` ou `product_id`), a posição e a quantidade; o `move` pede também `from_location_code`. A contagem de um corredor inteiro é `scope: aisle` com `location_code`, sem itens. O recebimento não se abre por aqui: nasce da nota.

> **Ação da chave: `work_order.create`** 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

- `depot_code` (string): O código do CD. Sem ele, o CD ativo mais antigo.
- `items` (lista de objeto)
  - `ean_or_sku` (string): O EAN ou o SKU do item (ou `product_id`).
  - `from_location_code` (string): No `move`: a posição de onde sai.
  - `location_code` (string): A posição, com ou sem separador.
  - `lot_code` (string): Obrigatório no item que controla lote.
  - `place_kind` ("picking" | "bulk" | ""): Na guarda: a face de separação ou o pulmão; `bulk` por padrão.
  - `product_id` (uuid)
  - `qty` (integer ou string): Inteiro maior que zero.
  - `stock_status` (string): No `move`: o estado do saldo que se move; `available` por padrão.
- `kind` ("putaway" | "pick" | "count" | "move" | "unplace", obrigatório)
- `location_code` (string): Com `scope: aisle`: o corredor.
- `priority` (integer ou string): 1, 2 ou 3.
- `scope` ("position" | "aisle" | ""): `aisle` só na contagem: as paradas são o corredor inteiro de `location_code`.
- `source` ("qr-camera" | "code128-gun" | "typed" | "import", obrigatório): De onde veio o pedido de escrita: câmera, leitor, digitado ou sistema (`import`).

## Devolve

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

- `work_order` (objeto, obrigatório)
  - `depot_code` (string)
  - `id` (uuid)
  - `items` (integer)
  - `items_done` (integer)
  - `kind` (string): `receive`, `putaway`, `pick`, `count`, `move`, `replenish` ou `unplace`.
  - `priority` (integer)
  - `short_code` (string, ou nulo)
  - `status` (string): `open`, `in_progress`, `done` ou `cancelled`.

## 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.
- [`not_found`](/erros/not_found.md) (HTTP 404): Não existe objeto com esse identificador 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.
- [`other_depot`](/erros/other_depot.md) (HTTP 409): A posição é de outro CD.
- [`picking_face_exists`](/erros/picking_face_exists.md) (HTTP 409): O produto já tem posição de separação neste CD. Guarde como pulmão ou desenderece a separação antes.

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

- [`invalid_kind`](/erros/invalid_kind.md) (HTTP 422): Tipo de ordem de serviço inválido: use um dos valores declarados no campo.
- [`invalid_move_origin`](/erros/invalid_move_origin.md) (HTTP 422): Transferência precisa da posição de origem, além da de destino.
- [`invalid_params`](/erros/invalid_params.md) (HTTP 422): Um parâmetro está fora do formato declarado; o `field` diz qual.
- [`invalid_place_kind`](/erros/invalid_place_kind.md) (HTTP 422): Tipo de guarda inválido para esta parada.
- [`invalid_priority`](/erros/invalid_priority.md) (HTTP 422): Prioridade inválida: use um dos valores declarados no campo.
- [`invalid_qty`](/erros/invalid_qty.md) (HTTP 422): Quantidade inválida: um inteiro maior que zero.
- [`invalid_scope`](/erros/invalid_scope.md) (HTTP 422): Recontar o corredor inteiro só existe na ordem de contagem.
- [`invalid_source`](/erros/invalid_source.md) (HTTP 422): Origem da leitura inválida: use um dos valores declarados no campo.
- [`items_required`](/erros/items_required.md) (HTTP 422): A ordem precisa de pelo menos uma parada.
- [`lot_not_found`](/erros/lot_not_found.md) (HTTP 422): O lote não existe para este produto. O lote nasce no recebimento da nota.
- [`lot_required`](/erros/lot_required.md) (HTTP 422): O produto é rastreado por lote: diga de qual lote é o item.
- [`no_aisle`](/erros/no_aisle.md) (HTTP 422): O padrão de endereço deste CD não tem corredor: reconte posição por posição.

### 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

**Abre uma OS de guarda, separação, contagem, movimento ou retirada**

```sh
curl -X POST "https://stag.kernos.com.br/v1/objects/work-order" \
  -H "Authorization: Bearer ksk_test_…" \
  -H "Kernos-Version: 2026-10-03" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "count",
    "source": "import",
    "items": [
      {
        "ean_or_sku": "7899990000021",
        "location_code": "TREINO-01",
        "qty": 5
      }
    ]
  }'
```

#### Node

**Abre uma OS de guarda, separação, contagem, movimento ou retirada**

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

const kernos = new Kernos(process.env.KERNOS_API_KEY)
const result = await kernos.workOrder.create({
  kind: 'count',
  source: 'import',
  items: [{ ean_or_sku: '7899990000021', location_code: 'TREINO-01', qty: 5 }]
})
```

#### Python

**Abre uma OS de guarda, separação, contagem, movimento ou retirada**

```python
import os
from kernos import Kernos

kernos = Kernos(os.environ["KERNOS_API_KEY"])
result = kernos.work_order.create({
  "kind": "count",
  "source": "import",
  "items": [
    {"ean_or_sku": "7899990000021", "location_code": "TREINO-01", "qty": 5}
  ]
})
```

#### PHP

**Abre uma OS de guarda, separação, contagem, movimento ou retirada**

```php
<?php
$kernos = new Kernos\Kernos(getenv('KERNOS_API_KEY'));
$result = $kernos->workOrder->create([
  'kind' => 'count',
  'source' => 'import',
  'items' => [
    [
      'ean_or_sku' => '7899990000021',
      'location_code' => 'TREINO-01',
      'qty' => 5
    ]
  ]
]);
```

#### Java

**Abre uma OS de guarda, separação, contagem, movimento ou retirada**

```java
Kernos kernos = new Kernos(System.getenv("KERNOS_API_KEY"));
JsonNode result = kernos.workOrder().create(Map.of(
    "kind", "count",
    "source", "import",
    "items", List.of(
        Map.of(
            "ean_or_sku", "7899990000021",
            "location_code", "TREINO-01",
            "qty", 5
        )
    )
));
```

#### C#

**Abre uma OS de guarda, separação, contagem, movimento ou retirada**

```csharp
var kernos = new KernosClient(Environment.GetEnvironmentVariable("KERNOS_API_KEY"));
var result = await kernos.WorkOrder.CreateAsync(new {
  kind = "count",
  source = "import",
  items = new[] {
    new {
      ean_or_sku = "7899990000021",
      location_code = "TREINO-01",
      qty = 5
    }
  }
});
```

#### Go

**Abre uma OS de guarda, separação, contagem, movimento ou retirada**

```go
client, err := kernos.New(os.Getenv("KERNOS_API_KEY"))
result, err := client.WorkOrder.Create(ctx, map[string]any{
  "kind": "count",
  "source": "import",
  "items": []any{
    map[string]any{
      "ean_or_sku": "7899990000021",
      "location_code": "TREINO-01",
      "qty": 5,
    },
  },
})
```

#### Rust

**Abre uma OS de guarda, separação, contagem, movimento ou retirada**

```rust
let kernos = Kernos::new(&std::env::var("KERNOS_API_KEY")?)?;
let result = kernos.work_order().create(&json!({
  "kind": "count",
  "source": "import",
  "items": [
    {
      "ean_or_sku": "7899990000021",
      "location_code": "TREINO-01",
      "qty": 5
    }
  ]
}), None)?;
```

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

```json
{
  "ok": true,
  "data": {
    "work_order": {
      "depot_code": "…",
      "id": "3f1e0c8a-5b7d-4c21-9a3e-0b1c2d3e4f50",
      "items": 0,
      "items_done": 0,
      "kind": "…",
      "priority": 0,
      "short_code": "…",
      "status": "…"
    }
  },
  "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.
