API de Integração

Consulte diários oficiais, jornais, editais e buscas por termo, e receba notificações por webhook diretamente nos seus sistemas.

Obter acesso

Seu token, liberado pela nossa equipe

O acesso à API é concedido por e-mail. Assim entendemos seu caso de uso e acompanhamos a integração desde o primeiro dia.

1. Envie um e-mail

Escreva para contato@adoo.com.br contando quem é você, sua empresa e o que pretende construir com a API.

2. Análise do pedido

Nossa equipe avalia o caso de uso, tira eventuais dúvidas e cadastra sua empresa como cliente da API.

3. Receba seu token

Enviamos o token de acesso para o e-mail técnico informado. Ele é exibido uma única vez: guarde-o em local seguro.

O botão abaixo abre seu programa de e-mail com uma mensagem pronta para completar. Se preferir, escreva direto para

Informe no e-mail: nome, empresa e CNPJ, e-mail técnico, caso de uso, diários de interesse e volume estimado de consultas. Para gerar um novo token ou revogar o atual, use o mesmo endereço.

Autenticação

Envie o token no header x-api-key

Todas as requisições precisam do token recebido por e-mail. Sem ele, ou com um token inválido, a API responde 401.

Exemplo de requisição

curl 'https://api.adoo.com.br/integration/v1/diarios' \
  -H 'x-api-key: SEU_TOKEN'

Resposta sem token ou com token inválido · 401

{
  "timestamp": "2026-01-10T14:32:10",
  "status": 401,
  "error": "Unauthorized",
  "message": "API key ausente ou inválida",
  "path": "/integration/v1/diarios"
}

Boas práticas

  • Use o token apenas no seu servidor. Nunca o coloque em aplicativos, páginas web ou repositórios de código.
  • Guarde-o em um cofre de segredos ou variável de ambiente. O Adoo não consegue exibi-lo de novo.
  • Suspeita de vazamento? Escreva para contato@adoo.com.br e revogamos o token e enviamos um novo.
  • O token dá acesso somente aos dados da sua empresa: termos e webhooks de outros clientes não são visíveis.

Convenções

Como a API funciona

Regras que valem para todos os endpoints.

URL base

https://api.adoo.com.br/integration/v1

Formato

Requisições e respostas em JSON (Content-Type: application/json), com campos em camelCase. Datas no formato AAAA-MM-DD e data-hora no formato ISO 8601 (2026-01-10T14:32:10).

Enums

Valores como poder, esfera e uf são enviados e retornados em maiúsculas, ex.: EXECUTIVO, SP.

Paginação

Listagens paginadas aceitam page (começa em 0) e size (padrão 20, máximo 100; na busca, máximo 50) e respondem neste formato:

{
  "content": [ ... ],
  "page": 0,
  "size": 20,
  "totalElements": 134,
  "totalPages": 7
}

Referência da API

Endpoints

Todos os endpoints, parâmetros e exemplos de uso.

Diários

Catálogo de diários oficiais monitorados pelo Adoo. Use os IDs retornados aqui nos demais endpoints.

GET/diarios

Lista os diários disponíveis, com filtros opcionais.

Parâmetros de query

  • poderenum

    EXECUTIVO, LEGISLATIVO, JUDICIARIO, MINISTERIO_PUBLICO ou DEFENSORIA_PUBLICA.

  • esferaenum

    FEDERAL, ESTADUAL ou MUNICIPAL.

  • ufenum

    Sigla da UF em maiúsculas, ex.: SP, RJ, MG.

  • tipoJusticaenum

    ELEITORAL, TRABALHO, ESTADUAL ou FEDERAL (diários do Judiciário).

  • pageinteiro

    Página, começando em 0. Padrão: 0.

  • sizeinteiro

    Itens por página, de 1 a 100. Padrão: 20.

Requisição

curl -X GET 'https://api.adoo.com.br/integration/v1/diarios?poder=EXECUTIVO&uf=SP&page=0&size=20' \
  -H 'x-api-key: SEU_TOKEN'

