A API v1 de produtos consulta o catálogo do produtor autenticado e permite cadastrar e atualizar produtos com nome, tipo, peso unitário e dimensões.
Nem todo verbo existe. GET, POST e PATCH estão disponíveis. PUT e DELETE em
/api/v1/produtos ou /api/v1/produtos/{id} retornam sempre 405 Method Not Allowed, antes
da autenticação, e não há rota para excluir produto nem para ajustar estoque pela API:
{
"errors": "The PUT method is not supported for route api/v1/produtos/489. Supported methods: GET, HEAD, PATCH.",
"code": 405
}
Endpoints disponíveis
| Método | Endpoint | Permissão do token | Resultado |
|---|---|---|---|
GET |
/api/v1/produtos |
listar_produtos_api |
Lista paginada, até 500 produtos por página |
GET |
/api/v1/produtos/{id} |
listar_produtos_api |
Um produto |
POST |
/api/v1/produtos |
criar_produtos_api |
Cadastra um produto pendente de aprovação |
PATCH |
/api/v1/produtos/{id} |
criar_produtos_api |
Atualiza os campos enviados de um produto |
listar_produtos_api não autoriza escrita. As duas rotas de leitura exigem
listar_produtos_api; POST e PATCH exigem criar_produtos_api, uma permissão separada. Um
token que só possui a permissão de leitura recebe 403 ao tentar cadastrar ou atualizar.
Escopo por produtor efetivo. Todas as rotas filtram por produtos.user_id = <id do produtor>. Igual a Clientes: quando o token pertence a uma subconta (funcionário vinculado a um produtor), a API sobe para o produtor pai e retorna os produtos dele, não apenas os cadastrados sob a subconta.
POST e PATCH exigem uma conta de produtor ativa. Além da permissão criar_produtos_api, o token precisa pertencer a uma conta com status ativo e papel de produtor. Um token de subconta de funcionário — que consegue ler o catálogo do produtor pai — recebe 403 com { "message": "A conta autenticada não está autorizada a cadastrar produtos." } ao tentar escrever.
Listar produtos
GET
curl --request GET \
--url 'https://ev.paylog.cash/api/v1/produtos?status=1&page=1' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer SEU_TOKEN'
GET /api/v1/produtos?status=1&page=1 HTTP/1.1
Host: ev.paylog.cash
Accept: application/json
Authorization: Bearer SEU_TOKEN
Parâmetros de consulta
| Campo | Tipo | Obrigatório | Comportamento atual |
|---|---|---|---|
page |
inteiro | Não | Página solicitada. Quando omitido, usa 1. |
status |
inteiro | Não | Filtra pelo código exato: 0 pendente, 1 aprovado ou 2 desabilitado. Quando omitido, usa 1. |
data_inicio |
data | Não | Inclui produtos com created_at a partir da data. Recomenda-se YYYY-MM-DD. |
data_fim |
data | Não | Inclui produtos com created_at até o fim da data. Recomenda-se YYYY-MM-DD. |
Os códigos de status estão em Status de pacotes e produtos. data_inicio e data_fim podem ser usados separadamente ou em conjunto; o primeiro considera o início do dia e o segundo, o fim do dia.
status é convertido para inteiro antes de filtrar. Qualquer valor não numérico — inclusive ?status= vazio ou ?status=aprovado — vira 0 e a resposta traz apenas produtos pendentes, sem erro de validação. Envie só 0, 1 ou 2, ou omita o parâmetro para receber o padrão 1 (aprovado).
Como um produto recém-cadastrado nasce pendente (status = 0), ele não aparece na listagem padrão. Use ?status=0 para acompanhar a fila de aprovação.
Resposta 200
{
"current_page": 1,
"data": [
{
"id": 489,
"nome": "Produto Exemplo API",
"status": 1,
"estoque": 120
}
],
"first_page_url": "https://ev.paylog.cash/api/v1/produtos?page=1",
"from": 1,
"last_page": 1,
"last_page_url": "https://ev.paylog.cash/api/v1/produtos?page=1",
"links": [
{
"url": null,
"label": "« Anterior",
"page": null,
"active": false
},
{
"url": "https://ev.paylog.cash/api/v1/produtos?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": null,
"label": "Próxima »",
"page": null,
"active": false
}
],
"next_page_url": null,
"path": "https://ev.paylog.cash/api/v1/produtos",
"per_page": 500,
"prev_page_url": null,
"to": 1,
"total": 1
}
Campos de data[]
| Campo | Tipo | Sempre retornado | Descrição |
|---|---|---|---|
id |
inteiro | Sim | ID numérico do produto. |
nome |
string | Sim | Nome do produto. |
status |
inteiro | Sim | Código de status do produto. |
estoque |
inteiro | Sim | Saldo de estoque atual. Pode ser 0. |
O nome correto do campo de saldo é estoque, tanto na listagem quanto na consulta individual. A API não retorna quantidade para produtos — quantidade só existe dentro da composição de um pacote, onde representa quantas unidades daquele produto o kit contém. Veja Pacotes.
Também não existe campo de preço, peso, dimensões ou SKU nesta resposta: o payload de produto tem exatamente quatro chaves (id, nome, status, estoque). Peso, dimensões e codigo_plataforma aparecem apenas nas respostas de POST e PATCH.
Metadados da paginação
| Campo | Tipo | Sempre retornado | Descrição |
|---|---|---|---|
current_page |
inteiro | Sim | Página atual. |
data |
array | Sim | Produtos da página; pode ser []. |
first_page_url |
string | Sim | URL da primeira página. |
from |
inteiro ou null |
Sim | Posição do primeiro item; null quando vazio. |
last_page |
inteiro | Sim | Última página disponível. |
last_page_url |
string | Sim | URL da última página. |
links |
array | Sim | Links navegáveis da paginação. |
links[].url |
string ou null |
Sim | URL do link; null quando indisponível. |
links[].label |
string | Sim | Rótulo HTML do link. |
links[].page |
inteiro ou null |
Sim | Página do link. |
links[].active |
booleano | Sim | Indica a página atual. |
next_page_url |
string ou null |
Sim | Próxima página, quando existir. |
path |
string | Sim | URL base da coleção. |
per_page |
inteiro | Sim | Limite fixo de 500 itens por página. |
prev_page_url |
string ou null |
Sim | Página anterior, quando existir. |
to |
inteiro ou null |
Sim | Posição do último item; null quando vazio. |
total |
inteiro | Sim | Total de produtos encontrados. |
Consultar um produto
GET
curl --request GET \
--url 'https://ev.paylog.cash/api/v1/produtos/489' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer SEU_TOKEN'
GET /api/v1/produtos/489 HTTP/1.1
Host: ev.paylog.cash
Accept: application/json
Authorization: Bearer SEU_TOKEN
Parâmetro de rota
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
inteiro | Sim | ID de um produto pertencente ao produtor autenticado. |
Resposta 200
{
"id": 489,
"nome": "Produto Exemplo API",
"status": 1,
"estoque": 120
}
| Campo | Tipo | Sempre retornado | Descrição |
|---|---|---|---|
id |
inteiro | Sim | ID numérico. |
nome |
string | Sim | Nome do produto. |
status |
inteiro | Sim | Código de status. |
estoque |
inteiro | Sim | Saldo atual. |
Produto ausente ou de outra conta
A implementação atual responde com 204 No Content nos dois casos. Não há corpo JSON e a resposta não revela se o ID existe em outra conta.
Cadastrar um produto
POST
POST /api/v1/produtos
Cria um produto vinculado ao produtor autenticado. O produto nasce pendente (status = 0) e
entra na fila de aprovação da Paylog, exatamente como um cadastro feito pelo painel.
A configuração fiscal e a aprovação são feitas pela Paylog, por decisão de projeto. O corpo da
requisição não aceita tipo_emissao, notazz_product_code, emite_nota_fiscal nem nicho_id: a
Paylog configura o emissor e o enquadramento fiscal no momento da aprovação. É justamente isso que
destrava a emissão de NF-e para os pedidos do produtor sem que a sua integração precise conhecer
regras fiscais.
peso_unitario é em QUILOGRAMAS, por unidade — e o teto é 30 kg. Um valor acima de 30 é
recusado com 422. Enviar o peso em gramas é o erro mais comum: um item de 250 g é
0.25, nunca 250.
A API recusa e nunca converte automaticamente. Não há heurística de divisão por 1000, porque
ela sub-declararia por 1000× encomendas genuinamente pesadas. Um peso implausível cadastrado no
produto contamina toda a operação: acima do teto os Correios simplesmente não devolvem a cotação
de frete e o pedido fica travado em Correção com a mensagem enganosa
Sem resposta da API — sem indicar o peso como causa.
curl --request POST \
--url 'https://ev.paylog.cash/api/v1/produtos' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer SEU_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"nome": "Erec Prime Frasco",
"codigo_plataforma": "PRD8H2K9L",
"tipo_produto": 1,
"peso_unitario": 0.12,
"comprimento": 12.5,
"largura": 6,
"altura": 6
}'
POST /api/v1/produtos HTTP/1.1
Host: ev.paylog.cash
Accept: application/json
Authorization: Bearer SEU_TOKEN
Content-Type: application/json
{
"nome": "Erec Prime Frasco",
"codigo_plataforma": "PRD8H2K9L",
"tipo_produto": 1,
"peso_unitario": 0.12,
"comprimento": 12.5,
"largura": 6,
"altura": 6
}
{
"nome": "Erec Prime Frasco", // Obrigatório: nome do produto, até 255 caracteres.
"codigo_plataforma": "PRD8H2K9L", // Obrigatório: o SKU; único dentro da sua conta.
"tipo_produto": 1, // Obrigatório: código da tabela de tipos.
"peso_unitario": 0.12, // Obrigatório: quilogramas POR UNIDADE, de 0.001 a 30.
"comprimento": 12.5, // Obrigatório: centímetros, de 0.1 a 200.
"largura": 6, // Obrigatório: centímetros, de 0.1 a 200.
"altura": 6, // Obrigatório: centímetros, de 0.1 a 200.
"gtin": null, // Opcional: GTIN, até 20 caracteres.
"ean": null, // Opcional: EAN, até 20 caracteres.
"product_barcode": null, // Opcional: código de barras interno, até 255 caracteres.
"site_oficial": null // Opcional: URL válida do produto, até 255 caracteres.
}
Campos da requisição
| Campo | Tipo | Presença | Regra |
|---|---|---|---|
nome |
string |
Obrigatório | Nome do produto; até 255 caracteres. |
codigo_plataforma |
string |
Obrigatório | O SKU do produto; até 255 caracteres. Único por produtor — veja Reenvio do mesmo SKU. |
tipo_produto |
integer |
Obrigatório | Um dos códigos da tabela de tipos. |
peso_unitario |
number |
Obrigatório | Peso de uma unidade em quilogramas, de 0.001 a 30. |
comprimento |
number |
Obrigatório | Comprimento da embalagem em centímetros, de 0.1 a 200. |
largura |
number |
Obrigatório | Largura da embalagem em centímetros, de 0.1 a 200. |
altura |
number |
Obrigatório | Altura da embalagem em centímetros, de 0.1 a 200. |
gtin |
string |
Opcional | GTIN; até 20 caracteres. Aceita null. |
ean |
string |
Opcional | EAN; até 20 caracteres. Aceita null. |
product_barcode |
string |
Opcional | Código de barras usado na sua operação; até 255 caracteres. Aceita null. |
site_oficial |
string |
Opcional | URL válida da página do produto; até 255 caracteres. Aceita null. |
O corpo não aceita nenhuma outra chave — nada é ignorado em silêncio. Estas chaves são
recusadas com 422, mesmo que o valor enviado esteja correto:
| Chave recusada | Por quê |
|---|---|
status |
A aprovação é da Paylog. Todo produto nasce 0 (Pendente). |
user_id |
O produtor vem sempre do token, nunca do corpo. |
tipo_emissao |
Configuração fiscal definida pela Paylog na aprovação. |
notazz_product_code |
Configuração fiscal definida pela Paylog. |
emite_nota_fiscal |
Configuração fiscal definida pela Paylog. |
nicho_id |
Classificação interna definida pela Paylog. |
estoque |
Não há ajuste de estoque pela API; o produto é criado com 0. |
A recusa é intencional: preferimos que a sua integração descubra na primeira chamada que o campo não é dela a deixá-la acreditar que gravou um valor fiscal que nunca chegou ao banco.
Tipos de produto
| Código | Tipo |
|---|---|
1 |
Encapsulado |
2 |
Cosmético |
3 |
Roupa |
5 |
Livro |
6 |
Acessórios |
7 |
Chá |
8 |
Líquido |
9 |
Outros |
10 |
Goma |
11 |
Brindes |
12 |
Suplemento em Pó |
A sequência não é contínua: o código 4 não existe. Envie exatamente um dos códigos da
tabela; qualquer outro valor retorna 422 em tipo_produto.
Valores definidos pelo servidor
| Campo | Valor na criação |
|---|---|
user_id |
Produtor efetivo do token. |
status |
0 (Pendente), até a aprovação da Paylog. |
estoque |
0. |
tipo_emissao |
Permanece nulo até a Paylog aprovar o produto e configurar a emissão fiscal. |
Resposta 201
{
"code": 201,
"message": "Produto criado com sucesso",
"produto": {
"id": 489,
"nome": "Erec Prime Frasco",
"codigo_plataforma": "PRD8H2K9L",
"tipo_produto": 1,
"peso_unitario": 0.12,
"comprimento": 12.5,
"largura": 6,
"altura": 6,
"gtin": null,
"ean": null,
"product_barcode": null,
"site_oficial": null,
"status": 0,
"status_label": "Pendente",
"created_at": "2026-08-07T14:03:11+00:00"
}
}
Reenvio do mesmo SKU
codigo_plataforma é único por produtor, e a criação é idempotente: reenviar o mesmo SKU não
duplica nem sobrescreve nada.
| Situação | Status | Corpo |
|---|---|---|
| SKU ainda não cadastrado | 201 |
message: "Produto criado com sucesso" |
| SKU já cadastrado nesta conta | 200 |
message: "Produto já cadastrado para este código de plataforma" |
{
"code": 200,
"message": "Produto já cadastrado para este código de plataforma",
"produto": {
"id": 489,
"nome": "Erec Prime Frasco",
"codigo_plataforma": "PRD8H2K9L",
"tipo_produto": 1,
"peso_unitario": 0.12,
"comprimento": 12.5,
"largura": 6,
"altura": 6,
"gtin": null,
"ean": null,
"product_barcode": null,
"site_oficial": null,
"status": 0,
"status_label": "Pendente",
"created_at": "2026-08-07T14:03:11+00:00"
}
}
O 200 devolve o produto inalterado. Se o corpo reenviado trouxer um peso, uma dimensão ou um
nome diferente do que está gravado, esses valores são descartados — nenhuma alteração
silenciosa é aplicada. Para editar um produto existente, use
PATCH /api/v1/produtos/{id}. Trate 201 e 200 como sucesso e leia o
produto.id retornado nos dois casos.
Atualizar um produto
PATCH
PATCH /api/v1/produtos/{id}
Atualiza somente os campos enviados de um produto do produtor autenticado.
curl --request PATCH \
--url 'https://ev.paylog.cash/api/v1/produtos/489' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer SEU_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"peso_unitario": 0.25,
"altura": 7
}'
PATCH /api/v1/produtos/489 HTTP/1.1
Host: ev.paylog.cash
Accept: application/json
Authorization: Bearer SEU_TOKEN
Content-Type: application/json
{
"peso_unitario": 0.25,
"altura": 7
}
{
"peso_unitario": 0.25, // Opcional: quilogramas por unidade; corrige um peso enviado errado.
"altura": 7 // Opcional: centímetros. Os campos não enviados permanecem como estão.
}
Parâmetro de rota
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
inteiro | Sim | ID de um produto pertencente ao produtor autenticado. |
Campos da requisição
Os campos são os mesmos de Cadastrar um produto, com as mesmas regras de
validação, e todos são opcionais: nome, tipo_produto, peso_unitario, comprimento,
largura, altura, gtin, ean, product_barcode e site_oficial.
| Regra | Comportamento |
|---|---|
| Ao menos um campo enviado | Um corpo vazio ({}) retorna 422 na chave produto. |
| Campo omitido | Permanece com o valor atual; não é apagado. |
| Campo enviado | Precisa satisfazer a mesma regra do cadastro — inclusive o teto de 30 kg. |
codigo_plataforma é imutável aqui. Ele identifica o produto nas integrações e no
mapeamento de SKU, então PATCH recusa a chave com 422 em vez de reescrevê-la. Também são
recusados status, user_id e as chaves fiscais (tipo_emissao, notazz_product_code,
emite_nota_fiscal, nicho_id) e estoque — os mesmos motivos descritos no cadastro.
Corrigir peso e dimensões de um produto já aprovado é permitido. A edição não depende do
status: um produtor que cadastrou 250 g como 250 e só percebeu depois da aprovação corrige com
um PATCH, e os pesos derivados passam a usar o valor corrigido nos próximos cálculos de frete. O
status continua sendo alterado apenas pela Paylog.
Resposta 200
{
"code": 200,
"message": "Produto atualizado com sucesso",
"produto": {
"id": 489,
"nome": "Erec Prime Frasco",
"codigo_plataforma": "PRD8H2K9L",
"tipo_produto": 1,
"peso_unitario": 0.25,
"comprimento": 12.5,
"largura": 6,
"altura": 7,
"gtin": null,
"ean": null,
"product_barcode": null,
"site_oficial": null,
"status": 0,
"status_label": "Pendente",
"created_at": "2026-08-07T14:03:11+00:00"
}
}
Produto de outro produtor
Um id inexistente e um id pertencente a outro produtor recebem exatamente a mesma resposta
404, sem revelar que o produto existe em outra conta:
{
"message": "Produto não encontrado."
}
Atenção à assimetria entre os verbos do mesmo recurso: GET /api/v1/produtos/{id} responde
204 No Content para produto inexistente ou de outra conta, enquanto PATCH /api/v1/produtos/{id}
responde 404 com corpo JSON. Trate os dois como "não encontrado".
O objeto produto de POST e PATCH
As duas rotas de escrita devolvem o mesmo objeto.
| Campo | Tipo | Sempre retornado | Descrição |
|---|---|---|---|
id |
inteiro | Sim | ID interno do produto. Guarde-o para as chamadas seguintes. |
nome |
string | Sim | Nome do produto. |
codigo_plataforma |
string | Sim | O SKU gravado. |
tipo_produto |
inteiro | Sim | Código do tipo do produto. |
peso_unitario |
número | Sim | Peso de uma unidade, em quilogramas. |
comprimento |
número | Sim | Comprimento em centímetros. |
largura |
número | Sim | Largura em centímetros. |
altura |
número | Sim | Altura em centímetros. |
gtin |
string ou null |
Sim | GTIN, quando informado. |
ean |
string ou null |
Sim | EAN, quando informado. |
product_barcode |
string ou null |
Sim | Código de barras, quando informado. |
site_oficial |
string ou null |
Sim | URL da página do produto, quando informada. |
status |
inteiro | Sim | 0 pendente, 1 aprovado ou 2 desabilitado. |
status_label |
string | Sim | Rótulo do status: Pendente, Aprovado ou Desabilitado. |
created_at |
string | Sim | Data e hora da criação, em ISO 8601. |
O objeto não traz estoque, campos fiscais nem preço. O saldo de estoque continua disponível
em GET /api/v1/produtos.
Erros
| Status | Corpo atual | Quando ocorre |
|---|---|---|
204 |
Sem conteúdo | Produto inexistente ou de outro produtor, em GET /produtos/{id}. |
401 |
{ "message": "Unauthenticated." } |
Token ausente ou inválido. |
403 |
{ "errors": "User does not have the right permissions.", "code": 403 } |
Token sem a permissão da rota: listar_produtos_api nos GET, criar_produtos_api no POST e no PATCH. |
403 |
{ "message": "A conta autenticada não está autorizada a cadastrar produtos." } |
Token válido e com a permissão, mas a conta não é um produtor ativo. Só em POST e PATCH. |
404 |
{ "message": "Produto não encontrado." } |
PATCH /produtos/{id} de um produto inexistente ou de outro produtor. |
405 |
{ "errors": "The PUT method is not supported for route api/v1/produtos/489. Supported methods: GET, HEAD, PATCH.", "code": 405 } |
PUT ou DELETE. Ocorre antes da autenticação. |
422 |
{ "message": "Os dados fornecidos são inválidos.", "errors": { "campo": ["mensagem"] } } |
Campo obrigatório ausente, valor fora das faixas, tipo inexistente, chave recusada ou corpo vazio no PATCH. |
500 |
{ "message": "Não foi possível cadastrar o produto." } · { "message": "Não foi possível atualizar o produto." } |
Falha inesperada em POST e PATCH, respectivamente. |
500 |
Texto Algo deu errado! (não é JSON) |
Falha capturada pelos GET legados — tipicamente data_inicio ou data_fim que o interpretador de datas não consegue converter. |
Exemplo de 422 ao enviar o peso em gramas e uma chave fiscal:
{
"message": "Os dados fornecidos são inválidos.",
"errors": {
"peso_unitario": [
"O peso unitário deve ser informado em quilogramas e não pode exceder 30 kg."
],
"tipo_emissao": [
"A configuração fiscal é definida pelo Paylog e não pode ser enviada."
]
}
}
Exemplo de 422 em PATCH sem nenhum campo atualizável — o erro vem na chave produto, não em um
campo. Enviar apenas campos imutáveis produz o erro do próprio campo recusado (abaixo), não este:
{
"message": "Os dados fornecidos são inválidos.",
"errors": {
"produto": [
"Informe ao menos um campo para atualizar."
]
}
}
Ao tentar alterar o SKU:
{
"message": "Os dados fornecidos são inválidos.",
"errors": {
"codigo_plataforma": [
"O código de plataforma não pode ser alterado após o cadastro."
]
}
}
204 não é um erro para a maioria dos clientes HTTP. GET /api/v1/produtos/{id} de um produto inexistente ou de outra conta responde 204 No Content, e não 404. Bibliotecas que só tratam 4xx como falha vão considerar a chamada bem-sucedida e receber um corpo vazio. Verifique explicitamente status === 204 antes de desserializar.
Os envelopes de erro não são uniformes neste recurso. O 500 dos GET devolve texto puro
(Algo deu errado!); os 403 e 405 do tratador global usam as chaves errors (string) e
code; POST e PATCH usam message (e errors como objeto por campo no 422). Trate cada
status pelo código HTTP, nunca pelo formato do corpo.