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.
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
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_alteradotraz ostatusAnterior.
Validar comprovante
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
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
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
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
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 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
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
- Crie um pedido → chega
pedido.criado. - Envie o comprovante.
- Simule a validação do comprovante → chega
pedido.comprovante_validado. - Simule o status
PAGO, depoisEM_SEPARACAOeENVIADO→ umpedido.status_alteradoa cada passo. - 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 |
