⚡ AutomationsAI|Portal de Cursos →

Verificando acesso...

MÓDULO 2.5

🔗 API e webhooks

Transformar o Postiz em um motor de publicação programável: REST API, API keys, POST de posts via curl, webhooks de status e integração com n8n/Make.

6
Tópicos
35
Minutos
Intermediário
Nível
Prático
Tipo
1

🔓 Habilitar a API REST do Postiz

Por padrão, a API pública do Postiz vem desligada. Para evitar que alguém abuse do seu endpoint, é preciso ativar via flag no .env e reiniciar o stack. Depois disso, todos os endpoints ficam disponíveis em /api na mesma porta do app.

O endpoint base segue o padrão {MAIN_URL}/api. Em dev local, vira http://localhost:5000/api. Em produção atrás de domínio, https://postiz.seudominio.com/api.

# ~/postiz/.env — adicione (ou descomente)
API_LIMIT=30
NX_ADD_PLUGINS=false

# Habilitar API pública
IS_GENERAL=true
DISABLE_REGISTRATION=false

# (Opcional) restringir CORS a um domínio específico
FRONTEND_URL=http://localhost:5000
# Aplicar e reiniciar
docker compose up -d --force-recreate postiz

# Smoke test do endpoint base
curl -i http://localhost:5000/api/health
# HTTP/1.1 200 OK
# {"status":"ok"}

💡 Dica prática

Se sua instância do Postiz está em domínio público, considere colocar a rota /api atrás de rate limiting no proxy (Caddy, Traefik, Nginx). A flag API_LIMIT ajuda, mas defesa em camadas é mais segura.

Conceitos-chave

REST

Recursos expostos via HTTP, verbos padrão (GET/POST/PUT/DELETE).

Feature flag

Var de ambiente que liga/desliga funcionalidades sem rebuild.

Base URL

Prefixo comum a todos os endpoints (/api).

Rate limit

Teto de requisições por janela de tempo por chave/IP.

2

🔑 Gerar uma API key

A API do Postiz autentica via Bearer token no header Authorization. Cada chave fica vinculada a um usuário e herda os scopes dele — ou seja, só consegue publicar nas contas de redes sociais que aquele usuário conectou.

Na UI: Settings → API Keys → Create new key. Copie o valor na hora — depois que a modal fecha, o token nunca mais aparece (só o hash fica no banco).

# Exemplo de chave gerada (formato real)
pk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6

# Guarde em variável de ambiente, nunca no código
export POSTIZ_API_KEY="pk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
export POSTIZ_API_URL="http://localhost:5000/api"

# Testar a chave (lista contas conectadas)
curl -s -H "Authorization: Bearer $POSTIZ_API_KEY" \
  $POSTIZ_API_URL/integrations \
  | jq '.[].name'

✓ O que FAZER

  • Criar uma chave por integração (n8n, app próprio, script de bot).
  • Nomear cada chave com contexto: n8n-prod, app-mobile.
  • Guardar em secret manager (1Password, Vault, env do orquestrador).
  • Regenerar imediatamente se houver suspeita de vazamento.

✗ O que NÃO fazer

  • Compartilhar uma chave única entre múltiplos sistemas.
  • Commitar a chave no git — mesmo em repo privado.
  • Mandar a chave pra frontend público (vaza no DevTools).
  • Deixar chave de teste ativa após o desenvolvimento.

Conceitos-chave

Bearer token

Token portador enviado no header Authorization.

Scope

Conjunto de permissões herdadas do usuário dono da chave.

Hash one-way

O banco guarda só o hash; o segredo não pode ser recuperado.

Rotação

Trocar a chave periodicamente reduz impacto de vazamento.

3

📤 POST /posts via curl

O endpoint POST /api/posts é o coração da API. Você manda um JSON descrevendo o conteúdo, em quais integrações publicar e quando. O Postiz responde com o postId e enfileira o trabalho no BullMQ.

Os três campos obrigatórios são type (draft, schedule ou now), date (ISO 8601 em UTC) e posts — array com um item por integração.

