OAuth (Ligar com o Vero)

Referência dos endpoints para plataformas que emitem em nome de outras empresas. Para o passo a passo, veja o guia.

Registar a app

Sem pedir ao Vero: no painel, em Chaves de API → Apps OAuth, ou pela API com a sessão do painel (não com chave de API nem token OAuth).

  • Teste: qualquer empresa. Emite em sandbox, nada vai à AGT.
  • Live: só empresas activas na AGT (com a chave AGT configurada). Emite facturas reais.
  • Uma app live por verificar liga no máximo 100 empresas, e o ecrã de autorização avisa que não foi verificada. Para tirar o limite, peça a verificação ao suporte.
GET/v1/organisations/:orgId/oauth-appsApps da empresa, com empresas ligadas e limite
POST/v1/organisations/:orgId/oauth-appsRegistar. Devolve o client_secret uma única vez
PATCH/v1/organisations/:orgId/oauth-apps/:idEditar nome, retornos, permissões ou webhook (o ambiente não muda)
DELETE/v1/organisations/:orgId/oauth-apps/:idApagar. As empresas ligadas perdem o acesso na hora
POST/v1/organisations/:orgId/oauth-apps/:id/rotate-secretNovo client_secret (o anterior deixa de valer)
GET/v1/organisations/:orgId/oauth-apps/:id/connectionsEmpresas ligadas
POST/v1/organisations/:orgId/oauth-apps/:id/webhook/pingEnviar um evento de teste
POST/v1/organisations/:orgId/oauth-apps/:id/webhook/rotate-secretNovo segredo de assinatura do webhook
GET/v1/organisations/:orgId/oauth-apps/:id/webhook/eventsÚltimos eventos enviados
POST/v1/organisations/:orgId/oauth-apps/:id/webhook/events/:eventId/redeliverReenviar um evento

Corpo do POST

JSON
{
  "name": "Loja Exemplo",
  "environment": "test",
  "redirectUris": ["https://loja.exemplo.ao/vero/callback"],
  "allowedScopes": ["organisation:read", "invoices:write", "customers:write"],
  "website": "https://loja.exemplo.ao",
  "logoUrl": "https://loja.exemplo.ao/logo.png",
  "contactEmail": "dev@loja.exemplo.ao",
  "webhookUrl": "https://loja.exemplo.ao/vero/webhook"
}

redirectUris: https (numa app Test também http://localhost ou 127.0.0.1; numa Live nunca) e sem #. webhookUrl: https público (nunca localhost; para testar use um túnel como o ngrok). Erros: 400 invalid_redirect_uri, 400 invalid_scope, 400 invalid_webhook_url, 403 org_not_activated (live sem chave AGT).

Autorização

Envie o comerciante para o ecrã do Vero (Authorization Code com PKCE S256, obrigatório):

Terminal
https://vero.ao/oauth/authorize
  ?response_type=code
  &client_id=app_test_...
  &redirect_uri=https://loja.exemplo.ao/vero/callback
  &scope=organisation:read invoices:write
  &state=ALEATORIO
  &code_challenge=BASE64URL(SHA256(code_verifier))
  &code_challenge_method=S256

Volta ao redirect_uri com code (vale 60 segundos, uma vez) e state, ou com error (ex.: access_denied).

Tokens

POST/oauth/tokengrant_type=authorization_code (code, redirect_uri, code_verifier) ou refresh_token
POST/oauth/revokeRevogar um token (token=...). Desliga a empresa da app
GET/v1/oauth/token-infoEmpresa, permissões, app e ambiente do token em uso
GET/.well-known/oauth-authorization-serverMetadados (RFC 8414)

/oauth/token e /oauth/revoke aceitam application/x-www-form-urlencoded ou JSON, com o cliente em Basic auth ou em client_id/client_secret no corpo. Só a partir do servidor.

JSON
{
  "access_token": "vero_oat_...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "vero_ort_...",
  "scope": "organisation:read invoices:write",
  "organisation": { "id": "8f1c...", "name": "Padaria Kianda, Lda", "taxId": "5000123456" }
}

O access token dura 1 hora. O refresh token muda a cada renovação e expira após 90 dias sem uso; se um refresh token já usado voltar a aparecer, a ligação é desligada (provável roubo).

O scope devolvido é o que a empresa aceitou, que pode ser menos do que o pedido: no ecrã de autorização o comerciante desmarca permissões, e depois pode retirar outras sem desligar a app. Uma permissão retirada vale de imediato (403 insufficient_scope com o mesmo token).

Com organisation:read, GET /v1/organisations/:id devolve só: id, name, legalName, taxId, email, phone, website, morada (addressLine1, addressLine2, city, province, country), logoUrl, eacCode, ivaRegime (geral | reduzido | simplificado | isento | null), defaultTaxRate, taxExemptionCode, status, agtKeysConfigured e partialPaymentsEnabled (sempre true: todas as empresas aceitam recibos parciais).

Webhooks da app

Um URL por app recebe os eventos de todas as empresas ligadas. Cada pedido traz X-Vero-Signature: sha256=HMAC_SHA256(segredo, corpo), X-Vero-Event e X-Vero-Delivery (id único, para ignorar repetidos). Sem resposta 2xx, o Vero tenta de novo após 1 min, 5 min, 30 min, 2 h e 8 h.

invoice.agt_validatedFactura emitida pela app validada pela AGT
invoice.agt_rejectedFactura rejeitada pela AGT, com os erros
credit_note.agt_validatedNota de crédito validada pela AGT
credit_note.agt_rejectedNota de crédito rejeitada pela AGT
app.authorization.revokedA empresa desligou a app (ou foi desligada)
pingEvento de teste enviado do painel