Pular para o conteúdo

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/json

O 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 .:

  1. <t>: o mesmo valor do cabeçalho.
  2. <MÉTODO>: em maiúsculas (POST, PUT).
  3. <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.
  4. <Idempotency-Key>: o valor do cabeçalho, sem espaços nas pontas.
  5. <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 t atual, a mesma chave e o mesmo corpo: a assinatura é conferida antes da idempotência e vale só por 5 minutos.
  • Respostas 5xx não ficam guardadas: repetir tenta de novo. 401, 403 e 429 també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 um 422, 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 ​

  • 401 assinatura_invalida: a assinatura está ausente, mal formada, fora da janela de 5 minutos ou não confere. A message diz qual. As causas mais comuns:
    • assinar só /ctes em 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.
  • 400 idempotency_key_ausente: faltou o cabeçalho Idempotency-Key, ou ele passou de 100 caracteres.
  • 409 idempotency_key_reutilizada: a chave já foi usada com outro método, caminho ou corpo. Gere uma chave nova para cada operação.
  • 409 idempotency_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.

Documentação da API da Subcontratei.