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”.
| Escopo | Permite |
|---|---|
| data:read | Ler métricas, campanhas, produtos, modelo de dados, metas e orçamentos |
| clients:read | Ver os clientes |
| clients:write | Criar, alterar e arquivar clientes |
| integrations:read | Ver as integrações e o estado das sincronizações |
| integrations:write | Conectar, alterar e sincronizar integrações |
| dashboards:read | Ver os dashboards |
| dashboards:write | Criar e alterar dashboards |
| reports:read | Ver os modelos de relatório |
| reports:write | Criar e alterar modelos de relatório |
| alerts:read | Ver as regras de alerta e os destinatários |
| alerts:write | Criar e alterar regras de alerta e destinatários |
| shares:read | Ver os links de compartilhamento |
| shares:write | Criar, pausar e excluir links de compartilhamento |
| users:read | Ver a equipe e os convites |
| users:write | Convidar, alterar e remover pessoas da equipe |
| exports:read | Ver e baixar as exportações dos dados |
| exports:write | Pedir exportações dos dados |
| webhooks:read | Ver os webhooks e as entregas |
| webhooks:write | Criar, 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 })eUtilities.parseCsv, num acionador por tempo (oIMPORTDATAnã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 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ídaintegration.sync_failed— Sincronização com erroalert.triggered— Alerta disparadoreport.sent— Relatório enviadoexport.ready— Exportação prontaping— 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.