Resposta · 200 OK

{
  "content": [
    {
      "id": 12,
      "name": "Diário Oficial do Estado de São Paulo",
      "poder": "EXECUTIVO",
      "esfera": "ESTADUAL",
      "uf": "SP",
      "tipoJustica": null
    }
  ],
  "page": 0,
  "size": 20,
  "totalElements": 1,
  "totalPages": 1
}
GET/diarios/{id}

Retorna um diário pelo ID.

Parâmetros de rota

  • idobrigatóriointeiro

    ID do diário.

Requisição

curl -X GET 'https://api.adoo.com.br/integration/v1/diarios/12' \
  -H 'x-api-key: SEU_TOKEN'

Resposta · 200 OK

{
  "id": 12,
  "name": "Diário Oficial do Estado de São Paulo",
  "poder": "EXECUTIVO",
  "esfera": "ESTADUAL",
  "uf": "SP",
  "tipoJustica": null
}
  • Retorna 404 se o diário não existir ou estiver desativado.

Jornais

Edições (cadernos) publicadas por um diário em uma data, com link para o PDF.

GET/journals

Lista os jornais de um diário em uma data.

Parâmetros de query

  • diarioIdobrigatóriointeiro

    ID do diário.

  • dateobrigatóriodata

    Data da edição, no formato AAAA-MM-DD.

Requisição

curl -X GET 'https://api.adoo.com.br/integration/v1/journals?diarioId=12&date=2026-01-10' \
  -H 'x-api-key: SEU_TOKEN'

Resposta · 200 OK

[
  {
    "id": 845210,
    "code": "EXEC-1",
    "name": "Executivo - Caderno 1",
    "description": "Poder Executivo - Seção I",
    "date": "2026-01-10",
    "pages": 184,
    "link": "https://.../doe-sp-2026-01-10-exec-1.pdf"
  }
]
  • Retorna uma lista vazia quando não houve publicação na data.
  • Retorna 404 se o diário não existir.

Editais

Editais de concurso público e processo seletivo identificados por IA nos jornais do dia.

GET/editais

Lista os editais encontrados em um diário em uma data.

Parâmetros de query

  • diarioIdobrigatóriointeiro

    ID do diário.

  • dateobrigatóriodata

    Data da edição, no formato AAAA-MM-DD.

Requisição

curl -X GET 'https://api.adoo.com.br/integration/v1/editais?diarioId=12&date=2026-01-10' \
  -H 'x-api-key: SEU_TOKEN'

Resposta · 200 OK

[
  {
    "id": 5521,
    "journalId": 845210,
    "type": "CONCURSO_PUBLICO",
    "diario": { "id": 12, "name": "Diário Oficial do Estado de São Paulo" },
    "date": "2026-01-10",
    "link": "https://.../doe-sp-2026-01-10-exec-1.pdf",
    "page": "37",
    "contestName": "Concurso Público para Analista Administrativo",
    "organization": "Secretaria da Fazenda",
    "noticeNumber": "01/2026",
    "examBoard": "FGV",
    "positions": "120 vagas de Analista Administrativo",
    "requirements": "Ensino superior completo",
    "salary": "R$ 8.500,00",
    "registration": "Pelo site da banca",
    "importantDates": "Inscrições até 10/02/2026",
    "otherInfo": null
  }
]
  • type é CONCURSO_PUBLICO ou PROCESSO_SELETIVO.
  • Campos que a IA não conseguiu identificar vêm como null.

Busca

Busca por termo exato (frase) no texto dos jornais.

GET/search

Busca um termo exato em um ou mais diários, dentro de um período.

Parâmetros de query

  • termobrigatóriotexto

    Termo ou frase exata, com pelo menos 3 caracteres.

  • diarioIdsobrigatóriolista de inteiros

    De 1 a 50 IDs de diário, separados por vírgula.

  • startDateobrigatóriodata

    Início do período (AAAA-MM-DD).

  • endDateobrigatóriodata

    Fim do período (AAAA-MM-DD). Até 31 dias após startDate.

  • pageinteiro

    Página, começando em 0. Padrão: 0.

  • sizeinteiro

    Itens por página, de 1 a 50. Padrão: 20.

