ACOMPANHAR / Webhooks

Avisos automáticos

Quer ser avisado quando seu post foi publicado, falhou ou precisou reconectar a rede? A gente avisa seu sistema em tempo real.


Esta é uma página técnica. Se você só usa o painel do Outposted, pode pular — o painel já te mostra status em tempo real.

Pra quê isso me serve

Se você é dev (ou tem dev), webhook é como o Outposted avisa seu sistema quando algo acontece. Em vez de você ficar perguntando a cada 30s "já foi?", a gente bate na sua porta quando tem novidade.

Exemplos:

  • Agência: quando o post foi publicado, salvar a URL pública na ficha do cliente.
  • Dashboard interno: atualizar "1.247 posts publicados este mês" em tempo real.
  • Slack interno: quando um post falhou, mandar mensagem no canal de operações.
  • Aprovação editorial: quando a rede aceitou, marcar a peça como "no ar".

Os 4 eventos que a gente envia

EventoQuando dispara
content.publishedTodas as redes do post publicaram com sucesso.
content.partialAlgumas publicaram, outras falharam.
content.failedNenhuma rede publicou.
connection.expiredUma rede precisa ser reconectada (você trocou senha, etc).

Como funciona

  1. Você cria um endpoint no seu sistema — um endereço público que vai receber os avisos.
  2. Você registra esse endpoint no Outposted, dizendo quais eventos quer receber.
  3. Toda vez que algo acontece, a gente faz um POST pro seu endpoint com os detalhes.
  4. Seu sistema recebe e processa.
Por que assinatura?

Como webhook bate em endereço público, qualquer pessoa que descobrir a URL pode tentar mandar um POST falso. Pra você ter certeza que o aviso é nosso, a gente assina cada POST com uma assinatura digital. A seção pra integradores explica como verificar.

Cenário concreto

Você tem agência com 12 clientes. Cada cliente publica 30 posts/mês. Você quer atualizar dashboard interno, receber alerta no Slack quando algum post falhar, marcar peça como publicada no Notion.

Você registra um endpoint só, assina os 4 eventos. Seu backend recebe o POST, decide o que fazer. Daí pra frente, não precisa mais ficar olhando o painel. Tudo chega na sua mão.

Se seu endpoint estiver fora do ar

Acontece. Servidor reinicia, deploy, instabilidade. A gente tenta de novo várias vezes antes de desistir:

TentativaQuanto tempo depois
1imediato
2+1 minuto
3+5 minutos
4+30 minutos
5+2 horas
6+12 horas (última tentativa)

Depois de 6 tentativas, marcamos o aviso como "morto" (DLQ — dead letter queue). Você ainda pode forçar reenvio manual quando consertar (veja seção pra integradores).

A gente considera "deu certo" quando seu endpoint responde 2xx em até 30s. Status 5xx, timeout ou conexão recusada = tenta de novo. Status 4xx (exceto 408/429) = sua decisão consciente, paramos.

Setup técnico

Pra integradores.

1

Criar um endpoint

POST /api/v1/workspaces/:wsId/webhooks
curl -X POST "https://api.outposted.one/api/v1/workspaces/$WS_ID/webhooks" \
  -H "Authorization: Bearer opst_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.minhaempresa.com/outposted/webhook",
    "description": "prod backend",
    "enabled_events": [
      "content.published",
      "content.partial",
      "content.failed"
    ]
  }'
201 Created
{
  "id": "wh_abc...",
  "url": "https://api.minhaempresa.com/outposted/webhook",
  "secret": "whsec_def456...",
  "active": true,
  "enabled_events": ["content.published", "content.partial", "content.failed"]
}

O secret aparece uma vez só. Guarde em variável de ambiente (OUTPOSTED_WEBHOOK_SECRET).

2

Payload + headers de assinatura

POST /outposted/webhook
Content-Type: application/json
Outposted-Timestamp: 1735350120
Outposted-Signature: sha256=a3f...
Outposted-Webhook-Id: wh_abc...
Outposted-Delivery-Id: del_xyz...

{
  "id": "evt_...",
  "type": "content.published",
  "created_at": "2026-06-05T02:12:14Z",
  "data": {
    "content_id": "cef7e46c-...",
    "workspace_id": "e9fe5698-...",
    "jobs": [
      {
        "platform_slug": "threads",
        "status": "published",
        "external_post_url": "https://www.threads.com/..."
      }
    ]
  }
}
3

Verificar a assinatura (Node.js)

HMAC-SHA256 do payload {timestamp}.{body} usando o secret como chave. Constant-time compare contra Outposted-Signature.

import { createHmac, timingSafeEqual } from 'node:crypto';

function verifyWebhook(
  payload: string,
  signatureHeader: string,
  timestampHeader: string,
  secret: string,
  toleranceSeconds = 300,
): boolean {
  const timestamp = parseInt(timestampHeader, 10);
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - timestamp) > toleranceSeconds) return false;

  const presented = signatureHeader.replace(/^sha256=/, '');
  const computed = createHmac('sha256', secret)
    .update(`${timestamp}.${payload}`)
    .digest('hex');

  const a = Buffer.from(presented, 'hex');
  const b = Buffer.from(computed, 'hex');
  if (a.length !== b.length) return false;
  return timingSafeEqual(a, b);
}

// Express
app.post('/outposted/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const valid = verifyWebhook(
    req.body.toString(),
    req.header('outposted-signature')!,
    req.header('outposted-timestamp')!,
    process.env.OUTPOSTED_WEBHOOK_SECRET!,
  );
  if (!valid) return res.status(401).send('invalid signature');
  res.status(200).send('ok');
});
4

Via SDK

import { Outposted } from '@outposted/node';

const valid = Outposted.webhooks.verify({
  payload: rawBody,
  signature: req.header('outposted-signature')!,
  timestamp: req.header('outposted-timestamp')!,
  secret: process.env.OUTPOSTED_WEBHOOK_SECRET!,
});
Playground interativo

Teste seu payload/secret na página do playground. Roda 100% no seu browser, o secret nunca sai dele.

5

Inspecionar deliveries

curl "https://api.outposted.one/api/v1/workspaces/$WS_ID/webhooks/$WH_ID/deliveries?status=failed&limit=25" \
  -H "Authorization: Bearer opst_live_..."
6

Redelivery manual

curl -X POST "https://api.outposted.one/api/v1/workspaces/$WS_ID/deliveries/$DEL_ID/redeliver" \
  -H "Authorization: Bearer opst_live_..."
7

Endpoint público de debug

Sem auth, valida uma assinatura específica. Útil pra confirmar que sua implementação bate com a nossa antes de plugar em produção:

curl -X POST "https://api.outposted.one/api/v1/webhooks/verify" \
  -H "Content-Type: application/json" \
  -d '{
    "secret": "whsec_...",
    "signature_header": "sha256=a3f...",
    "payload": "1735350120.{\"id\":\"evt_...\"}",
    "tolerance_seconds": 300
  }'
{
  "valid": true,
  "checks": {
    "signature_format": true,
    "timestamp_within_tolerance": true,
    "hmac_matches": true
  },
  "reason": null
}

Nesta página