Autenticação — OAuth 2.0

A Partner API usa OAuth 2.0 (Authorization Code com PKCE). O lojista autoriza seu App e você recebe um token de acesso no contexto da loja.

Modelo de integração — 1 App, várias lojas

Existem duas credenciais, em níveis diferentes — entender isso é a chave para integrar a sua plataforma à Sellit:

  • Sua plataforma → client_id + client_secret. Você registra um App e recebe esse par. Ele identifica a sua plataforma e é o mesmo para todos os seus clientes. A client_secret fica só no seu backend.
  • Cada loja do seu cliente → access_token. Quando um lojista autoriza o seu App, você recebe um token por loja. É ele que vai no Authorization: Bearer de cada chamada, agindo em nome daquela loja.
Ou seja: 1 App (id + secret) → N lojas (N tokens). O lojista nunca cola um token — ele só clica em “Conectar Sellit”, autoriza, e o seu backend recebe o token daquela loja. Guarde um access_token + refresh_token por cliente/loja.

Gerando o code_verifier e code_challenge (PKCE)

Antes de redirecionar o lojista, gere um par de valores PKCE no seu backend. O code_verifier fica com você; o code_challenge é enviado na URL de autorização.

Node.js
import { randomBytes, createHash } from "node:crypto"

function generatePkce() {
  const verifier = randomBytes(32).toString("base64url") // 43-128 chars
  const challenge = createHash("sha256")
    .update(verifier)
    .digest("base64url")
  return { verifier, challenge }
}

const { verifier, challenge } = generatePkce()
// Armazene o verifier na sessão — você vai precisar dele na Etapa 2

Visão geral do fluxo

  1. 1. Redirecione o lojista para a tela de autorização

    Envie o lojista para o endpoint de autorização com os parâmetros do seu App:

    GET /oauth/authorize
    https://app.sellitbr.com/oauth/authorize
      ?response_type=code
      &client_id=SEU_CLIENT_ID
      &redirect_uri=https://seuapp.com/oauth/callback
      &scope=products:read products:write stock:write
      &state=UM_VALOR_ALEATORIO
      &code_challenge=CHALLENGE_GERADO_ACIMA
      &code_challenge_method=S256

    O lojista verá os escopos solicitados e poderá autorizar ou negar. Em seguida será redirecionado para sua redirect_uri:

    Aprovado
    https://seuapp.com/oauth/callback?code=ac_...&state=SEU_STATE
    Negado
    https://seuapp.com/oauth/callback?error=access_denied&state=SEU_STATE

    Verifique sempre o parâmetro error antes de tentar trocar o code. O code expira em 10 minutos e só pode ser usado uma vez.

  2. 2. Troque o code por tokens

    No seu backend, troque o code por um access token. Envie a client_secret apenas server-side.

    POST /api/oauth/token
    curl -X POST https://app.sellitbr.com/api/oauth/token \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -d grant_type=authorization_code \
      -d client_id=SEU_CLIENT_ID \
      -d client_secret=SUA_CLIENT_SECRET \
      -d code=O_CODE_RECEBIDO \
      -d redirect_uri=https://seuapp.com/oauth/callback \
      -d code_verifier=O_VERIFIER_DO_PKCE
    200 OK
    {
      "access_token": "eyJ...",
      "token_type": "Bearer",
      "expires_in": 7200,
      "refresh_token": "rt_...",
      "scope": "products:read products:write stock:write"
    }
  3. 3. Chame a API com o Bearer token

    GET /api/v1/products
    curl https://app.sellitbr.com/api/v1/products \
      -H "Authorization: Bearer SEU_ACCESS_TOKEN"
  4. 4. Renove o token quando expirar

    O access token dura 2 horas. O refresh token dura 30 dias e é rotativo: cada uso emite um novo par (access_token + refresh_token). Sempre salve o novo refresh_token recebido — o anterior é invalidado imediatamente.

    POST /api/oauth/token
    curl -X POST https://app.sellitbr.com/api/oauth/token \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -d grant_type=refresh_token \
      -d client_id=SEU_CLIENT_ID \
      -d client_secret=SUA_CLIENT_SECRET \
      -d refresh_token=SEU_REFRESH_TOKEN
    200 OK
    {
      "access_token": "eyJ...",
      "token_type": "Bearer",
      "expires_in": 7200,
      "refresh_token": "rt_NOVO_TOKEN",
      "scope": "products:read products:write stock:write"
    }
    Se o refresh token expirar (30 dias sem uso) ou for usado mais de uma vez, a resposta retorna 400 invalid_grant. Nesse caso, o lojista precisa reautorizar o seu App.

Escopos disponíveis

Solicite apenas os escopos que seu App realmente precisa. O lojista consente cada um.

EscopoPermite
products:readLer produtos
products:writeCriar e editar produtos
stock:writeAtualizar estoque
categories:readLer categorias
customers:readLer clientes
orders:readLer pedidos
orders:writeAtualizar status e reembolsar pedidos
shipping:readConsultar fretes e rastreamento
shipping:writeGerar etiquetas e atualizar envios
media:writeEnviar imagens de produtos
webhooks:manageGerenciar webhooks
Uma chamada com escopo insuficiente retorna 403 insufficient_scope. Token ausente, expirado ou revogado retorna 401 invalid_token.

Erros do token endpoint

O endpoint POST /api/oauth/token retorna erros no formato RFC 6749: { "error": "...", "error_description": "..." }.

EscopoPermite
invalid_clientclient_id ou client_secret incorretos.
invalid_grantcode ou refresh_token inválido, expirado ou já usado. Para authorization_code, também cobre PKCE incorreto.
invalid_requestParâmetros obrigatórios ausentes (ex.: grant_type, code).
unsupported_grant_typegrant_type não é authorization_code nem refresh_token.