Ir para o conteúdo

Desenvolvedores

Documentação da API

Data API para levar as métricas ao Power BI, Excel e Google Sheets, e Platform API para automatizar clientes, relatórios, alertas e webhooks do Signal Nebula.

Visão geral

Há duas APIs, com a mesma chave e as mesmas regras. A Data API lê os dados de marketing (métricas por plataforma, campanha e produto, o modelo de dados unificado, metas e orçamentos) para painéis e planilhas. A Platform API automatiza o espaço de trabalho: clientes, equipe, integrações, dashboards, relatórios, alertas, links de compartilhamento, exportações e webhooks.

  • Data API: https://nublahub.com/api/data/v1
  • Platform API: https://nublahub.com/api/platform/v1

Tudo é JSON em UTF-8, dentro de um envelope { data, meta } (listas também com pagination). A especificação OpenAPI 3.1 de cada API está em /openapi.json no endereço base, pronta para gerar um cliente ou importar no Postman.

Autenticação

Crie uma chave em Configurações › API (dono ou administrador, num plano com API). O segredo aparece uma vez só; guarde-o num cofre de senhas ou numa variável de ambiente. Envie-o em todos os pedidos:

curl https://nublahub.com/api/data/v1/metrics \
  -H "Authorization: Bearer snk_live_…" \
  -H "X-Client-Id: 42"

Uma chave pode ficar limitada a alguns clientes. Nesse caso só vê esses; diga qual com o cabeçalho X-Client-Id ou o parâmetro ?client_id= (sem ele, vale o primeiro cliente da chave). As rotas por cliente (as da Data API, por exemplo) usam o mesmo cabeçalho com qualquer chave.

Uma chave revogada ou expirada responde 401 de imediato; a lista em Configurações › API mostra o último uso de cada uma.

Escopos

Cada chave tem escopos. write inclui read do mesmo recurso; data:read cobre toda a Data API. Para Power BI, Excel e Sheets, use o atalho “Só leitura”.

EscopoPermite
data:readLer métricas, campanhas, produtos, modelo de dados, metas e orçamentos
clients:readVer os clientes
clients:writeCriar, alterar e arquivar clientes
integrations:readVer as integrações e o estado das sincronizações
integrations:writeConectar, alterar e sincronizar integrações
dashboards:readVer os dashboards
dashboards:writeCriar e alterar dashboards
reports:readVer os modelos de relatório
reports:writeCriar e alterar modelos de relatório
alerts:readVer as regras de alerta e os destinatários
alerts:writeCriar e alterar regras de alerta e destinatários
shares:readVer os links de compartilhamento
shares:writeCriar, pausar e excluir links de compartilhamento
users:readVer a equipe e os convites
users:writeConvidar, alterar e remover pessoas da equipe
exports:readVer e baixar as exportações dos dados
exports:writePedir exportações dos dados
webhooks:readVer os webhooks e as entregas
webhooks:writeCriar, alterar e testar webhooks

Paginação e campos

As listas paginam por cursor: limit (por omissão 50, no máximo 200) e cursor, o pagination.nextCursor da página anterior. Continue enquanto pagination.hasMore for true.

{
  "data": [ … ],
  "pagination": { "limit": 50, "nextCursor": "eyJpZCI6MTIzfQ", "hasMore": true },
  "meta": { "currency": "BRL" }
}

Use fields para receber só algumas colunas por item (?fields=date,spend,clicks): respostas menores e planilhas mais limpas.

CSV: Power BI, Excel e Sheets

Qualquer lista responde em CSV com Accept: text/csv ou ?format=csv (separador vírgula, UTF-8, uma linha de cabeçalho). A paginação continua a valer: peça limit=200 e siga o cursor, ou filtre o período para caber numa página.

  • Power BI: Obter dados › Web › Avançado, com o URL e o cabeçalho Authorization = Bearer snk_live_….
  • Excel: Dados › Obter Dados › Da Web (Power Query), com o mesmo cabeçalho.
  • Google Sheets: um Apps Script com UrlFetchApp.fetch(url, { headers }) e Utilities.parseCsv, num acionador por tempo (o IMPORTDATA não envia cabeçalhos).

Datas e moeda

Datas em ISO 8601: dias como 2026-11-05 e instantes como 2026-11-05T14:30:00.000Z (UTC). Os períodos usam period (last_7, last_30, mtd…) ou period=custom com from e to (YYYY-MM-DD). Os valores de dinheiro vêm na moeda do cliente, indicada em meta.currency.

Erros

Os erros seguem o RFC 9457 (application/problem+json). O code é estável e serve para decidir no seu código; o type aponta para a explicação nesta página.