# Body JSON: agendar um post no LinkedIn e no X
cat > payload.json <<'EOF'
{
  "type": "schedule",
  "date": "2026-06-01T14:30:00.000Z",
  "shortLink": false,
  "tags": ["lançamento", "produto"],
  "posts": [
    {
      "integration": { "id": "ckxlinkedin123" },
      "value": [
        {
          "content": "Lançamos uma feature nova: agendamento em massa via API. Quem usa Postiz, vai amar.",
          "image": []
        }
      ],
      "group": null,
      "settings": {}
    },
    {
      "integration": { "id": "ckxtwitter456" },
      "value": [
        { "content": "Postiz agora aceita agendamento via API REST. 🚀", "image": [] },
        { "content": "Detalhes no thread abaixo 👇", "image": [] }
      ],
      "group": null,
      "settings": {}
    }
  ]
}
EOF
# Disparar o POST
curl -X POST "$POSTIZ_API_URL/posts" \
  -H "Authorization: Bearer $POSTIZ_API_KEY" \
  -H "Content-Type: application/json" \
  -d @payload.json

# Resposta esperada (201 Created)
# {
#   "postId": "ckxpost789abc",
#   "scheduledFor": "2026-06-01T14:30:00.000Z",
#   "status": "QUEUED",
#   "integrations": ["ckxlinkedin123", "ckxtwitter456"]
# }

Dica prática

Para descobrir os integration.id certos, chame GET /api/integrations uma vez e cache os IDs no seu lado. Não dá pra usar o nome da rede — o Postiz precisa do ID da conta específica conectada.

Conceitos-chave

Idempotency

Re-enviar o mesmo POST não deve duplicar — use tags únicas.

ISO 8601

Formato padrão de data em UTC: YYYY-MM-DDTHH:mm:ssZ.

Job queue

O 201 só significa "enfileirado", não "publicado".

Multi-rede

Um POST pode fanout para várias integrações simultaneamente.

4

🔔 Webhooks de status

Como o POST inicial só enfileira o trabalho, o jeito limpo de saber o resultado é receber um webhook. O Postiz dispara um POST para a URL que você cadastrar sempre que um post muda de estado — PUBLISHED, FAILED ou DELETED.

Cadastre o callback em Settings → Webhooks → Add URL. Cada webhook tem um secret usado para assinar o payload no header X-Postiz-Signature — valide isso no seu lado pra confirmar que a requisição veio mesmo do Postiz.

# Payload que o Postiz envia para sua URL
{
  "event": "post.published",
  "timestamp": "2026-06-01T14:30:12.482Z",
  "data": {
    "postId": "ckxpost789abc",
    "status": "PUBLISHED",
    "integration": {
      "id": "ckxlinkedin123",
      "provider": "linkedin",
      "name": "Minha Página LinkedIn"
    },
    "publishedAt": "2026-06-01T14:30:12.000Z",
    "remoteId": "urn:li:share:7203451829",
    "remoteUrl": "https://www.linkedin.com/feed/update/urn:li:share:7203451829/",
    "tags": ["lançamento", "produto"]
  }
}
# Payload de falha (mesma estrutura, status diferente)
{
  "event": "post.failed",
  "timestamp": "2026-06-01T14:30:09.117Z",
  "data": {
    "postId": "ckxpost789abc",
    "status": "FAILED",
    "integration": { "id": "ckxtwitter456", "provider": "x" },
    "error": {
      "code": "RATE_LIMITED",
      "message": "X API: 429 too many requests; retry after 900s"
    },
    "willRetry": true,
    "retryAt": "2026-06-01T14:45:09.000Z"
  }
}

📡 Eventos suportados

  • post.queued: trabalho aceito e na fila — útil para auditoria.
  • post.published: publicação concluída — guarde o remoteUrl para deep-link.
  • post.failed: erro na API da rede; inspecione error.code para alertar.
  • post.deleted: alguém removeu via UI ou API — sincronize seu lado.

🛡️ Dica prática

Responda 200 OK rápido (<2s) e processe o evento em fila do seu lado. Se demorar, o Postiz reenvia — e você processa o mesmo evento duas vezes. Use o postId + event como chave de deduplicação.

Conceitos-chave

Callback

URL do seu sistema que o Postiz chama assincronamente.

Signature

HMAC do payload assinado com o secret do webhook.

At-least-once

Garantia de entrega — pode chegar duas vezes; deduplicar no consumidor.

Retry

Postiz reenvia em backoff se você não responder 2xx.

5

🔗 Integração com n8n e Make

Tanto n8n quanto Make.com têm nodes nativos do Postiz (community node no n8n; módulo oficial no Make). Eles encapsulam a autenticação Bearer e expõem ações como Create Post, List Integrations, Delete Post. Se preferir, use o HTTP Request node genérico — funciona igual.

Para receber webhooks no n8n, use o Webhook node como trigger e cole a URL gerada no painel de webhooks do Postiz. No Make, use o módulo Custom Webhook.

