OTL ShoesAPI
Exemplos em
Menu da documentação

Simuladores do sandbox

No ambiente de testes, você faz o papel da equipe OTL — paga o pedido, valida o comprovante, lança um crédito — para testar o fluxo inteiro sem esperar ninguém.

Em produção, várias coisas só acontecem quando alguém da OTL age: confirmar o pagamento de um pedido, conferir um comprovante, lançar um crédito no saldo. No sandbox ninguém faz isso por você — então existem rotas que simulam essas ações.

O simulador não é um atalho: ele faz a mudança de verdade na sua conta de teste, e o webhook correspondente sai como sairia em produção. É assim que se testa uma automação de ponta a ponta.

Só existem no sandbox

Todas as rotas desta página começam com /v1/sandbox e respondem apenas em https://api-sandbox.otlshoes.com.br. Em produção elas não existem: a chamada responde 404. Não deixe nenhuma delas no código que vai para a produção.

  • Dá para testar daqui mesmo: cada simulador abaixo tem o painel Testar agora, e todos estão na coleção do Postman, na pasta “Simuladores (sandbox)”.
  • Qualquer token de teste (otl_sbx_…) pode chamar, independente das permissões da integração: quem age aqui é a “OTL”, não o parceiro.
  • O alcance é a conta do token. Pedido de outra conta de teste responde 404.
  • Os simuladores contam no limite de requisições como qualquer outra rota.
Simulador O que a OTL faria Webhook que dispara
Status do pedido Confirmar pagamento, separar, enviar, entregar, cancelar pedido.status_alterado
Validar comprovante Conferir o comprovante de pagamento pedido.comprovante_validado
Validar rastreio Conferir o código de rastreio —
Status da etiqueta Imprimir a etiqueta, despachar a caixa etiqueta.status_alterado
Lançamento no saldo Lançar um crédito ou débito financeiro.lancamento_criado
Estoque de uma numeração — (em produção vem do sistema de estoque) produto.estoque_atualizado
Resetar a conta Botão “Resetar” da equipe —

Status do pedido

POST/v1/sandbox/pedidos/{id}/status
qualquer token
curl -X POST "https://api-sandbox.otlshoes.com.br/v1/sandbox/pedidos/cmg8k1p2a0007/status" \
  -H "Authorization: Bearer otl_sbx_SEU_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "status": "PAGO"
}'
const resposta = await fetch('https://api-sandbox.otlshoes.com.br/v1/sandbox/pedidos/cmg8k1p2a0007/status', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer otl_sbx_SEU_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "status": "PAGO"
  }),
});

if (!resposta.ok) {
  const { erro } = await resposta.json();
  throw new Error(`${erro.codigo}: ${erro.mensagem} (${erro.requestId})`);
}
const dados = await resposta.json();
<?php
$ch = curl_init('https://api-sandbox.otlshoes.com.br/v1/sandbox/pedidos/cmg8k1p2a0007/status');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer otl_sbx_SEU_TOKEN',
    'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'status' => 'PAGO',
]));

$corpo = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($status >= 400) {
    throw new Exception($corpo['erro']['codigo'] . ': ' . $corpo['erro']['mensagem']);
}
import requests

resposta = requests.post(
    "https://api-sandbox.otlshoes.com.br/v1/sandbox/pedidos/cmg8k1p2a0007/status",
    headers={
        "Authorization": "Bearer otl_sbx_SEU_TOKEN",
    },
    json={
        "status": "PAGO"
    },
    timeout=30,
)

if not resposta.ok:
    erro = resposta.json()["erro"]
    raise RuntimeError(f'{erro["codigo"]}: {erro["mensagem"]} ({erro["requestId"]})')
dados = resposta.json()
Testar agora no ambiente de testes

