Para desenvolvedores

API e webhooks do Posttai

O mesmo token que conecta o Claude (MCP) também abre a API REST. Crie posts, agende, suba mídias e receba avisos assinados quando algo acontecer.

Autenticação

Gere um token em Configurações → Tokens e MCP (planos Profissional e Agência). Ele começa com ptt_, pertence a uma marca e aparece uma vez só. Mande em todo pedido no cabeçalho Authorization:

curl https://api.posttai.com/tokens-api/atual \
  -H "Authorization: Bearer ptt_seu_token"
  • O token tem papel de editor: cria, edita, agenda e cancela posts; não aprova, não mexe no time, nos tokens, nos webhooks nem no plano.
  • Se quem criou o token sair do time, ou se o plano deixar de incluir a API, o token para na hora (401/403).
  • Horários em ISO 8601. Mande com fuso (2026-10-09T18:30:00-03:00); o Posttai trabalha no horário de Brasília.

Respostas, erros e limites

Toda resposta vem no mesmo envelope. Em erro, body traz a mensagem pronta, em português.

{ "status": "ok", "statusCode": 200, "body": { ... } }
{ "status": "error", "statusCode": 422, "body": "Escolha pelo menos um canal." }
  • 401 token inválido ou revogado · 403 sem permissão ou fora do plano · 404 não existe nesta marca · 409 conflito de estado · 422 dado inválido · 429 limite.
  • Limite geral: 300 pedidos por minuto por IP. IA tem limite próprio por pessoa e por marca.

Canais

Contas conectadas da marca. Conectar uma conta nova é feito no app (ou pelo link “Pedir para o cliente conectar”).

GET/canais

Lista as contas conectadas: _id, rede, nome, usuario e status (ativo, expirado, erro).

curl https://api.posttai.com/canais -H "Authorization: Bearer $POSTTAI_TOKEN"
GET/canais/redes

Redes disponíveis, com limites de texto e mídia de cada uma.

curl https://api.posttai.com/canais/redes -H "Authorization: Bearer $POSTTAI_TOKEN"
GET/grupos-canais

Grupos de canais (ex.: “Cliente X”), para publicar em vários de uma vez.

curl https://api.posttai.com/grupos-canais -H "Authorization: Bearer $POSTTAI_TOKEN"

Posts

Um post tem texto base, canais, mídias e, se quiser, um texto próprio por canal (variacoes).

GET/posts?status=agendado&de=…&ate=…

Lista posts. status aceita vários separados por vírgula; de/ate filtram pela data agendada.

curl "https://api.posttai.com/posts?status=agendado,publicado&de=2026-10-01&ate=2026-10-31" -H "Authorization: Bearer $POSTTAI_TOKEN"
GET/posts/:id

Um post com publicações por rede (link e erro de cada uma) e histórico.

curl https://api.posttai.com/posts/ID_DO_POST -H "Authorization: Bearer $POSTTAI_TOKEN"
POST/posts

Cria um rascunho. canais e midias são ids; variacoes é opcional.

curl -X POST https://api.posttai.com/posts -H "Authorization: Bearer $POSTTAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"conteudo":"Novidade na loja!","canais":["ID_DO_CANAL"],"midias":[]}'
PUT/posts/:id

Altera texto, canais, mídias, variações ou data (os mesmos campos do POST).

curl -X PUT https://api.posttai.com/posts/ID_DO_POST -H "Authorization: Bearer $POSTTAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"conteudo":"Texto novo"}'
POST/posts/:id/agendar

Agenda. Se a marca exige aprovação, o post vai para a fila de aprovação em vez de agendar.

curl -X POST https://api.posttai.com/posts/ID_DO_POST/agendar -H "Authorization: Bearer $POSTTAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"agendadoPara":"2026-10-09T18:30:00-03:00"}'
POST/posts/:id/publicar-agora

Manda para a fila de publicação agora.

curl -X POST https://api.posttai.com/posts/ID_DO_POST/publicar-agora -H "Authorization: Bearer $POSTTAI_TOKEN"
POST/posts/:id/cancelar

Tira um post agendado da fila (volta a rascunho).

curl -X POST https://api.posttai.com/posts/ID_DO_POST/cancelar -H "Authorization: Bearer $POSTTAI_TOKEN"
DELETE/posts/:id

Exclui ideia, rascunho, cancelado ou com erro.

curl -X DELETE https://api.posttai.com/posts/ID_DO_POST -H "Authorization: Bearer $POSTTAI_TOKEN"
GET/calendario?de=…&ate=…

Posts do período em todos os status do calendário.

curl "https://api.posttai.com/calendario?de=2026-10-01&ate=2026-10-31" -H "Authorization: Bearer $POSTTAI_TOKEN"
GET/sugestoes/horarios?rede=instagram&dias=7

Horários sugeridos e livres nos próximos dias.

curl "https://api.posttai.com/sugestoes/horarios?rede=instagram&dias=7" -H "Authorization: Bearer $POSTTAI_TOKEN"

Mídias

POST/midias/url

Traz uma imagem ou vídeo de um endereço público e devolve o _id para usar em midias.

