Pular para o conteúdo
API de admissãov1OpenAPIPara IA

Avisos (webhook)

O Motor faz POST no endereço do webhook da integração da empresa quando a admissão chega a:

avisoquando
admissao.processadao eSocial aceitou; leva o nr_recibo
admissao.recusadao eSocial recusou; leva as ocorrencias. O escritório também é avisado
admissao.descartadao escritório descartou o evento
admissao.aguardando_escritoriocom o envio automático ligado, quando ele para no meio e o evento cai na fila do escritório; e quando a chave que enviou a admissão é revogada antes da transmissão

O "recebido" é a própria resposta do POST. Com o envio automático desligado, esperar o escritório é o normal e não gera aviso.

Por que o envio automático para

O motivo do aviso (e da consulta) diz por que a admissão passou a esperar uma pessoa do escritório. Os casos, em resumo — o texto exato pode mudar, então não decida pelo texto:

  • o evento não pôde ser montado com os dados do cadastro;
  • o envio automático foi desligado, ou a chave que enviou a admissão foi revogada;
  • a admissão foi alterada no Motor depois de recebida (dados ou montagem do evento) — dali em diante quem aprova é uma pessoa, nunca o envio automático;
  • o envio teve um erro técnico, o certificado não pôde ser usado, ou o eSocial precisa de outro evento da empresa antes deste — o envio automático nunca tenta de novo;
  • o escritório que atende a empresa está com o serviço suspenso;
  • uma falha do Motor impediu o envio automático de terminar.

Em todos eles a admissão segue na fila do escritório, e o resultado final (processada, recusada ou descartada) chega por aviso como sempre.

O corpo

Cabeçalhos: X-Motor-Aviso-Id, X-Motor-Evento e X-Motor-Assinatura.

{
  "id": "c0e1d2f3-0000-4000-8000-000000000001",
  "evento": "admissao.processada",
  "ocorrido_em": "2026-10-28T13:07:40-03:00",
  "ambiente": "PRODUCAO",
  "admissao": {
    "id": "9a3f0c55-7d7e-4a52-b8c1-5d9f3c2e1a10",
    "estado": "PROCESSADA",
    "matricula": "MOT-000042",
    "nr_recibo": "1.1.0000000000123456789",
    "ocorrencias": [],
    "motivo": null
  }
}

O aviso nunca leva o XML nem dados pessoais (CPF, salário, endereço): se o endereço do webhook estiver errado ou for tomado, nada disso vaza. Busque os XML pela consulta.

Regras de entrega

  • Pelo menos uma vez. O mesmo aviso pode chegar mais de uma vez: descarte o que tiver id já processado.
  • Sem ordem garantida. Vale o mais recente por ocorrido_em; ignore aviso mais antigo que o último que você aplicou àquela admissão.
  • Responda 2xx em até 10 segundos e processe depois. Qualquer outra resposta, ou nenhuma, gera nova tentativa, com intervalo crescente, por 72 horas. Depois disso o Motor para e avisa o escritório; o resultado continua na consulta.
  • Redirecionamento não é seguido: um 3xx conta como falha de entrega. Cadastre o endereço final.
  • Sem webhook cadastrado, os avisos não são enviados nem contam tentativa. Se o escritório cadastrar o endereço depois, só os avisos novos saem; os anteriores continuam valendo pela consulta.
  • Trocar o endereço troca o segredo na hora: o escritório recebe o segredo novo uma vez, e os avisos seguintes já saem assinados com ele. Combine a troca com o escritório para atualizar os dois lados juntos. Avisos que já estavam em nova tentativa seguem para o endereço novo.

Conferir a assinatura

X-Motor-Assinatura: t=<unix>,v1=<hex>. v1 é o HMAC-SHA256, em hexadecimal minúsculo, de t + "." + corpo cru, com o segredo do webhook (o texto exato que apareceu ao gerá-lo, em UTF-8) como chave. Confira sobre os bytes recebidos, antes de interpretar o JSON, e recuse t com mais de 5 minutos de diferença do seu relógio.

Node.js (Express com express.raw({ type: 'application/json' })):

import crypto from 'node:crypto';

export function avisoAutentico(corpoCru, cabecalho, segredo, toleranciaSeg = 300) {
  const partes = {};
  for (const item of String(cabecalho ?? '').split(',')) {
    const i = item.indexOf('=');
    if (i > 0) partes[item.slice(0, i).trim()] = item.slice(i + 1).trim();
  }
  const t = Number(partes.t);
  if (!Number.isInteger(t) || !/^[0-9a-f]{64}$/.test(partes.v1 ?? '')) return false;
  if (Math.abs(Date.now() / 1000 - t) > toleranciaSeg) return false;

  const esperado = crypto
    .createHmac('sha256', Buffer.from(segredo, 'utf8'))
    .update(Buffer.concat([Buffer.from(`${partes.t}.`, 'utf8'), corpoCru]))
    .digest();
  return crypto.timingSafeEqual(esperado, Buffer.from(partes.v1, 'hex'));
}

C# (.NET 6 ou mais novo):

using System.Security.Cryptography;
using System.Text;

public static bool AvisoAutentico(byte[] corpoCru, string cabecalho, string segredo, int toleranciaSeg = 300)
{
    string? t = null, v1 = null;
    foreach (var item in (cabecalho ?? "").Split(','))
    {
        var i = item.IndexOf('=');
        if (i <= 0) continue;
        var nome = item.Substring(0, i).Trim();
        var valor = item.Substring(i + 1).Trim();
        if (nome == "t") t = valor; else if (nome == "v1") v1 = valor;
    }
    if (t is null || v1 is null || v1.Length != 64 || !long.TryParse(t, out var ts)) return false;
    if (Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - ts) > toleranciaSeg) return false;

    byte[] recebido;
    try { recebido = Convert.FromHexString(v1); } catch (FormatException) { return false; }

    var prefixo = Encoding.UTF8.GetBytes(t + ".");
    var mensagem = new byte[prefixo.Length + corpoCru.Length];
    Buffer.BlockCopy(prefixo, 0, mensagem, 0, prefixo.Length);
    Buffer.BlockCopy(corpoCru, 0, mensagem, prefixo.Length, corpoCru.Length);

    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(segredo));
    return CryptographicOperations.FixedTimeEquals(hmac.ComputeHash(mensagem), recebido);
}