Requisição

curl -X GET 'https://api.adoo.com.br/integration/v1/search?term=licita%C3%A7%C3%A3o%20emergencial&diarioIds=12,15&startDate=2026-01-01&endDate=2026-01-31' \
  -H 'x-api-key: SEU_TOKEN'

Resposta · 200 OK

{
  "content": [
    {
      "diarioId": 12,
      "diarioName": "Diário Oficial do Estado de São Paulo",
      "journalCode": "EXEC-1",
      "journalName": "Executivo - Caderno 1",
      "date": "2026-01-10",
      "page": 42,
      "excerpt": "... aviso de <adootag>licitação emergencial</adootag> para aquisição de ...",
      "link": "https://.../doe-sp-2026-01-10-exec-1.pdf"
    }
  ],
  "page": 0,
  "size": 20,
  "totalElements": 1,
  "totalPages": 1
}
  • O trecho encontrado vem destacado entre as tags <adootag> e </adootag> no campo excerpt.
  • Resultados ordenados da data mais recente para a mais antiga.
  • A paginação alcança no máximo os 10.000 primeiros resultados; refine a busca se precisar ir além.
  • Retorna 502 se o serviço de busca estiver indisponível no momento.

Termos monitorados

Termos que você quer monitorar em diários específicos. Eles alimentam o evento de webhook ALERTA_NOTIFICACAO.

GET/terms

Lista os termos monitorados pela sua conta.

Parâmetros de query

  • pageinteiro

    Página, começando em 0. Padrão: 0.

  • sizeinteiro

    Itens por página, de 1 a 100. Padrão: 20.

Requisição

curl -X GET 'https://api.adoo.com.br/integration/v1/terms?page=0&size=20' \
  -H 'x-api-key: SEU_TOKEN'

Resposta · 200 OK

{
  "content": [
    {
      "id": 31,
      "term": "licitação emergencial",
      "active": true,
      "diarioIds": [12, 15],
      "createdAt": "2026-01-05T14:32:10",
      "updatedAt": "2026-01-05T14:32:10"
    }
  ],
  "page": 0,
  "size": 20,
  "totalElements": 1,
  "totalPages": 1
}
POST/terms

Cadastra um termo para monitoramento.

Corpo (JSON)

  • termobrigatóriotexto

    De 3 a 255 caracteres (espaços nas pontas são ignorados).

  • diarioIdsobrigatóriolista de inteiros

    De 1 a 100 IDs de diário. IDs repetidos são ignorados.

Requisição

curl -X POST 'https://api.adoo.com.br/integration/v1/terms' \
  -H 'x-api-key: SEU_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"term": "licitação emergencial", "diarioIds": [12, 15]}'

Resposta · 201 Created

{
  "id": 31,
  "term": "licitação emergencial",
  "active": true,
  "diarioIds": [12, 15],
  "createdAt": "2026-01-05T14:32:10",
  "updatedAt": "2026-01-05T14:32:10"
}
  • Retorna 409 se você já monitora o mesmo termo (sem diferenciar maiúsculas e minúsculas).
  • Retorna 400 listando os IDs de diário inválidos, se houver.
DELETE/terms/{id}

Remove um termo monitorado.

Parâmetros de rota

  • idobrigatóriointeiro

    ID do termo.

Requisição

curl -X DELETE 'https://api.adoo.com.br/integration/v1/terms/31' \
  -H 'x-api-key: SEU_TOKEN'

Resposta

204 No Content, sem corpo.

  • Retorna 404 se o termo não existir ou não pertencer à sua conta.

Webhooks

Endpoints para cadastrar as URLs que recebem os eventos. Veja a seção Webhooks para o formato das notificações.

GET/webhooks

Lista os webhooks da sua conta.

Requisição

curl -X GET 'https://api.adoo.com.br/integration/v1/webhooks' \
  -H 'x-api-key: SEU_TOKEN'