# Exemplo: n8n HTTP Request node configurado manualmente
{
  "method": "POST",
  "url": "={{ $env.POSTIZ_API_URL }}/posts",
  "authentication": "genericCredentialType",
  "genericAuthType": "httpHeaderAuth",
  "sendHeaders": true,
  "headerParameters": {
    "parameters": [
      { "name": "Authorization", "value": "Bearer {{ $env.POSTIZ_API_KEY }}" },
      { "name": "Content-Type", "value": "application/json" }
    ]
  },
  "sendBody": true,
  "bodyContentType": "json",
  "jsonBody": "={{ JSON.stringify($json.payload) }}"
}

🔄 Fluxo completo no n8n

  • 1. Trigger — Schedule (diário 09:00), Webhook (form externo) ou Postgres trigger (novo registro).
  • 2. Preparar conteúdo — node de IA (OpenAI/Claude) gera texto + imagem com base no input.
  • 3. Montar payload — Function node monta o JSON com posts[] e datas.
  • 4. POST Postiz — HTTP Request node ou Postiz node enfileira o post.
  • 5. Webhook de retorno — outro workflow escuta post.published e atualiza um Google Sheet ou Notion.

🧠 Dica prática

No n8n self-hosted, suba o Postiz e o n8n na mesma docker network. Aí o webhook do Postiz pode apontar para http://n8n:5678/webhook/... sem precisar de domínio público — mais rápido e mais seguro.

Conceitos-chave

Community node

Plugin de terceiros que adiciona suporte a uma API ao n8n.

HTTP genérico

Fallback que sempre funciona — qualquer API REST.

Trigger

Primeiro node de um workflow; inicia a execução.

Function node

JavaScript inline para transformar dados entre passos.

6

🎯 Fluxo: app próprio → API Postiz → redes

O grande motivo de usar a API do Postiz em vez de integrar direto com cada rede é não reinventar OAuth, renovação de tokens, rate limit e idempotência N vezes. Seu app fala com uma API consistente; o Postiz cuida do resto.

# Arquitetura recomendada
[ Seu App ]
     |
     | (1) POST /api/posts  — Bearer token
     v
[ Postiz API ]
     |
     | (2) enfileira no Redis (BullMQ)
     v
[ Worker Postiz ]
     |
     | (3) chama API de cada rede (OAuth gerenciado pelo Postiz)
     v
[ LinkedIn ] [ X ] [ Instagram ] [ Bluesky ] ...
     |
     | (4) sucesso/erro
     v
[ Postiz ]
     |
     | (5) POST webhook  → https://seuapp.com/webhooks/postiz
     v
[ Seu App ]  (atualiza DB, dispara notificação ao usuário)

✓ Vantagens vs integrar direto

  • Uma única autenticação (Bearer) em vez de OAuth por rede.
  • Refresh de tokens, retry e rate limit já implementados.
  • Agendamento e fila prontos — não precisa de cron/BullMQ próprio.
  • Quando o Postiz adiciona uma rede nova, você ganha de graça.

✗ Quando NÃO usar a API

  • Você precisa de uma feature exótica que a rede X tem mas Postiz não expõe.
  • Volume gigantesco (milhões/dia) — pode bater no rate limit interno.
  • Latência crítica em tempo real — passa por fila, não é instantâneo.
  • Você já tem integração direta madura e estável.

🚀 Dica prática

Pense no Postiz como um broker de publicação: seu app trata regra de negócio (quem pode postar o quê, quando, com qual aprovação) e o Postiz trata o last mile técnico de chegar nas redes. Essa separação é o que torna a arquitetura escalável.

Conceitos-chave

Broker

Camada intermediária que isola producers de consumers.

Fanout

Uma entrada → várias saídas (uma por rede social).

Decoupling

Seu app não conhece detalhes de cada API de rede.

Eventual consistency

Publicação é assíncrona; estado final chega via webhook.

🎯 Resumo do Módulo

API habilitada — flag IS_GENERAL=true no .env, endpoint base em /api.
API key gerada — uma chave por integração, guardada em secret manager, autenticando via Bearer token.
POST /posts dominado — payload JSON com type, date e array posts multi-rede.
Webhooks configurados — eventos published/failed com assinatura HMAC e deduplicação.
n8n/Make integrado — node nativo ou HTTP Request genérico, com webhook node escutando retornos.
Arquitetura clara — Postiz como broker que isola seu app do last mile de cada rede social.

Trilha concluída! 🎉

Você dominou o Postiz: do Docker ao agendamento, das contas conectadas à API programática. A próxima trilha foca em redes sociais — algoritmos, formatos, melhores horários, estratégia de conteúdo por plataforma.