A chamada é feita de verdade, do seu navegador para a API de testes (https://api-sandbox.otlshoes.com.br/v1). Use um token otl_sbx_…: ele fica só nesta aba, e some ao fechá-la. Token de produção não é aceito aqui.

status aceita AGUARDANDO_PAGAMENTO, PAGO, EM_SEPARACAO, ENVIADO, ENTREGUE, CANCELADO e ESTORNADO. Responde 200 com o pedido inteiro, igual a Detalhar pedido.

  • Qualquer transição é aceita — a equipe também pode voltar um status. Não espere uma ordem fixa.
  • Enviar o status que o pedido já tem não muda nada e não dispara webhook.
  • O webhook pedido.status_alterado traz o statusAnterior.

Validar comprovante

POST/v1/sandbox/pedidos/{id}/anexos/{anexoId}/validar
qualquer token
curl -X POST "https://api-sandbox.otlshoes.com.br/v1/sandbox/pedidos/cmg8k1p2a0007/anexos/cmg8k5z7u0004/validar" \
  -H "Authorization: Bearer otl_sbx_SEU_TOKEN"
const resposta = await fetch('https://api-sandbox.otlshoes.com.br/v1/sandbox/pedidos/cmg8k1p2a0007/anexos/cmg8k5z7u0004/validar', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer otl_sbx_SEU_TOKEN',
  },
});

if (!resposta.ok) {
  const { erro } = await resposta.json();
  throw new Error(`${erro.codigo}: ${erro.mensagem} (${erro.requestId})`);
}
const dados = await resposta.json();
<?php
$ch = curl_init('https://api-sandbox.otlshoes.com.br/v1/sandbox/pedidos/cmg8k1p2a0007/anexos/cmg8k5z7u0004/validar');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer otl_sbx_SEU_TOKEN',
]);

$corpo = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($status >= 400) {
    throw new Exception($corpo['erro']['codigo'] . ': ' . $corpo['erro']['mensagem']);
}
import requests

resposta = requests.post(
    "https://api-sandbox.otlshoes.com.br/v1/sandbox/pedidos/cmg8k1p2a0007/anexos/cmg8k5z7u0004/validar",
    headers={
        "Authorization": "Bearer otl_sbx_SEU_TOKEN",
    },
    timeout=30,
)

if not resposta.ok:
    erro = resposta.json()["erro"]
    raise RuntimeError(f'{erro["codigo"]}: {erro["mensagem"]} ({erro["requestId"]})')
dados = resposta.json()
Testar agora no ambiente de testes

A chamada é feita de verdade, do seu navegador para a API de testes (https://api-sandbox.otlshoes.com.br/v1). Use um token otl_sbx_…: ele fica só nesta aba, e some ao fechá-la. Token de produção não é aceito aqui.

Sem corpo. Responde 200 com o anexo, agora validado: true:

{
  "id": "cmg8k5z7u0004",
  "tipo": "COMPROVANTE",
  "nomeArquivo": "comprovante-pix.pdf",
  "tipoArquivo": "application/pdf",
  "tamanhoBytes": 51234,
  "validado": true,
  "enviadoPeloParceiro": true,
  "criadoEm": "2026-09-20T09:20:00-03:00"
}
  • Só comprovante é validado; outro tipo de anexo responde 400.
  • Depois de validado, o comprovante não pode mais ser removido, e o pedido não aceita outro. É a mesma regra da produção — vale a pena testar.
  • Validar o comprovante não muda o status do pedido. Para simular “pagamento confirmado”, chame também o status com PAGO.

Validar rastreio

POST/v1/sandbox/pedidos/{id}/rastreios/{rastreioId}/validar
qualquer token
curl -X POST "https://api-sandbox.otlshoes.com.br/v1/sandbox/pedidos/cmg8k1p2a0007/rastreios/cmg8n9q4t0001/validar" \
  -H "Authorization: Bearer otl_sbx_SEU_TOKEN"
const resposta = await fetch('https://api-sandbox.otlshoes.com.br/v1/sandbox/pedidos/cmg8k1p2a0007/rastreios/cmg8n9q4t0001/validar', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer otl_sbx_SEU_TOKEN',
  },
});

if (!resposta.ok) {
  const { erro } = await resposta.json();
  throw new Error(`${erro.codigo}: ${erro.mensagem} (${erro.requestId})`);
}
const dados = await resposta.json();
<?php
$ch = curl_init('https://api-sandbox.otlshoes.com.br/v1/sandbox/pedidos/cmg8k1p2a0007/rastreios/cmg8n9q4t0001/validar');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer otl_sbx_SEU_TOKEN',
]);

