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).
Quando a rede confirma tarde

Um mesmo content_id pode receber content.published ou content.partial depois de content.failed/content.partial quando a rede confirma tarde. O evento traz corrected: true e previous_status com o status anterior. Valide pelo evento mais recente.

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