PostWharf
Agende e publique posts no Instagram, TikTok, X, LinkedIn, YouTube, Facebook, Threads, Pinterest, Bluesky e Telegram a partir de qualquer cliente MCP, com comprovante de entrega por rede.
Servidor MCP hospedado
npx add-mcp 'https://mcp.postwharf.com/mcp'Instala no Claude Code, Codex, Cursor e outros
Documentação
Autenticação
Toda requisição carrega uma chave de workspace como token bearer. As chaves são limitadas a um único workspace, então uma chave de uma marca nunca pode publicar em outra. Crie e revogue-as em Configurações → Chaves de API.
# every request
curl https://api.postwharf.com/v1/channels \
-H "Authorization: Bearer wf_live_..."
Nunca envie uma chave para um navegador. As chaves publicam em seu nome. Mantenha-as no lado do servidor, em uma variável de ambiente, e rotacione imediatamente se uma vazar.
API REST
URL base https://api.postwharf.com/v1. JSON entra, JSON sai.
GET/platforms
GET/channels
GET/connector-requests
POST/connector-requests
POST/media/upload-url
POST/media
GET/media
PATCH/media/:id
POST/posts
GET/posts
GET/posts/:id
PATCH/posts/:id
DELETE/posts/:id
GET/analytics
GET/events
Toda rota, com seus parâmetros e recusas, está na referência da API.
Quatro conectores são somente sob solicitação. Reddit, Discord, Snapchat e WordPress permanecem visíveis, mas ainda não podem criar canais ou publicar. POST /connector-requests registra a demanda do workspace uma vez por plataforma.
Criar uma publicação
Defina qualquer conjunto de canais como alvo. Forneça publish_at para agendar, ou omita-o para publicar imediatamente. As substituições por canal permitem que um único payload carregue uma legenda diferente para cada rede.
POST /v1/posts
{
"text": "Most founders think they have a marketing problem.",
"media": ["med_8f2a"],
"channels": ["ch_x_kevin", "ch_li_rm", "ch_ig_hs"],
"publish_at": "2026-08-18T09:00:00+04:00",
"overrides": {
"ch_x_kevin": { "text": "Short version for X →" }
}
}
Resposta
{
"id": "post_31ba",
"status": "scheduled",
"deliveries": [
{ "channel": "ch_x_kevin", "status": "pending" },
{ "channel": "ch_li_rm", "status": "pending" }
]
}
Consulte GET /posts/:id ou assine um webhook. Uma vez publicada, cada entrega ganha um url apontando para a publicação na rede, e um url_state explicando o motivo quando não houver: none (nada foi publicado naquele trecho), pending (foi publicada e o endereço ainda não é conhecido, então vale a pena perguntar novamente), ou not_reported (foi publicada e essa rede não retorna nenhum endereço).
O array media é ordenado. Os arquivos são publicados na ordem em que você os lista, não na ordem em que foram enviados, então um carrossel chega da forma como você o montou. Enviar media em PATCH /posts/:id substitui a lista inteira, que também é como você reordena uma.
Pergunte o que cada rede aceita
GET /platforms retorna as regras como dados: o limite de texto para cada rede, o limite menor que se aplica quando um arquivo é anexado, quantas imagens e vídeos uma publicação pode carregar, os formatos que ela publica e se esta implantação publica nela. Cada número é aquele contra o qual as recusas são escritas, então não há nada a descobrir ao ter uma publicação rejeitada.
GET /v1/platforms
{
"platforms": [
{ "platform": "x", "text_limit": 280, "caption_limit": 280,
"media": { "images": 4, "videos": 1, "mixed": false },
"post_types": ["feed", "long_form"], "publishes": "live", "measures": true }
],
"upload": { "available": true, "max_image_bytes": 26214400, "max_video_bytes": 536870912 }
}
Verifique uma publicação antes de criá-la
Envie dry_run: true e a chamada executa todas as verificações que a chamada real executa e depois não grava nada. Uma publicação que seria recusada é recusada de forma idêntica, com o mesmo corpo — então um lote pode ser validado antes de o primeiro item ser enfileirado, em vez de ser descoberto no meio do caminho. Uma execução de teste não gasta uma chave de idempotência e não conta contra suas publicações mensais.
Arquivos que você ainda não registrou podem ser descritos com media_preview, então um ensaio não custa upload. Isso é aceito apenas em uma execução de teste.
POST /v1/posts
{
"text": "Most founders think they have a marketing problem.",
"channels": ["ch_x_kevin", "ch_ig_hs"],
"media_preview": [{ "kind": "video" }],
"dry_run": true
}
Executando a mesma chamada duas vezes
Envie um cabeçalho Idempotency-Key e uma repetição da mesma chamada retorna a publicação que a primeira criou, com um 200 em vez de um 201. Um lote que falha no meio pode ser reexecutado por inteiro: as chamadas que já foram concluídas não custam nada e não criam nada.
POST /v1/posts
Idempotency-Key: sept-campaign-day-03-x
Salvar um rascunho
Envie draft: true e a publicação é mantida sem ser enviada. Um rascunho é a única publicação que pode ainda não ter canais, porque as pessoas escrevem primeiro e escolhem as redes depois. Ele não conta contra suas publicações mensais até ser publicado.
POST /v1/posts
{
"text": "Most founders think they have a marketing problem.",
"draft": true
}
Leia a gaveta de volta com GET /posts?status=draft. Um status não reconhecido é um 422, não uma lista vazia, então um erro de digitação não pode ser lido como "sem rascunhos".
Editar uma publicação
PATCH /posts/:id altera um rascunho, ou uma publicação que ainda não foi publicada. Todo campo é opcional e omitir um significa deixá-lo como está, então mover um horário não pode apagar o texto por omissão. O que é verificado é a publicação mesclada, não o patch: adicionar um canal reverifica o texto contra o limite daquela rede, e mover o horário reverifica contra o relógio.
PATCH /v1/posts/post_31ba
{
"publish_at": "2026-08-19T09:00:00+04:00",
"channels": ["ch_x_kevin", "ch_bs_kevin"]
}
Definir draft: false é como um rascunho é liberado para a fila; draft: true puxa uma publicação agendada de volta para a gaveta. Uma vez que uma publicação começou a ser publicada, ela está finalizada, e a chamada é recusada com 409 post_not_editable em vez de ser aplicada parcialmente. post_type é o único campo que não pode ser alterado: uma publicação mantém o formato com o qual foi criada, e transformar silenciosamente um Story em uma publicação de feed é a substituição que esta API existe para impedir.
Enviando um arquivo
Você não precisa hospedar nada. POST /media/upload-url recebe um tipo de conteúdo e uma contagem de bytes e retorna um endereço PUT pré-assinado mais o endereço público que o arquivo terá. Envie os bytes diretamente para ele — eles nunca passam pela API — e a linha de mídia volta na mesma resposta, pronta para ser nomeada em uma publicação.
POST /v1/media/upload-url
{ "content_type": "video/mp4", "bytes": 18234112 }
{
"upload_url": "https://…",
"public_url": "https://media.postwharf.com/…",
"media": { "id": "med_8f2a" }
}
Ambos os cabeçalhos com os quais o endereço pré-assinado foi assinado — Content-Type e Content-Length — precisam ser enviados no PUT, e o comprimento precisa corresponder à contagem de bytes que você solicitou.
POST /media é a outra metade: um arquivo que você já hospeda, registrado por endereço. Ele não busca o endereço por padrão. Passe verify: true para que seja verificado uma vez, ali mesmo, para que um link expirado seja recusado enquanto você ainda o segura, em vez de quatro semanas depois como uma publicação agendada sem nada por trás.
Descrevendo uma imagem
POST /media e POST /media/upload-url ambos aceitam um alt opcional: uma descrição do arquivo, de até 2.000 caracteres. Uma descrição com apenas espaços em branco é armazenada como inexistente. GET /media a retorna. Redes que carregam uma descrição por imagem a publicam — no Bluesky ela se torna o texto alternativo da imagem.
POST /v1/media
{
"url": "https://cdn.example.com/desk.jpg",
"kind": "image",
"alt": "Two people at a standing desk, mid-conversation."
}
Para adicionar ou alterar uma descrição depois, PATCH /media/:id com alt. Envie null para remover a descrição. O campo é obrigatório nesta chamada — um corpo sem alt é um 422 alt_required em vez de uma operação nula silenciosa.
PATCH /v1/media/med_8f2a
{ "alt": "Two people at a standing desk, mid-conversation." }
Servidor MCP
O conector expõe seus canais a qualquer cliente MCP como ferramentas. Uma chave de workspace limita cada chamada a um workspace, então um agente só pode agir na marca que você entregou a ele — e se você opera várias marcas, isso significa uma conexão por marca, cada uma com sua própria chave.
Aponte um cliente hospedado para https://mcp.postwharf.com/mcp com sua chave de workspace.
Como cada cliente se conecta, o que cada ferramenta faz e o que cada rede aceita estão descritos na página do servidor MCP de mídia social.
O caminho mais rápido é pelo painel. Agente e MCP oferece um bloco com sua chave já incluída: copie-a, cole-a no seu agente como uma mensagem, e o agente se configura sozinho. O servidor então diz ao agente como enviar um arquivo, publicar um vídeo e ajustar um conjunto de imagens em cada rede, então você pede em palavras simples e ele faz o trabalho sozinho.
get_workspace Qual marca esta chave alcança, seu plano e o fuso horário em que agenda
list_channels Cada canal conectado e a saúde de seu token
describe_networks O que cada rede aceita: formatos, contagens de arquivos, limites de texto, suas próprias configurações
connect_channel Anexar um canal ao workspace
stage_upload Uma URL de upload e um endereço público para um arquivo que você acabou de criar
upload_media Registrar uma imagem ou vídeo que você já hospeda, obter um id de mídia
describe_media Escrever ou substituir o texto alternativo em um arquivo já registrado
create_post Publicar em um ou vários canais
schedule_post Enfileirar uma publicação para um determinado horário
schedule_many Enfileirar um lote — um mês de calendário em uma única chamada
save_draft Escrever uma publicação e deixá-la para uma pessoa aprovar
list_drafts Cada rascunho no workspace, salvo mas não enfileirado
update_post Editar um rascunho ou uma publicação enfileirada antes de ser publicada
publish_draft Enviar um rascunho, ou enfileirá-lo para um horário
get_status Estado de entrega, link permanente, texto de erro
list_queue O que está agendado e quando
cancel_post Retirar algo da fila
list_events O feed de recibos de entrega
health Se a API está acessível e em quais redes ela realmente publica
CLI
Ideal para cron, CI, um hook pós-exportação, um agente ou memória muscular. Cada comando, com as flags para agendamento, execuções de teste e tentativas, está na página da CLI de mídia social.
# authenticate once — or set POSTWHARF_API_KEY and skip this entirely
wharf auth --key wf_live_…
# what this key can do, before you queue a month of posts
wharf whoami
plan pro
posts 18 used of 1000, 982 left this month
# what each network takes, without sending a post to find out
wharf platforms
# a local file. It is uploaded for you; nothing needs hosting
wharf post "New reel" --to instagram,tiktok --media ./out/reel.mp4 --at "tue 9am"
# check it against every network first. Writes nothing, uploads nothing
wharf post "Same idea, two lengths" --to instagram,pinterest \
--text-for pinterest:"the short one" --dry-run
# re-run a batch that died halfway. The ones that landed cost nothing
wharf post "Day 3" --to x --idempotency-key sept-day03-x
# what happened this week
wharf status --since 7d --metrics
✓ 41 live ⚠ 1 retrying ✗ 0 failed
# and which of them worked
wharf analytics --days 7 --by post
# pipe a caption in
cat caption.txt | wharf post --to linkedin,x
Webhooks
Esta é a parte que todo outro agendador deixa de fora. Você é notificado quando uma publicação é publicada e quando falha, em vez de descobrir uma semana depois.
{
"event": "delivery.failed",
"post": "post_31ba",
"channel": "ch_ig_hs",
"attempt": 3,
"error": "token_expired",
"action": "reconnect_required"
}
Eventos: delivery.succeeded, delivery.failed, delivery.retrying, channel.token_expiring, channel.disconnected.
token_expiring dispara cedo. As redes expiram tokens OAuth a cada 60–90 dias. Você recebe o aviso antes de o canal ficar inativo, não depois de uma semana publicando no vazio.
Clientes de IA
C
Claude Code
Adicione o conector e seus canais se tornam ferramentas. Rascunho, enfileiramento e status funcionam todos a partir do terminal.
M
Clientes MCP
Qualquer cliente que fale Streamable HTTP MCP pode se conectar ao servidor hospedado com uma chave de workspace.
⏱
Cron e n8n
A CLI sai com código de erro diferente de zero em caso de falha, então um trabalho agendado falha de forma ruidosa em vez de silenciosa.
{ }
Seu próprio código
REST simples com um token bearer. Nenhum SDK é necessário, embora um esteja por vir.
Limites e erros
Não publicamos nenhum limite de taxa por chave. A taxa de publicação é limitada pelos limites de cada rede, não pelos nossos, e nós enfileiramos contra eles em vez de falhar. Entrar, cadastrar e redefinir senha são limitados por endereço e por conta.
| Código | Significado | O que fazer |
|---|---|---|
| 401 | Chave inválida ou revogada | Reemita em Configurações → Chaves de API |
| 403 | Chave está limitada a outro workspace | Use a chave daquele workspace |
| 409 | Canal precisa ser reconectado | Token expirado — execute o OAuth novamente |
| 422 | Conteúdo, plataforma ou conector somente sob solicitação inválido | Use o código de erro e a dica; connector_request_only significa solicite-o em vez disso |
| 429 | Limite de taxa atingido | Recue; Retry-After está definido |