$corpo = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($status >= 400) {
    throw new Exception($corpo['erro']['codigo'] . ': ' . $corpo['erro']['mensagem']);
}
import requests

resposta = requests.post(
    "https://api-sandbox.otlshoes.com.br/v1/sandbox/pedidos/cmg8k1p2a0007/rastreios/cmg8n9q4t0001/validar",
    headers={
        "Authorization": "Bearer otl_sbx_SEU_TOKEN",
    },
    timeout=30,
)

if not resposta.ok:
    erro = resposta.json()["erro"]
    raise RuntimeError(f'{erro["codigo"]}: {erro["mensagem"]} ({erro["requestId"]})')
dados = resposta.json()
Testar agora no ambiente de testes

A chamada é feita de verdade, do seu navegador para a API de testes (https://api-sandbox.otlshoes.com.br/v1). Use um token otl_sbx_…: ele fica só nesta aba, e some ao fechá-la. Token de produção não é aceito aqui.

Sem corpo. Responde 200 com o rastreio, agora validado: true:

{
  "id": "cmg8n9q4t0001",
  "codigo": "AA123456789BR",
  "transportadora": "Correios",
  "validado": true,
  "cadastradoPeloParceiro": true,
  "criadoEm": "2026-09-22T14:03:00-03:00"
}

Depois de validado, o código não pode mais ser removido pelo parceiro.

Status da etiqueta

POST/v1/sandbox/etiquetas/{id}/status
qualquer token
curl -X POST "https://api-sandbox.otlshoes.com.br/v1/sandbox/etiquetas/cmg8p2r5v0003/status" \
  -H "Authorization: Bearer otl_sbx_SEU_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "status": "DESPACHADA"
}'
const resposta = await fetch('https://api-sandbox.otlshoes.com.br/v1/sandbox/etiquetas/cmg8p2r5v0003/status', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer otl_sbx_SEU_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "status": "DESPACHADA"
  }),
});

if (!resposta.ok) {
  const { erro } = await resposta.json();
  throw new Error(`${erro.codigo}: ${erro.mensagem} (${erro.requestId})`);
}
const dados = await resposta.json();
<?php
$ch = curl_init('https://api-sandbox.otlshoes.com.br/v1/sandbox/etiquetas/cmg8p2r5v0003/status');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer otl_sbx_SEU_TOKEN',
    'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'status' => 'DESPACHADA',
]));

$corpo = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($status >= 400) {
    throw new Exception($corpo['erro']['codigo'] . ': ' . $corpo['erro']['mensagem']);
}
import requests

resposta = requests.post(
    "https://api-sandbox.otlshoes.com.br/v1/sandbox/etiquetas/cmg8p2r5v0003/status",
    headers={
        "Authorization": "Bearer otl_sbx_SEU_TOKEN",
    },
    json={
        "status": "DESPACHADA"
    },
    timeout=30,
)

if not resposta.ok:
    erro = resposta.json()["erro"]
    raise RuntimeError(f'{erro["codigo"]}: {erro["mensagem"]} ({erro["requestId"]})')
dados = resposta.json()
Testar agora no ambiente de testes

A chamada é feita de verdade, do seu navegador para a API de testes (https://api-sandbox.otlshoes.com.br/v1). Use um token otl_sbx_…: ele fica só nesta aba, e some ao fechá-la. Token de produção não é aceito aqui.

status aceita IMPRESSA e DESPACHADA. Responde 200 com a etiqueta, como em Etiquetas do pedido. Cancelar não é simulado: é uma ação do parceiro, pela rota de cancelar.

Para ter uma etiqueta no sandbox, a conta de teste precisa estar conectada à SuperFrete de testes (pelo painel de testes); a emissão usa saldo fictício.

Lançamento no saldo

POST/v1/sandbox/financeiro/lancamentos
curl -X POST "https://api-sandbox.otlshoes.com.br/v1/sandbox/financeiro/lancamentos" \
  -H "Authorization: Bearer otl_sbx_SEU_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "valor": "139.90",
  "descricao": "Devolução do pedido #12"
}'
const resposta = await fetch('https://api-sandbox.otlshoes.com.br/v1/sandbox/financeiro/lancamentos', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer otl_sbx_SEU_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "valor": "139.90",
    "descricao": "Devolução do pedido #12"
  }),
});

