A autenticação da Malvo segue duas superfícies paralelas, projetadas para separar o que é server-side (com acesso total aos dados) do que pode chegar ao browser do usuário (escopo restrito ao widget). Toda a API vive sob um único host: https://api.malvo.io. A cadeia completa parte das suas credenciais de aplicação e termina em um token efêmero entregue ao cliente:

As duas superfícies

A Malvo expõe credenciais com escopos deliberadamente diferentes. Entender qual usar em cada lugar é o ponto central da segurança da integração.
O connectToken é escopado ao widget. Qualquer tentativa de ler dados detalhados de produto (contas, transações, cartões etc.) usando um connect token retorna 403 Forbidden. Dados de produto só são lidos server-side com X-API-KEY.
Cada token é consumido de forma atômica na primeira criação/atualização e fica vinculado a um único itemId. Um retry idêntico retoma somente esse Item (e reutiliza a autorização já persistida); ele nunca cria um segundo Item. Trocar o Connector retorna 409. Durante a continuação OAuth, polling e MFA também só conseguem acessar o Item vinculado.

Passo a passo

1

Obtenha clientId e clientSecret

As credenciais da aplicação são emitidas no Dashboard, uma por Application. O clientSecret é server-side e nunca deve chegar ao browser.
2

Troque por um apiKey via POST /auth

Envie clientId e clientSecret para POST /auth. A resposta traz um apiKey com TTL de exatamente 2 horas, usado no header X-API-KEY de todas as chamadas server-side.
3

Mint de um connectToken via POST /connect_token

Com o apiKey no header, chame POST /connect_token para gerar um token de 30 minutos. O corpo pode ser {} (cria um novo item) ou conter itemId (modo de atualização de um item existente).
4

Entregue o connectToken ao cliente

O accessToken retornado é o único valor de autenticação que pode chegar ao browser. Ele dirige o Connect Widget enquanto o usuário autentica no banco dele.

POST /auth — credenciais para apiKey

Sem header de autenticação. Limite: 360 requisições/min por IP.
Resposta 200:
O apiKey é uma string opaca (JWT-like) com TTL de exatamente 2 horas a partir da emissão. Emitir um novo apiKey não invalida os anteriores — a validação é stateless e só observa a expiração.

Cache e refresh do apiKey

O apiKey deve ser cacheado e reutilizado entre chamadas — não chame POST /auth a cada requisição. Renove proativamente:
  • Antecipadamente, por volta de 1h50m após a emissão (margem antes do TTL de 2h).
  • Reativamente, no primeiro 401 recebido em um endpoint de dados (apiKey expirou): refaça POST /auth uma vez e repita a chamada original.

POST /connect_token — apiKey para connectToken

Requer header X-API-KEY: <apiKey>. Todos os campos do corpo são opcionais — {} é válido e retorna um token para criar um novo item.
Resposta 200:
O accessToken tem TTL de exatamente 30 minutos. Gere um token novo toda vez que o usuário abrir o widget. Para atualizar um item existente (re-autenticação, MFA, renovação de consentimento), inclua itemId no corpo — o widget entra em modo de atualização.

O detalhe 403-vs-401 no /connect_token

Atenção à semântica 403, não 401: falha de autenticação no POST /connect_token (apiKey ausente, malformado ou expirado) retorna 403 Forbidden, e não 401. Trate esse 403 como “renove o apiKey via POST /auth e repita” — não como um problema de permissão. Em endpoints de dados, ao contrário, um apiKey inválido/expirado retorna 401.
Resumo do que cada código significa em cada superfície:
  • 401 em endpoint de dados → apiKey expirou; refaça POST /auth e repita.
  • 403 em POST /connect_token → apiKey ausente/expirado; refaça POST /auth e repita.
  • 403 em endpoint de dados → você usou um connectToken para ler dados de produto; use X-API-KEY server-side.

Segurança

clientId, clientSecret e apiKey nunca devem chegar ao browser. O único valor de autenticação que o cliente vê é o connectToken de 30 minutos. Mantenha o segredo e o apiKey estritamente server-side.
  • Nunca exponha o clientSecret no front-end, em apps mobile ou em repositórios públicos. Faça o POST /auth sempre a partir do seu backend.
  • O connectToken é de curta duração e de escopo mínimo por design: mesmo que vaze, só dirige o widget e lê o próprio item.
  • Os webhooks são a fonte autoritativa do estado assíncrono das conexões; o onSuccess do widget é best-effort (o usuário pode fechar o widget no meio do fluxo).
  • Validação rápida de um apiKey fresco: GET /connectors com X-API-KEY deve retornar a lista de connectors.