Referência de API
O SpotHub conecta agências e veículos em torno do pedido de inserção. A agência lança a PI, o veículo recebe a programação, confirma a veiculação e devolve o checking. Esta referência cobre as três APIs públicas, endpoint por endpoint.
Do zero à primeira PI lida, em duas chamadas.
curl -X POST https://spothub.com.br/oauth/gam/v2.0/access_token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=password" \
-d "client_id=SEU_CLIENT_ID" \
-d "client_secret=SEU_CLIENT_SECRET" \
-d "username=SEU_USUARIO" \
-d "password=SUA_SENHA" \
-d "scope=fullcontrol"
curl https://spothub.com.br/veiculo/PIs?Veiculoidexterno=95 \
-H "Authorization: OAuth SEU_TOKEN"
| Você é | Use | Base | Endpoints |
|---|---|---|---|
| Veículo — rádio ou TV | API de Veículo | /veiculo | 9 |
| Agência ou sistema de mídia | API de Agência | /api/agencia v2 /agencia legada |
3 9 |
| Anunciante | API de Cliente | /client | 1 |
Homologação antes de produção
Todo integrador recebe credencial de homologação primeiro. O contrato é o mesmo nos dois ambientes; muda o host e o prefixo da chave. Rode o fluxo completo — inclusive os desfechos que não são sucesso — antes de pedir a credencial de produção.
OAuth 2.0 para as APIs de veículo, agência legada e cliente; chave de API para a v2 de agência.
POST /oauth/gam/v2.0/access_token, corpo
application/x-www-form-urlencoded, com grant_type=password,
client_id, client_secret, username,
password e scope=fullcontrol.
A resposta, e o que cada campo realmente entrega:
{"access_token":"4a154f7f-...!sygPyX2WIc3C0Lhx...",
"token_type":"Bearer",
"expires_in":0,
"refresh_token":"",
"scope":""}
| Campo | O que esperar |
|---|---|
| access_token | 115 caracteres. Trate como opaco — o formato é interno e pode mudar sem aviso; o que não muda é você devolvê-lo no header. |
| expires_in | 0. O token não expira hoje. Não programe renovação por tempo; se um dia passar a expirar, este campo trará o valor real. |
| refresh_token | Vem vazio. Não há fluxo de renovação disponível — para obter outro token, autentique de novo com usuário e senha. |
| scope | Vem vazio, mesmo que você envie scope=fullcontrol. O valor enviado não é ecoado. |
Credencial errada devolve 401 com uma forma própria de erro — não é o bloco
Messages do resto da API:
{"Code":11,"Message":"O usuário ou senha está incorreto."}
Os dois formatos de header são aceitos nas chamadas seguintes:
Authorization: OAuth <token>
Authorization: Bearer <token>
Em breve: a autenticação muda de host
A autenticação vai passar de spothub.com.br para
api.spothub.com.br. Muda só o host — mesmo caminho, mesmo
corpo de requisição, mesma resposta, e o token continua com o mesmo formato. Uma linha de
configuração do seu lado.
Ainda não está ativo, e esta página é atualizada quando estiver. Nada a fazer agora; a data vem por contato direto, com o endereço definitivo.
Duas garantias para o dia da troca: o token que você já tem continua valendo depois dela, e o endereço antigo responde durante a transição. Ou seja, você pode trocar a URL num dia e não precisa coordenar nada com o ciclo de vida do seu token.
Reaproveite o token
O endpoint de autenticação é limitado por IP. Guarde o token e reutilize entre chamadas em vez de autenticar a cada requisição — autenticar em laço é a causa mais comum de bloqueio durante a integração.
Header X-SpotHub-Key, com prefixo de ambiente
(spk_homo_ ou spk_prod_) seguido de 64 caracteres hexadecimais.
A chave é entregue uma única vez e não pode ser recuperada — guarde em
cofre de segredo no momento em que receber.
A agência vem da chave, nunca do corpo. Você não envia o seu identificador em lugar nenhum. Uma agência pode ter mais de uma chave ativa, o que permite rotação sem janela de indisponibilidade.
Base /veiculo · OAuth · 9 endpoints · leitura e retorno de veiculação.
É a API da emissora. Além de ler a programação a veicular, ela permite devolver o ciclo: mudar o status da PI, enviar checking parcial durante a campanha e checking final ao término, atualizar a própria grade de programação e responder ao e-mail que originou a PI.
Devolve os pedidos com dados cadastrais, grade de programação, inserções por dia e anexos.
| Parâmetro | Tipo | Descrição | |
|---|---|---|---|
| Numero | string | funciona | O único que seleciona. Sem ele, a resposta é 204. |
| Veiculoidexterno | string | escopo | Não seleciona: restringe. Com o código de um veículo seu, a PI vem; com o de outro licenciado, 204. |
| Status | string | ignorado | Sozinho dá 204. Com Numero, é ignorado — Status=P devolve PI que está em R. |
| PIGUID | uuid | ignorado | Sozinho dá 204, mesmo com GUID válido. |
| Agenciaid | int64 | ignorado | Sozinho dá 204. |
| Dataentradainicio | date-time | ignorado | Sozinho dá 204. |
| Dataentradafim | date-time | ignorado | Sozinho dá 204. |
| Retornaarquivos | boolean | não desliga | true e false devolvem o mesmo corpo. |
[{// array cru quando há resultado — ver Convenções
"agencia": { "codigo_externo": "117", "nome": "...", "cnpj": "00000000000100",
"cidade": "Brasília", "uf": "DF", "cep": "70316000" },
"cliente": { "nome": "...", "cnpj": "...", "ie": "" },
"pi": {
"piguid": "eeb7d9b3-27dd-4375-8e74-636da73f04ab",
"veiculoExternalID": "95", "numero": "107821", "versao": "2", "status": "P",
"periodo_veiculacao": "MARÇO\/2024",
"Inicio": "2024-03-26", "Termino": "2024-03-29", "vencimento": "0000-00-00",
"total_insercoes": "21", "total_bruto": "11128.32", "total_liquido": "8902.66",
"programacao": [{
"codigo": "1", "formato": "SPOT", "posicao": "MANHÃ",
"hora_inicio": "06:00", "hora_fim": "10:00", "peca": "PEÇA 1",
"total_insercoes": "21", "custo_unitario": "828.00", "duracao": 30,
"codigoPrograma": "P1",
"insercao": [{ "dia": 26, "quantidade": "6" }, { "dia": 27, "quantidade": "5" }]
}],
"anexo": []
}}]
Sete dos oito parâmetros não filtram
Medido em homologação: chame sempre com Numero. É o único
que seleciona; sem ele a resposta é 204 com corpo vazio, e os demais
parâmetros não mudam isso. Veiculoidexterno é a exceção parcial — ele
restringe o escopo, e é o que impede um licenciado de ler a PI de outro.
O spec declara os oito como filtros. Não são. Programe para o comportamento.
Retornaarquivos não desliga
true e false devolvem exatamente o mesmo corpo. Os anexos vêm
sempre, em base64 no campo anexo[].arquivo — não por URL. Respostas grandes
são esperadas; dimensione buffer e timeout com folga.
| Parâmetro | Tipo | Descrição | |
|---|---|---|---|
| Veiculoidexterno | string | opcional | id ou código externo do veículo |
Devolve VeiculoInfos — cadastro, contatos e
grade. Use para conferir se o seu código externo está corretamente vinculado.
É por aqui que o veículo aceita, recusa ou confirma a veiculação de uma PI.
| Campo | Tipo | Descrição | |
|---|---|---|---|
| PIGUID | uuid | obrig. | a PI a alterar |
| Status | string | obrig. | R recebido · A aprovado · E recusado · V veiculado |
| Observacao | string | opcional | texto livre |
| MotivoRecusa | string | opcional | preencha quando Status = E |
curl -X POST https://spothub.com.br/veiculo/AlterarStatusPI \
-H "Authorization: OAuth $TOKEN" -H "Content-Type: application/json" \
-d '{"PIGUID":"eeb7d9b3-27dd-4375-8e74-636da73f04ab",
"Status":"A","Observacao":"Grade confirmada"}'
Registra uma veiculação individual, à medida que acontece. Use durante a campanha.
É por aqui que se registra veiculação — o
CheckingFinal faz o mesmo, não o lote que o nome sugere.
Reenviar é seguro. A mesma posição, peça e data e hora não geram um segundo registro — se a sua chamada falhou por rede e você não sabe se chegou, repita. Mas a chave é por linha de programação: a mesma data e hora em outra posição são duas veiculações, não uma.
| Campo | Tipo | Descrição | |
|---|---|---|---|
| PIGUID | uuid | obrig. | a PI |
| ProgPosicao | string | obrig. | a posição, como veio em programacao[].posicao |
| ProgPeca | string | obrig. | a peça veiculada |
| DataHora | date-time | obrig. | quando foi ao ar |
| Obs | string | opcional | observação |
O nome engana: ele não envia a coleção
Apesar de aceitar um array, só o primeiro item é gravado. Do segundo em
diante nada entra, e a resposta ainda é 200 — o serviço não avisa que
descartou. Medido em 24/09/2026: dois itens válidos e distintos entraram, um foi
registrado.
Se você manda a campanha inteira numa chamada, está perdendo tudo menos a
primeira linha. Para registrar N veiculações, faça N chamadas — de preferência
a /veiculo/CheckingParcial, que faz exatamente a mesma coisa e cujo nome
não promete o contrário.
O que ele tem de próprio é marcar a PI como Veiculada — e só
quando CheckingCollection chega vazia. Mandando com item,
ele grava a veiculação e o status da PI não muda.
Ou seja, o fim de campanha são duas etapas: registre as veiculações uma a uma, e depois feche a PI com a coleção vazia.
| Campo | Tipo | Descrição | |
|---|---|---|---|
| PIGUID | uuid | obrig. | a PI |
| CheckingCollection | array | obrig. | lista de CheckingVeiculoSDT |
# ERRADO — a segunda veiculação é descartada em silêncio, e a resposta é 200
curl -X POST https://spothub.com.br/veiculo/CheckingFinal \
-H "Authorization: OAuth $TOKEN" -H "Content-Type: application/json" \
-d '{"PIGUID":"eeb7d9b3-27dd-4375-8e74-636da73f04ab",
"CheckingCollection":[
{"ProgPosicao":"MANHÃ","ProgPeca":"PEÇA 1",
"DataHora":"2024-03-26 07:12:00","Obs":""},
{"ProgPosicao":"MANHÃ","ProgPeca":"PEÇA 1",
"DataHora":"2024-03-26 09:41:00","Obs":""}]}'
# CERTO — uma chamada por veiculação...
curl -X POST https://spothub.com.br/veiculo/CheckingParcial \
-H "Authorization: OAuth $TOKEN" -H "Content-Type: application/json" \
-d '{"PIGUID":"eeb7d9b3-27dd-4375-8e74-636da73f04ab",
"ProgPosicao":"MANHÃ","ProgPeca":"PEÇA 1",
"DataHora":"2024-03-26 07:12:00","Obs":""}'
# ...e a coleção VAZIA para encerrar a PI
curl -X POST https://spothub.com.br/veiculo/CheckingFinal \
-H "Authorization: OAuth $TOKEN" -H "Content-Type: application/json" \
-d '{"PIGUID":"eeb7d9b3-27dd-4375-8e74-636da73f04ab",
"CheckingCollection":[]}'
Mantém a grade do veículo no SpotHub. É a chamada que mais reduz trabalho manual na sua ponta: é contra essa grade que o SpotHub casa a posição escrita na PI com um programa seu.
Não substitui a grade — atualiza programa a programa, pela SIGLA.
O que você mandar é criado ou atualizado; o que você não mandar fica como estava.
Pode enviar só o que mudou, e pode reenviar a grade inteira sem medo de duplicar.
Excluir só funciona em programa que já existe
Mandar "Excluir": true numa SIGLA que o SpotHub ainda não
conhece cria o programa ativo, em vez de não criar nada. O campo é lido
apenas na atualização. Para desativar, a sigla precisa já estar na grade.
| Campo | Tipo | Descrição | |
|---|---|---|---|
| VeiculoIDExterno | string | obrig. | o seu código de veículo |
| programacao | array | obrig. | lista de ProgramacaoSDT |
{"VeiculoIDExterno":"95",
"programacao":[
{"SIGLA":"P1","Descricao":"Manhã Total","Inicio":"06:00","Termino":"10:00",
"Duracao":240,"Excluir":false},
{"SIGLA":"N18S","Descricao":"Novela I (Sáb)","Inicio":"18:00","Termino":"19:00",
"Duracao":60,"Excluir":false}]}
E-mails retidos para conferência humana — ainda não geraram PI. Útil para saber que algo chegou mas não entrou.
| Parâmetro | Tipo | Descrição | |
|---|---|---|---|
| Datahora | date-time | opcional | só o que entrou a partir daí |
Devolve lista de ListaTriagem.
| Campo | Tipo | Descrição | |
|---|---|---|---|
| Base64 | string | obrig. | o PDF codificado |
| ExtCode | string | obrig. | código de acompanhamento; use-o para consultar o desfecho |
Os dois campos vazios devolvem 422 — os únicos 422 da API de
veículo. Base64 que não decodifica devolve 500 com BASE64RENDER,
e nada é armazenado: o serviço recusa em vez de aceitar um PDF corrompido.
O 200 significa que o PDF foi recebido e enfileirado, não
que virou PI. A leitura do documento acontece depois; acompanhe pelo
ExtCode.
| Campo | Tipo | Descrição | |
|---|---|---|---|
| PIGUID | uuid | obrig. | a PI |
| texto | string | obrig. | corpo da resposta |
| assinatura | string | opcional | assinatura ao pé |
Responde ao e-mail que originalmente trouxe a PI, na mesma conversa — para a sua equipe não precisar sair do sistema e abrir o cliente de e-mail.
Só funciona em PI que nasceu de e-mail. PI que entrou por outra via não
tem a que responder e a chamada devolve 404 — o mesmo 404 de PI
inexistente, sem distinguir os casos. É o único endpoint da API que usa 404.
Sucesso e falha de envio respondem igual
Não conseguindo enfileirar a mensagem, a resposta continua sendo 200 com
corpo vazio: o serviço monta a mensagem de erro internamente e não a devolve.
Não trate o 200 como confirmação de que o e-mail saiu.
Se isso importa na sua operação, confirme pela caixa de saída.
Duas gerações convivem. Comece pela v2.
Qual usar
A v2 (/api/agencia) é a recomendada para integração nova: tem
idempotência explícita, códigos de erro nomeados e usa o status HTTP com o significado
usual. A legada (/agencia) continua no ar, tem mais endpoints
— inclusive leitura de checking e status — e segue as convenções descritas em
Convenções.
Chave X-SpotHub-Key. A ordem das chamadas não é sugestão: a PI referencia a
campanha, não a cria.
GET /api/agencia/veiculos — descubra os veículos e os códigos externos. Aceita busca.POST /api/agencia/campanha — crie a campanha, identificada pelo external_id do seu sistema.POST /api/agencia/pi — lance a PI, uma por veículo.| HTTP | Resultado | Significa |
|---|---|---|
| 201 | gravada | PI nova |
| 201 | versao_id maior | já existia, conteúdo mudou — versão nova criada |
| 200 | sem_alteracao | conteúdo idêntico. Nada criado — e isso é sucesso |
A identidade de uma PI é o trio agência + veículo + número.
Não existe PUT: corrigir é reenviar o mesmo POST
com o conteúdo novo. Reenviar conteúdo idêntico é seguro e não duplica, o que torna a
retentativa trivial.
A chamada é síncrona por decisão — o download do anexo e a marca d'água acontecem dentro dela. Típico de 1 a 5 segundos; configure timeout de 120 segundos, não os 30 que a maioria dos clientes usa por padrão.
| Campo | O que é | Exemplo |
|---|---|---|
| insercoes[].dia | Dia do mês, 1 a 31 — não é data. Mês e ano vêm do período da campanha. | 26 |
| insercoes[].quantidade | Texto, porque aceita marcação de peça além de contagem. | "2" · "A" · "X" |
| anexos[] | Exatamente um entre url e conteudo_base64. Teto de 20 MB. | {"url":"…"} |
| Entidade | Resolvida por | Se não achar |
|---|---|---|
| Agência | a chave de API | — |
| Veículo | codigo_externo; na primeira PI, nome + cidade + uf | VEICULO_NAO_ENCONTRADO |
| Cliente | CNPJ; depois nome exato | cadastra automaticamente |
| Campanha | external_id do seu sistema | CAMPANHAIDEXT |
O casamento por nome é exato
Quando o veículo é identificado por nome, cidade e UF, a comparação é caractere a caractere,
incluindo acento e pontuação. Rádio Massa e
Radio Massa são veículos diferentes. Casando uma vez, o vínculo passa a existir
e as PIs seguintes podem usar só o codigo_externo.
Base /agencia, OAuth. Oferece leitura que a v2 ainda não tem — em especial
Checking e StatusPI, com que a agência acompanha o que o veículo
devolveu.
| Método | Rota | Parâmetros | O que faz |
|---|---|---|---|
| GET | /agencia/PIs | Pinumero, Status, Inicio, Termino | recupera PIs |
| GET | /agencia/Dados | — | dados da agência |
| POST | /agencia/NovaPI | corpo | cria pedido de inserção |
| POST | /agencia/Novacampanha | corpo | cria campanha |
| POST | /agencia/AnexaMaterial | corpo | envia material |
| GET | /agencia/Checking | GUID | checking devolvido pelo veículo |
| GET | /agencia/StatusPI | Campanhaextid, Piguid | status das PIs da campanha |
| GET | /agencia/VeiculoProgramacao | Veiculoextid, Codigoprograma | grade do veículo |
| GET | /agencia/VeiculoDados | Veiculoidexterno | cadastro do veículo |
Base /client · OAuth · para o anunciante acompanhar as próprias campanhas.
Recupera os pedidos de inserção do anunciante. Superfície mínima — se você precisa de mais do que a lista, fale com a equipe antes de desenhar a integração.
Os schemas reutilizados entre endpoints.
| Campo | Tipo | Descrição |
|---|---|---|
| piguid | uuid | identificador estável da PI — use-o, não o número |
| numero | string | número da PI no sistema da agência |
| versao | int64 | incrementa quando o conteúdo muda |
| status | string | P pendente · R recebido · A aprovado · E recusado · V veiculado |
| vendadireta | boolean | cliente direto, sem agência |
| Inicio · Termino | date | período da campanha |
| total_insercoes | int64* | soma das inserções |
| total_bruto · comissao · total_liquido | double* | valores da PI |
| fatura_* · nf_* | string | dados impressos na PI para faturamento e nota |
| programacao[] | array | as linhas da grade |
| material[] | array | peca, Titulo, duracao, formato |
| comentario[] | array | texto livre |
| anexo[] | array | nome, extensao, arquivo em base64 |
* O spec declara tipo numérico, mas o serviço devolve string. Ver Convenções.
| Campo | Tipo | Descrição |
|---|---|---|
| posicao | string | a posição como escrita na PI — MANHÃ, NOVELA I |
| codigoPrograma | string | o código na sua grade. Pode vir vazio — ver aviso |
| formato · titulo · peca | string | o que veicular |
| hora_inicio · hora_fim | string | faixa horária, HH:mm |
| duracao | int32 | segundos da peça |
| total_insercoes | int64* | inserções desta linha |
| custo_unitario · custo_total · desconto | double* | valores da linha |
| insercao[] | array | dia (dia do mês) e quantidade (texto) |
codigoPrograma pode vir vazio — e isso é intencional
Quando o SpotHub não casa a posição escrita na PI com um programa da sua grade com confiança suficiente, ele deixa o campo vazio em vez de chutar: um palpite errado colocaria a inserção no programa errado, e ninguém perceberia.
Trate vazio como “requer conferência humana”, nunca como erro de transmissão. Manter a grade
atualizada por POST /veiculo/Programacao é o que reduz esses casos.
| Campo | Tipo | Descrição |
|---|---|---|
| ProgPosicao | string | a posição, como veio em programacao[].posicao |
| ProgPeca | string | a peça veiculada |
| DataHora | date-time | momento da veiculação |
| Obs | string | observação |
| Campo | Tipo | Descrição |
|---|---|---|
| SIGLA | string | o código do programa — é ele que volta em codigoPrograma |
| Descricao | string | nome do programa |
| Inicio · Termino | string | faixa horária |
| Duracao | double | duração em minutos |
| Excluir | boolean | true desativa o programa — apenas se a SIGLA já existir. Em sigla nova, cria ativo |
codigo, nome, tipo, contato,
telefone, email, endereco, cidade,
uf, cep, cnpj, ie, status,
frequencia, pais, observacao,
razao_social, nome_fantasia, logotipo, mais as
coleções contacts[] e programacao[].
| Campo | Tipo | Descrição |
|---|---|---|
| Id | int64 | identificador da triagem |
| Data | date-time | quando entrou |
| Obs | string | por que foi retida |
| De · Email · Assunto | string | o e-mail de origem |
As APIs OAuth têm clientes em produção há anos. O comportamento abaixo é preservado e não vai mudar.
O spec declara tipo; o serviço devolve string
total_bruto é declarado number/double e chega como
"11128.32". total_insercoes é declarado integer e
chega como "21". Programe para o comportamento, não para o
spec: converta explicitamente e não confie em coerção automática do seu
desserializador.
| Convenção | Como tratar |
|---|---|
| Erro de negócio vem com HTTP 200 — menos onde não vem | Nas consultas, sim: não use o status para detectar recusa, verifique a presença do bloco Messages. Nos endpoints de escrita, o status varia por rota — ver a tabela de códigos por endpoint. A regra que funciona em todos: havendo Messages, houve recusa, seja qual for o status. |
| Resposta vazia é 204 | /veiculo/PIs sem resultado devolve 204 com corpo vazio. Não tente desserializar. |
| A forma muda com o conteúdo | Campo vazio é omitido. Em 17 PIs reais, cliente veio em 15 e representante em nenhuma. Trate todo campo como opcional. |
| Envelope condicional | Havendo só um objeto de saída, ele vem cru, sem envelope; havendo mais de um, vem nomeado. Aceite as duas formas. |
| Não aplique Trim | Campos de largura fixa preservam espaço à esquerda: "codigo":" 7159". Compare aparando dos dois lados, mas não reenvie aparado. |
Nunca null | Ausência é string vazia. Data vazia é "0000-00-00" — inválida em quase toda biblioteca. Trate como “sem valor” antes de converter. |
| Data e hora com espaço | yyyy-MM-dd HH:mm:ss, espaço e não T. Não é ISO 8601. |
| Barra escapada | / aparece como \/. É JSON válido; só não compare string bruta. |
| UTF-8 direto | Sem sequências \uXXXX. Leia como UTF-8, nunca Latin-1. |
| A resposta vem comprimida | O serviço responde content-encoding: br (Brotli). Quase todo cliente descomprime sozinho; se o seu não descomprime, mande Accept-Encoding: identity e o serviço honra. |
| 204 traz headers de conteúdo | A resposta vazia vem com content-type e content-encoding mesmo sem corpo. Não deduza que há conteúdo a partir deles. |
| Inteiro vem cru; monetário vem string | No mesmo objeto: "duracao":0 é número e "custo_unitario":"914.00" é string. Não presuma um tipo só para a linha toda. |
| Formatação varia por rota | cnpj sem pontuação em /veiculo/PIs, com pontuação em /agencia/PIs. Normalize antes de comparar. |
Barra final devolve 404
O caminho não diferencia maiúscula de minúscula — /VEICULO/PIS funciona — mas
/veiculo/PIs/, com barra no fim, devolve 404. Se o seu cliente
HTTP ou proxy normaliza URL acrescentando barra, desligue isso para estas rotas.
No mesmo espírito: parâmetro de query repetido faz vencer o último, e parâmetro desconhecido é ignorado em silêncio — um nome digitado errado não gera erro, só deixa de filtrar.
Dois formatos, um por geração de API.
Chega com 200. Cada item traz Id, Type e
Description.
{"VeiculoInfos": {"codigo":"","nome":"","tipo":"", /* todos os campos, vazios */
"status":false, "logotipo":""},
"Messages": [{"Id":"VEICULO_NAO_EXISTE","Type":0,
"Description":"Não foi possível encontrar o codigo externo do veiculo."}]}
Não é uniforme, e programar supondo uniformidade quebra. Os valores abaixo foram medidos chamada a chamada contra o serviço, não lidos do spec.
| Endpoint | HTTP | Quando |
|---|---|---|
| GET /veiculo/* | 200 | recusa vem no corpo, em Messages; sem resultado é 204 |
| AlterarStatusPI | 200 | inclusive nas recusas |
| CheckingParcial · CheckingFinal | 400 | toda recusa de negócio |
| Programacao | 422 | código de veículo desconhecido |
| EnvioPIPDF | 422 | campo obrigatório vazio |
| EnvioPIPDF | 500 | base64 que não decodifica (BASE64RENDER) |
| ResponderEmail | 404 | PI inexistente, ou PI que não veio de e-mail — sem corpo de mensagem |
todos com PIGUID | 500 | ver abaixo |
PIGUID malformado devolve 500, não 400
O valor é desserializado antes de qualquer regra de negócio, e o que não é um UUID derruba essa camada. Quatro entradas ruins, duas respostas diferentes:
| Você envia | HTTP | Corpo |
|---|---|---|
| campo ausente do JSON | 400 | CODIGO_NAO_INFORMADO |
| "00000000-0000-0000-0000-000000000000" | 400 | CODIGO_NAO_INFORMADO |
| "" | 500 | {"error":{"code":"500",…}} |
| qualquer texto que não seja UUID | 500 | {"error":{"code":"500",…}} |
A linha que pega gente de surpresa é a terceira: string vazia não é o mesmo que
campo ausente. Se o seu código monta o corpo com um valor padrão ""
quando não tem a PI, você recebe 500 e vai procurar um problema no servidor.
Omita o campo, ou não faça a chamada.
DataHora se comporta ao contrário: texto ilegível — ou vazio — vira recusa de
negócio DATAVAZIA com 400, e não 500.
| Código | HTTP | Significa | Retentar? |
|---|---|---|---|
| CHAVE_INVALIDA | 401 | chave ausente, errada ou revogada | não |
| CHAVE_SEM_AGENCIA | 403 | a chave não está vinculada a uma agência | não |
| EXTERNAL_ID_EM_USO | 409 | o id de campanha pertence a outra agência | não |
| CAMPANHAIDEXT | 422 | a campanha referenciada não existe | depois de criá-la |
| VEICULO_NAO_ENCONTRADO | 422 | nome, cidade ou UF não casaram | depois de corrigir |
| FILEDOWN · STORAGE · WATER | 503 | falha ao baixar ou guardar o anexo | sim — 1, 5 e 15 min |
| — | 500 | falha inesperada | sim — com backoff |
| Assunto | Situação |
|---|---|
| Paginação | não há E não há como recortar por data, status ou agência — esses filtros não funcionam. O acesso prático é uma PI por vez, por Numero. |
| Webhook ou push | não há O fluxo é pull. Programe consulta periódica; o intervalo depende do seu volume. |
| Anexos na resposta | Vêm sempre em base64 e não podem ser desligados. Dimensione buffer e timeout com folga. |
| URLs de arquivo | Pré-assinadas e de vida curta. Baixe ao receber; não guarde a URL nem a use como identificador. |
| Limite de requisições | O endpoint de autenticação é limitado por IP. Reaproveite o token. |
Antes de ir para produção
Rode em homologação os desfechos que não são sucesso: reenvie a mesma PI e confirme que
200 sem_alteracao é tratado como sucesso; referencie uma campanha inexistente e
confirme que você não retenta em laço; consulte um veículo sem PI e confirme que o seu
cliente aceita 204 sem corpo; e receba uma PI com codigoPrograma
vazio e confirme que ela vai para conferência, não para o ar.
Se você registra veiculação, confira mais três, porque são erros que não
dão sintoma: mande duas veiculações num só CheckingFinal e confirme no
relatório que as duas ficaram registradas — se só uma ficou, quebre em chamadas
separadas; mande CheckingCollection: [] e confirme que a PI foi para
Veiculada; e mande a mesma veiculação duas vezes e confirme que não duplicou.
Se você envia a grade, mande uma SIGLA que você sabe que o
SpotHub não conhece, com "Excluir": true, e confirme se o programa apareceu —
ele aparece, ativo.