Resposta · 200 OK

[
  {
    "id": 7,
    "url": "https://sua-empresa.com.br/webhooks/adoo",
    "event": "NOVO_JORNAL",
    "active": true,
    "diarioIds": [12],
    "createdAt": "2026-01-05T14:40:00",
    "updatedAt": "2026-01-05T14:40:00",
    "lastSuccessAt": "2026-01-10T07:15:22",
    "lastErrorAt": null
  }
]
  • O secret nunca é retornado nas consultas.
  • lastSuccessAt e lastErrorAt indicam a última entrega com e sem sucesso.
GET/webhooks/{id}

Retorna um webhook pelo ID.

Parâmetros de rota

  • idobrigatóriointeiro

    ID do webhook.

Requisição

curl -X GET 'https://api.adoo.com.br/integration/v1/webhooks/7' \
  -H 'x-api-key: SEU_TOKEN'

Resposta · 200 OK

{
  "id": 7,
  "url": "https://sua-empresa.com.br/webhooks/adoo",
  "event": "NOVO_JORNAL",
  "active": true,
  "diarioIds": [12],
  "createdAt": "2026-01-05T14:40:00",
  "updatedAt": "2026-01-05T14:40:00",
  "lastSuccessAt": "2026-01-10T07:15:22",
  "lastErrorAt": null
}
  • Retorna 404 se o webhook não existir ou não pertencer à sua conta.
POST/webhooks

Cadastra uma URL para receber um evento.

Corpo (JSON)

  • urlobrigatóriotexto

    URL https pública, com até 500 caracteres.

  • eventobrigatórioenum

    ALERTA_NOTIFICACAO, ALERTA_EDITAL_CONCURSO ou NOVO_JORNAL.

  • diarioIdslista de inteiros

    Obrigatório (1 a 100 IDs) para NOVO_JORNAL; ignorado nos demais eventos.

Requisição

curl -X POST 'https://api.adoo.com.br/integration/v1/webhooks' \
  -H 'x-api-key: SEU_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"url": "https://sua-empresa.com.br/webhooks/adoo", "event": "NOVO_JORNAL", "diarioIds": [12]}'

Resposta · 201 Created

{
  "id": 7,
  "url": "https://sua-empresa.com.br/webhooks/adoo",
  "event": "NOVO_JORNAL",
  "active": true,
  "diarioIds": [12],
  "createdAt": "2026-01-05T14:40:00",
  "updatedAt": "2026-01-05T14:40:00",
  "secret": "q3Zp0x8B1n-4kL7YtVd9sE2wR6uHjF5aC0mNbXiOgQs"
}
  • Guarde o secret: ele é exibido somente nesta resposta e serve para validar a assinatura das notificações.
  • A URL precisa usar https e apontar para um endereço público; endereços de rede interna são recusados (400).
  • Retorna 409 se já existir um webhook seu com a mesma URL e o mesmo evento.
PUT/webhooks/{id}

Atualiza URL, evento, status ou diários de um webhook.

Parâmetros de rota

  • idobrigatóriointeiro

    ID do webhook.

Corpo (JSON)

  • urltexto

    Nova URL https.

  • eventenum

    Novo evento.

  • activebooleano

    false pausa as entregas; true retoma.

  • diarioIdslista de inteiros

    Substitui a lista de diários (NOVO_JORNAL).

Requisição

curl -X PUT 'https://api.adoo.com.br/integration/v1/webhooks/7' \
  -H 'x-api-key: SEU_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"active": false}'

Resposta · 200 OK

{
  "id": 7,
  "url": "https://sua-empresa.com.br/webhooks/adoo",
  "event": "NOVO_JORNAL",
  "active": false,
  "diarioIds": [12],
  "createdAt": "2026-01-05T14:40:00",
  "updatedAt": "2026-01-12T09:02:41",
  "lastSuccessAt": "2026-01-10T07:15:22",
  "lastErrorAt": null
}
  • Envie apenas os campos que quer alterar.
POST/webhooks/{id}/secret