{
  "type": "https://nublahub.com/docs/api#erro-insufficient-scope",
  "title": "Forbidden",
  "status": 403,
  "code": "insufficient_scope",
  "detail": "This operation requires the scope clients:write",
  "requestId": "req_01J…"
}
401 unauthorized
Falta o cabeçalho Authorization ou ele não é um Bearer.
401 invalid_api_key
A chave não existe ou foi mal copiada.
401 api_key_revoked
A chave foi revogada em Configurações › API. Crie outra.
401 api_key_expired
A chave passou da data de expiração. Edite a data ou crie outra.
403 insufficient_scope
A chave não tem o escopo que a operação pede (o cabeçalho WWW-Authenticate diz qual).
403 plan_upgrade_required
O plano atual da organização não inclui a API.
429 rate_limited
Passou o limite de pedidos da chave. Espere os segundos de Retry-After.
422 validation_failed
O corpo ou os parâmetros não são válidos; errors[] diz o campo e a regra.
404 not_found
O recurso não existe nesta organização (ou neste cliente).
403 client_access_denied
A chave está limitada a outros clientes, ou o cliente pedido não existe.
422 idempotency_key_reused
A mesma Idempotency-Key foi usada com outro pedido. Gere uma nova.
409 idempotency_in_progress
O pedido original com essa Idempotency-Key ainda está a correr. Tente de novo daqui a pouco.
402 trial_expired
O período de teste terminou: a organização só lê até escolher um plano.
402 plan_limit
A escrita passaria o limite do plano (clientes, usuários).

Limite de pedidos

Cada chave tem uma janela de pedidos por minuto. Todas as respostas trazem X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset (segundos até a janela recomeçar). Passado o limite, a resposta é 429 com Retry-After: espere esses segundos antes de repetir.

Idempotência

Em qualquer POST envie Idempotency-Key (um UUID por operação) para poder repetir com segurança depois de um timeout: durante 24 horas, a mesma chave com o mesmo pedido devolve a resposta guardada em vez de criar de novo. Com outro corpo, a resposta é idempotency_key_reused.

Request id e ETag

Cada resposta traz X-Request-Id (envie o seu, se quiser, no mesmo cabeçalho). Inclua-o ao falar com o suporte.

Os GET devolvem um ETag. Repita o pedido com If-None-Match e, se nada mudou, a resposta é 304 sem corpo: menos dados para transferir em cada atualização.

Webhooks

Em Configurações › API › Webhooks, registe um endereço https:// e os eventos que quer receber. Cada evento chega num POST JSON:

  • integration.sync_completed — Sincronização concluída
  • integration.sync_failed — Sincronização com erro
  • alert.triggered — Alerta disparado
  • report.sent — Relatório enviado
  • export.ready — Exportação pronta
  • ping — o envio de teste (botão “Enviar teste”).
{
  "id": "evt_01J…",
  "type": "alert.triggered",
  "createdAt": "2026-11-05T14:30:00.000Z",
  "organizationId": 7,
  "data": { … }
}

Verificar a assinatura

Cada envio leva Signal-Nebula-Signature: t=<unix>,v1=<hex>, em que v1 é o HMAC-SHA256, com o segredo do endpoint, de "<t>.<corpo cru>". Recuse assinaturas com mais de 5 minutos (contra repetições) e compare em tempo constante:

import crypto from 'node:crypto'

// O corpo cru (Buffer), antes de qualquer JSON.parse: express.raw({ type: 'application/json' })
export function verifySignalNebula(rawBody, header, secret, toleranceSeconds = 300) {
  const parts = Object.fromEntries(header.split(',').map((part) => part.split('=')))
  const timestamp = Number(parts.t)
  if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${parts.t}.${rawBody}`)
    .digest('hex')
  const given = Buffer.from(parts.v1 ?? '', 'hex')
  const wanted = Buffer.from(expected, 'hex')
  return given.length === wanted.length && crypto.timingSafeEqual(given, wanted)
}

app.post('/webhooks/signal-nebula', express.raw({ type: 'application/json' }), (req, res) => {
  const ok = verifySignalNebula(req.body, req.get('Signal-Nebula-Signature') ?? '', process.env.SN_WEBHOOK_SECRET)
  if (!ok) return res.status(400).end()
  const event = JSON.parse(req.body.toString('utf8'))
  // event.type, event.data… responda 2xx depressa e trate o resto em segundo plano
  res.status(204).end()
})

Novas tentativas

Responda 2xx em até 10 segundos. Fora disso, tentamos de novo até 8 vezes, com esperas de 30 s, 2 min, 10 min, 30 min, 1 h, 3 h, 6 h. Depois de 20 entregas seguidas falhadas, o endpoint é desligado até voltar a ser ativado no painel. O histórico de entregas, com o reenvio manual, fica em Configurações › API › Webhooks › Entregas.

Referência: Data API

Endereço base: https://nublahub.com/api/data/v1

A especificação desta API não está disponível neste momento. O guia acima continua válido; tente recarregar a página daqui a alguns minutos ou leia o OpenAPI diretamente em https://nublahub.com/api/data/v1/openapi.json.

Referência: Platform API

Endereço base: https://nublahub.com/api/platform/v1

A especificação desta API não está disponível neste momento. O guia acima continua válido; tente recarregar a página daqui a alguns minutos ou leia o OpenAPI diretamente em https://nublahub.com/api/platform/v1/openapi.json.