if (!resposta.ok) {
  const { erro } = await resposta.json();
  throw new Error(`${erro.codigo}: ${erro.mensagem} (${erro.requestId})`);
}
const dados = await resposta.json();
<?php
$ch = curl_init('https://api-sandbox.otlshoes.com.br/v1/sandbox/financeiro/lancamentos');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer otl_sbx_SEU_TOKEN',
    'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'valor' => '139.90',
    'descricao' => 'Devolução do pedido #12',
]));

$corpo = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($status >= 400) {
    throw new Exception($corpo['erro']['codigo'] . ': ' . $corpo['erro']['mensagem']);
}
import requests

resposta = requests.post(
    "https://api-sandbox.otlshoes.com.br/v1/sandbox/financeiro/lancamentos",
    headers={
        "Authorization": "Bearer otl_sbx_SEU_TOKEN",
    },
    json={
        "valor": "139.90",
        "descricao": "Devolução do pedido #12"
    },
    timeout=30,
)

if not resposta.ok:
    erro = resposta.json()["erro"]
    raise RuntimeError(f'{erro["codigo"]}: {erro["mensagem"]} ({erro["requestId"]})')
dados = resposta.json()
Testar agora no ambiente de testes

A chamada é feita de verdade, do seu navegador para a API de testes (https://api-sandbox.otlshoes.com.br/v1). Use um token otl_sbx_…: ele fica só nesta aba, e some ao fechá-la. Token de produção não é aceito aqui.

Campo
valor obrigatório Positivo é crédito, negativo é débito ("-50.00"). Zero é recusado.
descricao obrigatório Até 200 caracteres. É o que aparece no extrato.
pedidoId opcional Um pedido desta conta que originou o lançamento.

Responde 201 com o lançamento, no formato do extrato:

{
  "id": "cmg9a2b3c0001",
  "tipo": "CREDITO",
  "valor": "150.00",
  "descricao": "Devolução do pedido #12",
  "pedido": {
    "id": "cmg8k1p2a0007",
    "numero": 12
  },
  "criadoEm": "2026-09-25T09:00:00-03:00"
}

Estoque de uma numeração

PUT/v1/sandbox/produtos/{sku}/estoque
qualquer token
curl -X PUT "https://api-sandbox.otlshoes.com.br/v1/sandbox/produtos/320/estoque" \
  -H "Authorization: Bearer otl_sbx_SEU_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "numeracao": "38",
  "estoque": 0
}'
const resposta = await fetch('https://api-sandbox.otlshoes.com.br/v1/sandbox/produtos/320/estoque', {
  method: 'PUT',
  headers: {
    Authorization: 'Bearer otl_sbx_SEU_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "numeracao": "38",
    "estoque": 0
  }),
});

if (!resposta.ok) {
  const { erro } = await resposta.json();
  throw new Error(`${erro.codigo}: ${erro.mensagem} (${erro.requestId})`);
}
const dados = await resposta.json();
<?php
$ch = curl_init('https://api-sandbox.otlshoes.com.br/v1/sandbox/produtos/320/estoque');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer otl_sbx_SEU_TOKEN',
    'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'numeracao' => '38',
    'estoque' => 0,
]));

$corpo = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($status >= 400) {
    throw new Exception($corpo['erro']['codigo'] . ': ' . $corpo['erro']['mensagem']);
}
import requests

resposta = requests.put(
    "https://api-sandbox.otlshoes.com.br/v1/sandbox/produtos/320/estoque",
    headers={
        "Authorization": "Bearer otl_sbx_SEU_TOKEN",
    },
    json={
        "numeracao": "38",
        "estoque": 0
    },
    timeout=30,
)

if not resposta.ok:
    erro = resposta.json()["erro"]
    raise RuntimeError(f'{erro["codigo"]}: {erro["mensagem"]} ({erro["requestId"]})')
dados = resposta.json()
Testar agora no ambiente de testes