Gera um novo secret para o webhook e invalida o anterior.

Parâmetros de rota

  • idobrigatóriointeiro

    ID do webhook.

Requisição

curl -X POST 'https://api.adoo.com.br/integration/v1/webhooks/7/secret' \
  -H 'x-api-key: SEU_TOKEN'

Resposta · 200 OK

{
  "secret": "Hn7cV2kQ9pX0wZ4tB6mL1sJ8yR3eA5uD0fGiKoNqTbE"
}
  • As próximas notificações passam a ser assinadas com o novo secret imediatamente.
DELETE/webhooks/{id}

Remove um webhook.

Parâmetros de rota

  • idobrigatóriointeiro

    ID do webhook.

Requisição

curl -X DELETE 'https://api.adoo.com.br/integration/v1/webhooks/7' \
  -H 'x-api-key: SEU_TOKEN'

Resposta

204 No Content, sem corpo.

POST/webhooks/trigger

Reprocessa um evento para um diário e uma data.

Útil para testar sua integração ou recuperar notificações de um dia específico. O processamento é assíncrono: a resposta chega antes das notificações.

Corpo (JSON)

  • eventobrigatórioenum

    ALERTA_NOTIFICACAO ou ALERTA_EDITAL_CONCURSO.

  • diarioIdobrigatóriointeiro

    ID do diário.

  • dateobrigatóriodata

    Data a reprocessar (AAAA-MM-DD).

Requisição

curl -X POST 'https://api.adoo.com.br/integration/v1/webhooks/trigger' \
  -H 'x-api-key: SEU_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"event": "ALERTA_EDITAL_CONCURSO", "diarioId": 12, "date": "2026-01-10"}'

Resposta

202 Accepted, sem corpo.

  • NOVO_JORNAL é disparado automaticamente e não pode ser reprocessado (400).

Webhooks

Receba eventos no seu sistema

Cadastre uma URL com POST /webhooks e o Adoo envia um POST para ela a cada evento.

Entrega

  • A URL precisa usar https e ser acessível pela internet. Endereços de rede interna são recusados.
  • Responda com qualquer status 2xx em até 10 segundos. Processe o evento depois, de forma assíncrona.
  • Cada notificação é enviada uma única vez, sem novas tentativas. Redirecionamentos não são seguidos.
  • Para recuperar eventos de um dia, use POST /webhooks/trigger.
  • A última entrega bem-sucedida e a última falha aparecem em GET /webhooks.

Headers enviados

X-Adoo-Event
Nome do evento (o mesmo do campo event do corpo).
X-Adoo-Timestamp
Momento do envio, em segundos desde 01/01/1970 (UTC).
X-Adoo-Signature
sha256=<hex>, o HMAC-SHA256 de "<timestamp>.<corpo>" usando o secret do webhook.
User-Agent
Adoo-Webhook/1.0
Content-Type
application/json; charset=UTF-8
NOVO_JORNAL

Novo jornal publicado

Enviado quando um novo jornal de um dos diários cadastrados no webhook é publicado.

Corpo da notificação

{
  "event": "NOVO_JORNAL",
  "sentAt": "2026-01-10T10:15:22Z",
  "data": {
    "journalId": 845210,
    "code": "EXEC-1",
    "name": "Executivo - Caderno 1",
    "date": "2026-01-10",
    "pages": 184,
    "link": "https://.../doe-sp-2026-01-10-exec-1.pdf",
    "diarioId": 12,
    "diarioName": "Diário Oficial do Estado de São Paulo"
  }
}
ALERTA_NOTIFICACAO

Termo encontrado

Enviado para cada ocorrência de um dos seus termos monitorados. Cada ocorrência gera uma notificação.

Corpo da notificação

{
  "event": "ALERTA_NOTIFICACAO",
  "sentAt": "2026-01-10T10:20:05Z",
  "data": {
    "diarioId": 12,
    "diarioName": "Diário Oficial do Estado de São Paulo",
    "journalCode": "EXEC-1",
    "date": "2026-01-10",
    "page": 42,
    "term": "licitação emergencial",
    "excerpt": "... aviso de <adootag>licitação emergencial</adootag> para aquisição de ...",
    "link": "https://.../doe-sp-2026-01-10-exec-1.pdf"
  }
}
ALERTA_EDITAL_CONCURSO

