Autenticação
A API Externa usa OAuth 2.0 Client Credentials (RFC 6749 §4.4) — pensado pra integração servidor a servidor, sem usuário logado no meio. Você troca um client_id e um client_secret por um access token Bearer, e usa esse token em cada chamada.
Não existe refresh token. O token dura 1 hora (expires_in: 3600); quando expirar, peça outro com as mesmas credenciais.
Ciclo de vida das credenciais
Um usuário com a permissão API Externa gerencia credenciais em Config → Dados da Empresa → Integrações → API Externa. A criação e alterações sensíveis exigem confirmação da senha atual.
| Ação | Comportamento |
|---|---|
| Criar | Escolha nome, escopos e, opcionalmente, IPs individuais ou redes em CIDR. O client_secret aparece somente nessa resposta. |
| Expirar | A data configurada vale até o fim do dia informado. Depois dela, a credencial e seus tokens são recusados; novas emissões retornam invalid_client. |
| Rotacionar secret | Gera outro secret e revoga imediatamente todos os tokens anteriores. Copie o novo valor no momento da rotação. |
| Revogar | Desativa a credencial e invalida os tokens associados imediatamente. |
| Allowlist de IP | Quando configurada, toda chamada deve vir de um IP permitido; use um IP ou uma rede CIDR por linha. |
Segurança. Use a credencial somente em servidor. Nunca envie client_secret para navegador, aplicativo móvel, repositório ou URL.
Solicitar um token
Envie as credenciais via HTTP Basic (nunca na URL ou no corpo) para POST /oauth/token, com Content-Type: application/x-www-form-urlencoded.
| Campo | Obrigatório | Descrição |
|---|---|---|
| grant_type | Sim | Sempre client_credentials. |
| scope | Não | Lista de escopos separados por espaço. Ausente = emite todos os escopos da credencial. Se informado, deve ser subconjunto do que a credencial tem. |
Resposta
Sucesso retorna 200 com o token e Cache-Control: no-store. O campo scope devolvido reflete exatamente o que foi concedido a esse token — pode ser um subconjunto do que a credencial tem.
curl -s -u "SEU_CLIENT_ID:SEU_CLIENT_SECRET" \
-d "grant_type=client_credentials" \
-d "scope=clientes:ler os:ler" \
https://web.synerasis.com.br/api/external/v1/oauth/token
$ch = curl_init('https://web.synerasis.com.br/api/external/v1/oauth/token');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_USERPWD => 'SEU_CLIENT_ID:SEU_CLIENT_SECRET',
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'grant_type' => 'client_credentials',
'scope' => 'clientes:ler os:ler',
]),
]);
$resposta = json_decode(curl_exec($ch), true);
$token = $resposta['access_token'];
const auth = Buffer.from('SEU_CLIENT_ID:SEU_CLIENT_SECRET').toString('base64');
const resp = await fetch('https://web.synerasis.com.br/api/external/v1/oauth/token', {
method: 'POST',
headers: {
'Authorization': `Basic ${auth}`,
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'grant_type=client_credentials&scope=clientes%3Aler%20os%3Aler',
});
const { access_token } = await resp.json();
{
"access_token": "6f1a9c3e2b7d4158a0f9c62d7e3b1a4c9f0e2d5b8a7c6f1e3d9b0a4c7e2f1b6d",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "clientes:ler os:ler"
}
Escopos
Cada credencial recebe um conjunto fixo de escopos na criação. Um token só pode ter escopos que a credencial já tem — pedir mais que isso retorna invalid_scope.
| Escopo | Permite |
|---|---|
| clientes:ler | Campos básicos de GET /clientes e GET /clientes/{id}. |
| clientes:contato:ler | Uso de incluir=contato, filtro documento e campos de documento/contato/endereço. |
| os:ler | GET /ordens-servico e GET /ordens-servico/{id}. |
| produtos:ler | GET /produtos. |
| servicos:ler | GET /servicos. |
| contas-receber:ler | GET /contas-receber. |
| contas-pagar:ler | GET /contas-pagar. |
| boletos:ler | GET /boletos. |
Escopos sensíveis (financeiros e clientes:contato:ler) exigem que o administrador confirme a senha atual pra criar ou alterar uma credencial com eles.
Erros do token
Erros processados pelo POST /oauth/token seguem o formato padrão OAuth — não o envelope usado pelos endpoints de dados. Se outro método for enviado para essa rota, o roteador responde com o envelope global de erros da API.
{
"error": "invalid_client",
"error_description": "Não foi possível autenticar a credencial."
}
| HTTP | error | Causa |
|---|---|---|
| 400 | invalid_request | Requisição malformada ou mais de um método de autenticação. |
| 400 | invalid_scope | Escopo desconhecido ou não concedido à credencial. |
| 400 | unsupported_grant_type | grant_type diferente de client_credentials. |
| 401 | invalid_client | Client ID/secret inválido, inativo, revogado ou expirado — resposta não revela qual. |
| 413 | invalid_request | Corpo acima de 8 KiB. |
| 415 | invalid_request | Content-Type diferente de application/x-www-form-urlencoded. |
| 429 | temporarily_unavailable | Limite de tentativas excedido (20/min por IP, 5/min por credencial). |