A chamada é feita de verdade, do seu navegador para a API de testes (https://api-sandbox.otlshoes.com.br/v1). Use um token otl_sbx_…: ele fica só nesta aba, e some ao fechá-la. Token de produção não é aceito aqui.

Responde 200 com o estoque do produto, igual a Estoque do produto:

{
  "sku": "320",
  "ativo": true,
  "numeracoes": [
    {
      "numeracao": "38",
      "skuVariacao": "320U",
      "estoque": 4
    },
    {
      "numeracao": "39",
      "skuVariacao": "320V",
      "estoque": 0
    },
    {
      "numeracao": "40",
      "skuVariacao": "320W",
      "estoque": 7
    }
  ],
  "atualizadoEm": "2026-09-29T10:12:00-03:00"
}

Serve para testar o que o seu sistema faz quando um par acaba: o ITENS_INDISPONIVEIS ao criar um pedido e o webhook produto.estoque_atualizado.

O catálogo de teste é um espelho, e é de todos

O estoque que você definir vale até a próxima vez que a produção atualizar este produto — aí o valor real volta. E o catálogo é o mesmo para todas as contas de teste: outro programador testando vê a sua mudança. A numeracao precisa ser uma das que o produto já tem.

O webhook de estoque sai com cerca de 1 minuto de espera, como em produção: mudanças seguidas no mesmo produto dentro desse minuto viram um aviso só, com o estado final.

Resetar a conta

POST/v1/sandbox/conta/resetar
qualquer token
curl -X POST "https://api-sandbox.otlshoes.com.br/v1/sandbox/conta/resetar" \
  -H "Authorization: Bearer otl_sbx_SEU_TOKEN"
const resposta = await fetch('https://api-sandbox.otlshoes.com.br/v1/sandbox/conta/resetar', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer otl_sbx_SEU_TOKEN',
  },
});

if (!resposta.ok) {
  const { erro } = await resposta.json();
  throw new Error(`${erro.codigo}: ${erro.mensagem} (${erro.requestId})`);
}
const dados = await resposta.json();
<?php
$ch = curl_init('https://api-sandbox.otlshoes.com.br/v1/sandbox/conta/resetar');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer otl_sbx_SEU_TOKEN',
]);

$corpo = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($status >= 400) {
    throw new Exception($corpo['erro']['codigo'] . ': ' . $corpo['erro']['mensagem']);
}
import requests

resposta = requests.post(
    "https://api-sandbox.otlshoes.com.br/v1/sandbox/conta/resetar",
    headers={
        "Authorization": "Bearer otl_sbx_SEU_TOKEN",
    },
    timeout=30,
)

if not resposta.ok:
    erro = resposta.json()["erro"]
    raise RuntimeError(f'{erro["codigo"]}: {erro["mensagem"]} ({erro["requestId"]})')
dados = resposta.json()
Testar agora no ambiente de testes

A chamada é feita de verdade, do seu navegador para a API de testes (https://api-sandbox.otlshoes.com.br/v1). Use um token otl_sbx_…: ele fica só nesta aba, e some ao fechá-la. Token de produção não é aceito aqui.

Sem corpo. Responde 204. Apaga os pedidos, clientes, lançamentos e o carrinho da conta de teste e devolve a ficha ao estado inicial. As integrações, os tokens e os webhooks ficam — você recomeça os testes sem pedir nada a ninguém.

Não dispara webhook: os clientes somem sem cliente.removido.

Um fluxo completo

  1. Crie um pedido → chega pedido.criado.
  2. Envie o comprovante.
  3. Simule a validação do comprovante → chega pedido.comprovante_validado.
  4. Simule o status PAGO, depois EM_SEPARACAO e ENVIADO → um pedido.status_alterado a cada passo.
  5. Simule um lançamento de crédito → chega financeiro.lancamento_criado, e o saldo muda.

Erros possíveis

HTTP codigo Quando
400 VALIDACAO Status inexistente, valor zero, numeração que o produto não tem
404 NAO_ENCONTRADO Pedido, anexo, rastreio ou produto não existe — ou o pedido é de outra conta
404 ROTA_NAO_ENCONTRADA A chamada foi feita na produção, onde os simuladores não existem