Autenticação e assinatura
Toda chamada leva o token da credencial. As escritas (POST e PUT) levam também a assinatura, feita com o segredo, e uma chave de idempotência. Esta página mostra como montar os três.
Token
Envie o token da credencial no cabeçalho Authorization, em toda chamada:
http
Authorization: Bearer sbc_live_…
Accept: application/json
Content-Type: application/jsonO token não é o login de um usuário: ele vem da credencial emitida pela equipe Subcontratei (credenciais). Para obtê-lo, peça a credencial à equipe. O token de teste (sbc_test_…) chega antes do de produção, e os dois chegam por link de uso único, em canais separados do segredo. Para trocar um token, peça um novo à equipe: o anterior vale por mais 24 h. Token ausente, inválido, vencido ou revogado recebe 401. Assim que receber o token, consulte a credencial para conferir o ambiente e os escopos.
Assinatura das escritas
Os POST e PUT levam o cabeçalho X-Assinatura, calculado com o segredo de assinatura (sbcsig_…). Mesmo que o token vaze, sem o segredo não dá para criar nem alterar nada, e uma chamada assinada não pode ser repetida depois nem reaproveitada em outra rota.
http
X-Assinatura: t=<unix>,v2=<hex>
Idempotency-Key: <chave única da operação>t: o instante da chamada em segundos Unix. Precisa estar a até 5 minutos do relógio do servidor: mantenha o relógio da máquina sincronizado.v2:HMAC-SHA256(segredo, "<t>.<MÉTODO>.<caminho>.<Idempotency-Key>.<corpo>"), em hexadecimal minúsculo.
As partes do texto assinado, unidas por .:
<t>: o mesmo valor do cabeçalho.<MÉTODO>: em maiúsculas (POST,PUT).<caminho>: o caminho completo da URL chamada, sem domínio, sem query string e sem a barra final. Por exemplo,/services/v1/integracoes/ctes, não só/ctes.<Idempotency-Key>: o valor do cabeçalho, sem espaços nas pontas.<corpo>: exatamente o texto enviado, byte a byte. Nas rotas sem corpo, use o texto vazio.
Assine o que você envia
Gere o JSON uma vez, guarde o texto numa variável e use a mesma variável na assinatura e no corpo da requisição. Serializar duas vezes pode mudar a ordem dos campos ou os espaços, e a assinatura deixa de conferir.
Idempotência
Toda escrita exige Idempotency-Key: um texto de até 100 caracteres, único por operação. Um UUID serve.
- Por 24 h, repetir a mesma chave com o mesmo método, caminho e corpo devolve a mesma resposta, com o cabeçalho
Idempotent-Replayed: true, sem fazer a operação de novo. Use isso para repetir com segurança uma chamada que deu tempo esgotado ou erro de rede. - Ao repetir, assine de novo com um
tatual, a mesma chave e o mesmo corpo: a assinatura é conferida antes da idempotência e vale só por 5 minutos. - Respostas
5xxnão ficam guardadas: repetir tenta de novo.401,403e429também não: corrigida a causa, a mesma chave executa. - As recusas de negócio (
400,404,409,422) ficam guardadas como os sucessos. Para mandar os dados corrigidos depois de um422, use uma chave nova. - Uma operação nova pede uma chave nova. A mesma chave com outro método, caminho ou corpo é recusada.
Exemplo em JavaScript
Node.js 18 ou mais novo, sem dependências:
js
import { createHmac, randomUUID } from 'node:crypto'
const BASE = 'https://api.logmonitor.com.br'
const token = process.env.SUBCONTRATEI_TOKEN // sbc_live_… ou sbc_test_…
const segredo = process.env.SUBCONTRATEI_SEGREDO // sbcsig_…
async function escrever(metodo, caminho, payload) {
const corpo = payload === undefined ? '' : JSON.stringify(payload)
const chave = randomUUID()
const t = Math.floor(Date.now() / 1000)
const v2 = createHmac('sha256', segredo)
.update(`${t}.${metodo}.${caminho}.${chave}.${corpo}`)
.digest('hex')
const resposta = await fetch(BASE + caminho, {
method: metodo,
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
'Content-Type': 'application/json',
'Idempotency-Key': chave,
'X-Assinatura': `t=${t},v2=${v2}`,
},
body: corpo || undefined,
})
return resposta.json()
}
// Emitir um CT-e (corpo resumido; veja os campos em CT-e)
await escrever('POST', '/services/v1/integracoes/ctes', {
conteudo: { emitente: { cnpj: '00000000000000' } /* identificacao, remetente, carga… */ },
})Exemplo em PHP
PHP 8 com a extensão cURL:
php
<?php
function escrever(string $metodo, string $caminho, ?array $payload = null): array
{
$token = getenv('SUBCONTRATEI_TOKEN'); // sbc_live_… ou sbc_test_…
$segredo = getenv('SUBCONTRATEI_SEGREDO'); // sbcsig_…
$corpo = $payload === null ? '' : json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$chave = bin2hex(random_bytes(16));
$t = time();
$v2 = hash_hmac('sha256', "{$t}.{$metodo}.{$caminho}.{$chave}.{$corpo}", $segredo);
$ch = curl_init('https://api.logmonitor.com.br' . $caminho);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $metodo,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => $corpo,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer {$token}",
'Accept: application/json',
'Content-Type: application/json',
"Idempotency-Key: {$chave}",
"X-Assinatura: t={$t},v2={$v2}",
],
]);
$resposta = curl_exec($ch);
curl_close($ch);
return json_decode($resposta, true);
}
// Emitir um CT-e (corpo resumido; veja os campos em CT-e)
escrever('POST', '/services/v1/integracoes/ctes', [
'conteudo' => ['emitente' => ['cnpj' => '00000000000000']], // identificacao, remetente, carga…
]);Erros mais comuns
401assinatura_invalida: a assinatura está ausente, mal formada, fora da janela de 5 minutos ou não confere. Amessagediz qual. As causas mais comuns:- assinar só
/ctesem vez do caminho completo/services/v1/integracoes/ctes; - incluir a query string ou a barra final no caminho;
- assinar um JSON e enviar outro (serializado de novo, com outra ordem ou outros espaços);
- usar o token no lugar do segredo, ou o segredo de outra credencial;
- relógio da máquina adiantado ou atrasado mais de 5 minutos;
- enviar a assinatura antiga
v1(só o corpo), que não é mais aceita.
- assinar só
400idempotency_key_ausente: faltou o cabeçalhoIdempotency-Key, ou ele passou de 100 caracteres.409idempotency_key_reutilizada: a chave já foi usada com outro método, caminho ou corpo. Gere uma chave nova para cada operação.409idempotency_key_em_uso: outra chamada com a mesma chave ainda está em andamento. Espere a resposta dela. Uma chamada que morreu no meio libera a chave depois de 5 minutos.
Os demais códigos estão em respostas, erros e limites.