curl -X POST https://api.posttai.com/midias/url -H "Authorization: Bearer $POSTTAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://exemplo.com/foto.jpg"}'
POST/midias

Envia um arquivo (multipart, campo arquivo, até 100 MB).

curl -X POST https://api.posttai.com/midias -H "Authorization: Bearer $POSTTAI_TOKEN" -F "[email protected]"
GET/midias

Biblioteca de mídias da marca.

curl https://api.posttai.com/midias -H "Authorization: Bearer $POSTTAI_TOKEN"

Fluxo e comentários

GET/fluxo

Etapas do quadro da marca.

curl https://api.posttai.com/fluxo -H "Authorization: Bearer $POSTTAI_TOKEN"
POST/posts/:id/etapa

Move o post de etapa (com responsável e prazo opcionais).

curl -X POST https://api.posttai.com/posts/ID_DO_POST/etapa -H "Authorization: Bearer $POSTTAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"etapa":"revisao"}'
GET/posts/:id/comentarios

Comentários da equipe no post.

curl https://api.posttai.com/posts/ID_DO_POST/comentarios -H "Authorization: Bearer $POSTTAI_TOKEN"
POST/posts/:id/comentarios

Comenta no post.

curl -X POST https://api.posttai.com/posts/ID_DO_POST/comentarios -H "Authorization: Bearer $POSTTAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"texto":"Pronto para revisar"}'

Webhooks

Em Configurações → Webhooks, dono e admin cadastram até 5 URLs por marca e escolhem os eventos. Cada envio é um POST com JSON, saindo do nosso worker (nunca trava o app).

  • post.publicado o post saiu em todas as redes (ou em parte delas, com status: "parcial").
  • post.erro uma ou mais redes recusaram de vez.
  • post.aprovado aprovado pela equipe ou pelo cliente (dados.por).
  • post.reprovado reprovado pela equipe ou ajuste pedido pelo cliente, com dados.motivo.
  • canal.desconectado a conta saiu, expirou ou teve o acesso removido na rede (dados.motivo).
  • webhook.teste só no botão “Testar”.
POST https://seu-sistema.com/webhooks/posttai
Content-Type: application/json
X-Posttai-Evento: post.publicado
X-Posttai-Entrega: 6702f0c1a9e4b1d2c3e4f5a6
X-Posttai-Assinatura: t=1791403800,v1=5f0c…e9

{
  "id": "6702f0c1a9e4b1d2c3e4f5a6",
  "evento": "post.publicado",
  "criadoEm": "2026-10-09T21:30:04.120Z",
  "idMarca": "66fe…",
  "dados": {
    "post": { "id": "6701…", "status": "publicado", "trecho": "Lançamento amanhã…", "agendadoPara": "2026-10-09T21:30:00.000Z", "publicadoEm": "2026-10-09T21:30:04.000Z" },
    "publicacoes": [{ "idCanal": "66ff…", "rede": "instagram", "status": "publicado", "url": "https://www.instagram.com/p/…", "erro": null }]
  }
}
  • Entregue = resposta 2xx em até 10 s. Redirecionamento não é seguido.
  • Retentativas: 408, 429, 5xx e falha de rede tentam de novo em 30 s, 1, 2, 4 e 8 min (6 tentativas). Outros 4xx param na hora.
  • Use id (igual a X-Posttai-Entrega) para ignorar repetidos: a mesma entrega pode chegar mais de uma vez.
  • As últimas 20 entregas de cada URL, com corpo e resposta, ficam na tela do webhook por 30 dias.
  • Só https://. Endereços internos ou privados (localhost, 10/8, 172.16/12, 192.168/16, 169.254/16, ::1, fc00::/7…) são recusados, inclusive quando o nome resolve para eles no DNS.

Conferir a assinatura

O segredo (whsec_…) aparece uma vez, ao criar o webhook. A assinatura é HMAC-SHA256(segredo, t + "." + corpo cru) em hexadecimal. Confira sempre com o corpo exatamente como chegou, antes de interpretar o JSON, e recuse t com mais de 5 minutos.

import crypto from 'node:crypto'
import express from 'express'

const app = express()

// Corpo CRU: a assinatura é sobre os bytes que chegaram.
app.post('/webhooks/posttai', express.raw({ type: 'application/json' }), (req, res) => {
  const corpo = req.body.toString('utf8')
  const { t, v1 } = Object.fromEntries(String(req.get('X-Posttai-Assinatura')).split(',').map(p => p.split('=')))
  const esperado = crypto.createHmac('sha256', process.env.POSTTAI_WEBHOOK_SEGREDO).update(`${t}.${corpo}`).digest('hex')
  const ok = v1 && esperado.length === v1.length && crypto.timingSafeEqual(Buffer.from(esperado), Buffer.from(v1))
  if (!ok || Math.abs(Date.now() / 1000 - Number(t)) > 300) return res.sendStatus(401)

  const evento = JSON.parse(corpo)
  // ...trate evento.evento e evento.dados (ignore ids de entrega já vistos)
  res.sendStatus(204)
})

Monte a sua semana hoje

Crie a conta, conecte o time e deixe o calendário pronto. Quando as redes liberarem, é só o Posttai publicar.