Editais do dia

Enviado com a lista de editais de concurso e processo seletivo encontrados em um diário na data.

Corpo da notificação

{
  "event": "ALERTA_EDITAL_CONCURSO",
  "sentAt": "2026-01-10T11:02:40Z",
  "data": [
    {
      "id": 5521,
      "journalId": 845210,
      "type": "CONCURSO_PUBLICO",
      "diario": { "id": 12, "name": "Diário Oficial do Estado de São Paulo" },
      "date": "2026-01-10",
      "link": "https://.../doe-sp-2026-01-10-exec-1.pdf",
      "page": "37",
      "contestName": "Concurso Público para Analista Administrativo",
      "organization": "Secretaria da Fazenda",
      "noticeNumber": "01/2026",
      "examBoard": "FGV",
      "positions": "120 vagas de Analista Administrativo",
      "requirements": "Ensino superior completo",
      "salary": "R$ 8.500,00",
      "registration": "Pelo site da banca",
      "importantDates": "Inscrições até 10/02/2026",
      "otherInfo": null
    }
  ]
}

Validando a assinatura

Calcule o HMAC-SHA256 de <X-Adoo-Timestamp>.<corpo cru> com o secret do webhook e compare com X-Adoo-Signature. Descarte notificações com timestamp antigo para evitar reenvios.

Node.js

import crypto from 'node:crypto';

// Use o corpo cru da requisição (string), antes de qualquer JSON.parse.
export function isValidAdooSignature(rawBody, headers, secret) {
  const timestamp = headers['x-adoo-timestamp'];
  const signature = headers['x-adoo-signature'] ?? '';

  // Recuse notificações com mais de 5 minutos para evitar reenvios.
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;

  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  return signature.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

Python

import hashlib
import hmac
import time

def is_valid_adoo_signature(raw_body: bytes, headers, secret: str) -> bool:
    timestamp = headers.get("X-Adoo-Timestamp", "")
    signature = headers.get("X-Adoo-Signature", "")

    # Recuse notificações com mais de 5 minutos para evitar reenvios.
    if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > 300:
        return False

    message = timestamp.encode() + b"." + raw_body
    expected = "sha256=" + hmac.new(secret.encode(), message, hashlib.sha256).hexdigest()
    return hmac.compare_digest(signature, expected)

Códigos de Erro

Quando algo dá errado

Erros retornam o status HTTP correspondente e um corpo JSON com a mensagem.

Status

400 Bad Request
Parâmetro ausente ou inválido, corpo JSON mal formado ou regra de negócio violada.
401 Unauthorized
Header x-api-key ausente, token inválido, expirado ou revogado.
404 Not Found
Recurso inexistente ou que não pertence à sua conta.
405 Method Not Allowed
Método HTTP não suportado pela rota.
409 Conflict
Recurso duplicado, como um termo ou webhook já cadastrado.
415 Unsupported Media Type
Content-Type diferente de application/json no corpo.
502 Bad Gateway
Serviço de busca indisponível no momento. Tente novamente em instantes.
500 Internal Server Error
Erro inesperado no Adoo. Se persistir, fale com a gente.

Formato do erro

{
  "timestamp": "2026-01-10T14:32:10",
  "status": 404,
  "error": "Not Found",
  "message": "Diário not found with id: '999'",
  "path": "/integration/v1/diarios/999"
}

Erro de validação do corpo · 400

{
  "timestamp": "2026-01-10T14:32:10",
  "status": 400,
  "error": "Bad Request",
  "message": "Falha na validação dos dados",
  "path": "/integration/v1/terms",
  "validationErrors": {
    "term": "term deve ter entre 3 e 255 caracteres"
  }
}

Pronto para integrar?

Escreva para contato@adoo.com.br e nossa equipe libera seu token.