Autenticação

A API do marketplace UniSupri usa autenticação bearer token em todos os endpoints de seller. O token é obtido a partir de uma credencial (username + senha) criada no Portal do Vendedor. Leia Primeiros Passos se ainda não criou a sua.


Modelo

sequenceDiagram autonumber participant I as Integrador participant A as API I->>A: POST /integration/auth<br/>{ username, password } A-->>I: { token, expires_at, store, credential } I->>A: GET /api/* + Bearer sk_live_* A-->>I: 200 OK

Dois conceitos distintos:

ConceitoOnde moraQuem usaQuando renova
Credencial (username + senha)Portal do VendedorOperador humanoManualmente, quando suspeita de vazamento ou rotação programada
Token de acesso (sk_live_*)Memória do ERPSistema externoAutomaticamente via /api/integration/auth, antes de expirar

A credencial fica parada no seu cofre de senhas e não vai em chamadas de API. O token é o que circula em cada requisição.


Permissões da credencial

Ao criar a credencial (no Portal do Vendedor, em Configurações → API & Integrações → Nova credencial), além do nome você escolhe quais acessos ela concede. O token herda exatamente essas permissões: ele só consegue chamar os endpoints cobertos pelos acessos marcados. Conceda o mínimo necessário para a integração.

AcessoO que liberaEscopos
ProdutosCatálogo, preços, estoque e locais de estoquestore_products_read, store_products_write, store_stock_locations_read, store_stock_locations_write
AnúnciosPublicação e gestão de anúnciosstore_item_read, store_item_create, store_item_update
PedidosLeitura, avanço de status e geração de enviostore_orders_read, store_orders_manage, store_orders_create_shipment
Pedidos de sublojasEstende os acessos de Pedidos às sublojas faturadas pela matriz — ver Matriz e sublojasstore_orders_substores
FulfillmentRemessas e RMA (devoluções)store_fulfillment_read, store_fulfillment_create_shipment, store_fulfillment_manage_rma
AtendimentoConversas com o compradorstore_conversations_read, store_conversations_manage
PerguntasQ&A dos anúnciosstore_questions_read, store_questions_answer
OcorrênciasOcorrências/disputasstore_occurrences_read, store_occurrences_manage
ClientesDados de clientesstore_customers_read, store_customers_manage
FinanceiroLeitura de relatórios financeirosstore_finance_read
ReputaçãoAvaliações da lojastore_rating_read

Uma chamada a um endpoint fora das permissões da credencial retorna 403 Forbidden: marque o acesso correspondente no portal e gere uma nova credencial (ou ajuste a existente).

Vários acessos são exclusivos do Portal e não podem ser atribuídos a uma credencial de API — entre eles gestão de webhooks, gestão de tokens de API, integrações ERP, logística e transportadoras, força de vendas, dashboard e configurações da loja. Se você tentar criar uma credencial com um desses escopos, a API recusa com 422. No caso dos webhooks, isso continua valendo para a gestão pelo Portal — mas a credencial pode configurar o próprio webhook na chamada de autenticação, sem escopo nenhum: ver Configurar o webhook no login.


Obter um token

POST/api/integration/auth

Endpoint público, não exige Authorization. A própria credencial (no body) é a forma de autenticação.

Request

{
  "username": "loja-xpto@12345678",
  "password": "Xk9p2Lm7nQ4rT8vW3yZ1"
}

Response 200

{
  "success": true,
  "data": {
    "token": "sk_live_a1b2c3d4e5f6...",
    "expires_at": "2026-06-19T12:34:56-03:00",
    "credential": {
      "name": "ERP Bling",
      "username": "loja-xpto@12345678",
      "abilities": ["*"]
    },
    "store": {
      "store_id": "01KYN1VYYBFRDF14ZDXVWFB2MY",
      "store_name": "Loja XPTO",
      "store_code": "LOJA-XPTO",
      "store_status": "active",
      "document": "12345678000190",
      "company_name": "XPTO Comércio Ltda"
    }
  }
}

Identificar a conta do token

A própria emissão já diz de quem é o token — útil pra quem integra várias lojas e precisa casar o token com o cadastro do lado dele, sem uma segunda chamada.

CampoO que é
store.store_idIdentificador público da loja (ULID). É este id que aparece nos demais recursos da API — use ele como chave do seu lado.
store.store_nameNome da loja, como cadastrado no portal.
store.store_codeCódigo interno da loja, quando definido.
store.store_statusSituação da loja em minúsculas (ex.: active). Loja fora de active autentica, mas pode ter operações bloqueadas.
store.documentCNPJ da empresa dona da loja, só dígitos.
store.company_nameRazão social / nome da empresa.
credential.nameNome que o vendedor deu à credencial no portal (ex.: "ERP Bling") — identifica qual integração é, quando a loja tem várias.
credential.usernameO mesmo username usado na chamada, ecoado por conveniência.
credential.abilitiesEscopos da credencial. ["*"] = acesso total.

Os campos token e expires_at continuam onde sempre estiveram; os blocos credential e store são adição, não substituição.

Se você precisa desses dados fora do momento da emissão (ex.: validar um token já em cache), use o GET /api/stores/auth/session, que devolve o cadastro completo da loja.

Rotação

Cada chamada bem-sucedida ao /api/integration/auth:

  • Emite um token novo.
  • Substitui o token anterior. O antigo deixa de funcionar imediatamente.
  • Repopula expires_at para agora + 30 dias.

Implicação prática: só existe 1 token ativo por credencial. Se você roda dois sistemas que precisam de tokens independentes, crie duas credenciais separadas no portal.

Quando renovar

Renove antes de expirar. Recomendamos disparar uma nova chamada quando faltarem 24-48h pra expiração. Você não vai encontrar um endpoint de refresh-token separado: a renovação é a própria chamada ao /api/integration/auth (a mesma que você já usa pra obter o token). Um endpoint só, que serve tanto pra primeira emissão quanto pra renovar. Se o token expirar e você fizer uma chamada com ele, a API responde 401 e o ERP deve renovar e tentar de novo.


Configurar o webhook no login

Cadastrar webhook pelo Portal é trabalho técnico que cai no colo da pessoa errada: a URL é sua, os eventos são seus, o secret vai pro seu cofre. Por isso a autenticação aceita um bloco webhook opcional — o vendedor entrega só username e password, e sua integração se configura sozinha.

Request

{
  "username": "loja-xpto@12345678",
  "password": "Xk9p2Lm7nQ4rT8vW3yZ1",
  "webhook": {
    "url": "https://erp.exemplo.com/unisupri/webhook",
    "events": ["*"],
    "active": true,
    "headers": { "X-Erp-Key": "..." }
  }
}
CampoRegra
webhookOpcional. Ausente (ou null) não cria, não altera e não apaga nada.
webhook.urlObrigatória quando o bloco existe. Precisa ser HTTPS, no máximo 2048 caracteres.
webhook.eventsOpcional. Ausente ou ["*"] assina tudo que a credencial pode. Aceita evento individual e nome de grupo.
webhook.activeOpcional, true por padrão. falsepausa as entregas sem apagar a configuração.
webhook.headersOpcional. Cabeçalhos extras enviados em toda entrega (ex.: um Authorization do seu endpoint).

Response 200

{
  "success": true,
  "data": {
    "token": "sk_live_a1b2c3d4e5f6...",
    "expires_at": "2026-09-05T12:34:56-03:00",
    "credential": { "...": "..." },
    "store": { "...": "..." },
    "webhook": {
      "id": "9f1c8a2e-4b7d-4c31-9f0a-2e6d8b1c5a44",
      "url": "https://erp.exemplo.com/unisupri/webhook",
      "events": ["order.created", "order.paid"],
      "active": true,
      "secret": "whsec_AT1JfFiUI1xfZZb40rXw1K08XaWnn0lwArgIbHQN",
      "status": "created"
    }
  }
}
CampoO que é
idIdentificador do webhook. É o mesmo valor que chega em webhook_id no envelope de cada entrega.
eventsA lista resolvida: "*" e grupos já expandidos, e já filtrados pelas permissões da credencial. É exatamente o que você vai receber.
secretO signing secret, usado pra validar a assinatura. Vem em toda resposta que traga o bloco, não só na criação.
statuscreated, updated ou unchanged — o que a chamada fez.

Exemplo completo

Cenário real: um ERP que só se importa com pedidos e logística, cujo endpoint de recepção fica atrás de um gateway que exige um cabeçalho próprio, e que quer receber tudo num caminho versionado.

curl -X POST https://api.unisupri.com/api/integration/auth \
  -H "Content-Type: application/json" \
  -d '{
    "username": "loja-xpto@12345678",
    "password": "Xk9p2Lm7nQ4rT8vW3yZ1",
    "webhook": {
      "url": "https://erp.exemplo.com/v2/webhooks/unisupri",
      "events": ["orders", "logistics", "occurrence.created"],
      "active": true,
      "headers": {
        "X-Erp-Tenant": "loja-xpto",
        "Authorization": "Bearer o-token-do-SEU-gateway"
      }
    }
  }'

Três coisas acontecendo aí:

  • events mistura grupo e evento avulso.orders e logistics expandem para os 11 eventos dessas áreas; occurrence.created entra sozinho, sem trazer occurrence.resolved junto.
  • headers viaja em toda entrega. São os cabeçalhos que a UniSupri vai mandar para você — servem pra atravessar o seu gateway, seu WAF ou seu roteador de multi-tenant. Se você repetir um nome que a plataforma já usa (Content-Type, User-Agent), o seu valor substitui o padrão. O X-Webhook-Signature continua sendo calculado normalmente.
  • active: true é o default, mas mandar explícito deixa o bloco autocontido — o mesmo JSON serve pra reativar um webhook que você pausou antes.

Resposta:

{
  "success": true,
  "data": {
    "token": "sk_live_a1b2c3d4e5f6...",
    "expires_at": "2026-09-05T12:34:56-03:00",
    "credential": {
      "name": "ERP Bling",
      "username": "loja-xpto@12345678",
      "abilities": ["store_orders_read", "store_fulfillment_read", "store_occurrences_read"]
    },
    "store": { "store_id": "01KYN1VYYBFRDF14ZDXVWFB2MY", "store_name": "Loja XPTO" },
    "webhook": {
      "id": "9f1c8a2e-4b7d-4c31-9f0a-2e6d8b1c5a44",
      "url": "https://erp.exemplo.com/v2/webhooks/unisupri",
      "events": [
        "occurrence.created",
        "order.cancelled",
        "order.created",
        "order.invoiced",
        "order.paid",
        "order.refunded",
        "order.status_changed",
        "shipment.cancelled",
        "shipment.created",
        "shipment.dispatched",
        "shipment.out_for_delivery",
        "shipment.status_changed"
      ],
      "active": true,
      "secret": "whsec_AT1JfFiUI1xfZZb40rXw1K08XaWnn0lwArgIbHQN",
      "status": "created"
    }
  }
}

Repare que events volta expandido e ordenado, com os 12 eventos resolvidos. É essa lista que vale — não a que você mandou.

Os headersnão voltam na resposta. Eles ficam guardados na configuração e são aplicados nas entregas; se precisar trocar um, mande o objeto headers inteiro de novo (ele é substituído, não mesclado). Mandar o bloco webhooksem a chave headers limpa os cabeçalhos customizados.

Manutenção pelo mesmo endpoint

Tudo o que você precisa fazer depois passa pelo mesmo bloco:

O que você querO que mandarstatus de volta
Confirmar que está tudo certoo mesmo bloco de sempreunchanged
Trocar de endpointo bloco com a url novaupdated
Assinar mais um eventoo bloco com a lista nova, completaupdated
Pausar durante uma manutenção"active": falseupdated
Voltar do pause"active": trueupdated
Trocar o cabeçalho do gatewayo objeto headers inteiroupdated

Em nenhum desses casos o id ou o secret mudam. Você não precisa (nem deve) apagar e recriar pra alterar qualquer campo.

Um webhook por credencial

A configuração é idempotente pela credencial, não pela URL. Mandar o mesmo bloco em todo login é o uso recomendado: a primeira chamada cria (created), as seguintes não fazem nada (unchanged).

Trocar a URL atualiza a configuração existente: mesmo id, mesmo secret, histórico de entregas preservado. Você não precisa (nem consegue) acumular webhooks por esse caminho — não existe o risco de, a cada deploy, nascer um webhook novo apontando pro endpoint antigo.

Para pausar, mande "active": false. Para apagar de vez, use o Portal ou revogue a credencial.

O webhook criado por aqui aparece no Portal do Vendedor marcado como gerido pela integração. O vendedor consegue ver, testar e consultar o histórico, mas não editar nem apagar — quem manda na configuração é o seu sistema. Se a loja precisar cortar as entregas na marra, o caminho é revogar a credencial.

Eventos aceitos

Você pode listar eventos individuais, nomes de grupo, ou "*" pra assinar tudo.

GrupoEventos
ordersorder.created, order.paid, order.status_changed, order.cancelled, order.invoiced, order.refunded
logisticsshipment.created, shipment.dispatched, shipment.out_for_delivery, shipment.status_changed, shipment.cancelled
occurrencesoccurrence.created, occurrence.resolved
conversationsconversation.opened, conversation.message, conversation.closed
stockproduct.stock_updated, stock.depleted, stock.restored, product.price_updated
catalogpublication.approved, publication.rejected

{"events": ["orders", "logistics"]} é equivalente a listar os 11 eventos desses dois grupos.

O que cada evento entrega está no catálogo de payloads.

Os eventos seguem as permissões da credencial

"*" não significa "tudo que existe", e sim "tudo que esta credencial já poderia ler pela API". Uma credencial que só tem acesso a Produtos recebe eventos de estoque, mas não de pedidos — do contrário o webhook seria uma porta lateral para dados que a permissão nega.

Grupo de eventosExige o acesso
ordersPedidos
logisticsPedidos ou Fulfillment
occurrencesOcorrências
conversationsAtendimento
stockProdutos
catalogAnúncios

Pedir explicitamente um evento fora das suas permissões devolve 422 dizendo qual evento e qual acesso falta. Se é pra receber, marque o acesso correspondente na credencial (Portal → Configurações → API & Integrações).

Quando a configuração é recusada

Bloco webhook inválido devolve 422 e não emite token. A resposta traz, em meta, o catálogo que aquela credencial aceita:

{
  "success": false,
  "code": 422,
  "message_code": "VALIDATION_ERROR",
  "errors": {
    "webhook.events": ["Evento desconhecido: 'order.shipped'."]
  },
  "meta": {
    "accepted_events": ["order.created", "order.paid"],
    "accepted_groups": ["orders", "logistics", "occurrences", "conversations", "stock", "catalog"]
  }
}

422 aqui é erro de configuração, não falha temporária — não repita a chamada. Corrija o bloco usando o meta.accepted_events da resposta. Enquanto o bloco estiver inválido, o login não emite token: se sua integração está no ar, remova o bloco webhook da chamada pra voltar a autenticar enquanto investiga.

Testar a assinatura

POST/api/integration/webhook/test

Autenticado pelo seu token. Enfileira uma entrega real do evento webhook.test no webhook que você configurou, com assinatura e tudo — serve pra validar a recepção ponta a ponta sem esperar um pedido de verdade acontecer.

curl -X POST https://api.unisupri.com/api/integration/webhook/test \
  -H "Authorization: Bearer sk_live_a1b2c3d4e5f6..."
{ "success": true, "data": { "queued": true, "event": "webhook.test", "webhook_id": "9f1c8a2e-..." } }

A resposta confirma o enfileiramento, não a entrega: o resultado aparece no histórico do Portal em alguns instantes. Não use essa rota pra "acordar" a integração — ela não substitui o /auth nem renova nada.

StatusQuando
404Você ainda não configurou webhook pelo /auth.
422O webhook está pausado (active: false).
429Mais de 5 chamadas por minuto.

Usar o token

Em qualquer endpoint do seller, envie o token no cabeçalho Authorization:

curl https://api.unisupri.com/api/products \
  -H "Authorization: Bearer sk_live_a1b2c3d4e5f6..."

A API valida, nesta ordem:

  1. Existência: o token corresponde a uma credencial cadastrada.
  2. Ativação: a credencial está ativa (não foi desativada nem revogada).
  3. Validade:expires_at ainda não passou.

Falha em qualquer uma → resposta 401 Unauthorized.

Depois disso vem a checagem de escopo: se o token existe mas não tem a permissão exigida pela rota, a resposta é 403, com os escopos que faltam na lista errors. Um token com abilities: ["*"] passa em qualquer rota.

Uma exceção:store_orders_substores (alcance às sublojas) não é coberto pelo wildcard — ele precisa estar declarado explicitamente na credencial. Isso é deliberado: nenhuma integração existente passa a receber pedidos de outra loja sem alguém marcar o acesso. Ver Matriz e sublojas.


Confirmar a autenticação

GET/api/stores/auth/session

Use para validar que o token está autenticando a loja correta. A resposta traz os dados da loja (id, nome, código, status, tipo, limites e features), a company associada (CNPJ, razão social, regime tributário) e auth_type: "integration_token".


Limites de taxa

O /api/integration/auth é protegido contra brute-force:

  • 5 tentativas por minuto por par (IP, username).
  • Estourou o limite: 429 Too Many Requests, com Retry-After em segundos.

O contador é por par IP+username; tentativas legítimas de outras lojas/IPs não são afetadas.

Este limite é só da emissão de token. O limite das demais rotas de integração está em Limites de uso.


Erros possíveis

StatusQuando aconteceO que fazer
401 no /integration/authUsername inexistente, senha errada ou credencial inativaVerifique username/senha. A mensagem é genérica de propósito e não revela qual dos três casos é. Se persistir, confirme no portal que a credencial está ativa.
401 em endpoints autenticadosToken ausente, malformado, revogado ou expiradoRenove o token via /integration/auth. Se o erro persistir após renovar, a credencial pode ter sido revogada; verifique no portal.
422 no /integration/authusername ou password ausente / vazioGaranta que os dois campos vão no body como strings não-vazias.
422 no /integration/auth com bloco webhookURL não-HTTPS, evento inexistente ou evento fora das permissões da credencialErro de configuração: não repita a chamada. Corrija o bloco usando meta.accepted_events da resposta. Sem bloco webhook, o login volta a funcionar normalmente.
429 no /integration/authMais de 5 tentativas/minuto pra mesma credencial+IPAguarde o Retry-After segundos antes de tentar novamente. Cache o token e não chame o auth em loop.

Trocar a senha

Quando trocar:

  • Você suspeita que a senha vazou.
  • Rotação preventiva periódica (recomendado a cada 90 dias).
  • Trocou de mãos a operação do ERP.

Como trocar:

  1. Portal do Vendedor → Configurações → API & Integrações.
  2. Localize a credencial na lista.
  3. Clique no ícone de chave (🔑 Trocar senha).
  4. Escolha Gerar aleatória (recomendado, 20 caracteres) ou Definir manualmente (entre 12 e 64 caracteres).
  5. Copie a nova senha. Ela aparece uma única vez.

Importante: trocar a senha não invalida o token corrente. O ERP que está usando o token atual continua funcionando até a próxima expiração ou até você revogar a credencial inteira. Se você precisa invalidar imediatamente (suspeita de comprometimento), revogue a credencial (lixeira) e crie uma nova.


Revogar uma credencial

Portal do Vendedor → Configurações → API & Integrações → ícone de lixeira na linha da credencial.

Revogar corta o acesso na hora, dos dois lados:

  • Username e senha deixam de autenticar no /integration/auth.
  • O token corrente para de funcionar imediatamente, mesmo dentro do prazo de 30 dias: a validação parte do registro da credencial, que deixou de existir. A partir daí, toda chamada com esse token responde 401.

O mesmo vale para uma credencial marcada como inativa: o token dela é recusado na próxima chamada.

É esse o caminho para cortar acesso de imediato em caso de vazamento. Trocar a senha não serve para isso — veja a seção anterior.


Boas práticas

  • Cache o token. Não chame /integration/auth antes de cada requisição; emita um token e use-o pelos 30 dias.
  • Renove proativamente. Agende um job que renove o token quando faltarem ~24h.
  • Trate 401 como sinal de renovação. Se uma chamada autenticada retornar 401, renove o token e tente uma vez. Se ainda falhar, alerte um humano.
  • Uma credencial por sistema. Não compartilhe credenciais entre ERPs diferentes; a rotação substitui o token, e isso quebra o sistema que ficou pra trás.
  • Não logue a senha nem o token em arquivos de log persistentes. Use mascaramento (sk_live_...XXXX).
  • Cofre de senhas. Guarde username + password em um gerenciador de segredos (1Password, Vault, AWS Secrets Manager, etc.), nunca em código-fonte commitado.
  • Mande o bloco webhook em todo login. É idempotente: cria na primeira vez, e depois só confirma (unchanged). Isso mantém a configuração convergindo sozinha, inclusive se alguém mexer nela.
  • Logue o status do webhook.created em regime normal significa que a configuração sumiu e foi recriada — vale investigar.