{"openapi":"3.1.0","info":{"title":"manypost API","version":"0.0.1","description":"API do manypost — agendamento/publicação multicanal. Humanos usam sessão Clerk; máquinas usam `Authorization: Bearer mp_live_…`. Erros seguem problem+json (RFC 9457): o campo `title` carrega o código estável do erro."},"servers":[{"url":"https://manypost.malsandigital.online","description":"esta instalação"}],"tags":[{"name":"auth","description":"identidade Clerk e autorização Manypost"},{"name":"api-keys","description":"chaves de API por escopo"},{"name":"channels","description":"conexão de canais de rede social"},{"name":"posts","description":"agendar, editar, cancelar, retry, aprovação por link"},{"name":"publications","description":"feed de calendário/kanban"},{"name":"media","description":"biblioteca de mídia"},{"name":"webhooks","description":"webhooks de saída assinados"},{"name":"notifications","description":"notificações da organização"},{"name":"events","description":"stream SSE em tempo real"},{"name":"capabilities","description":"plano, features e limites da organização"},{"name":"ai","description":"geração de conteúdo e sugestão de horários (SPEC_AI)"},{"name":"billing","description":"assinatura e cobrança (só no serviço gerenciado)"},{"name":"approvals","description":"superfície pública de aprovação por token"},{"name":"public-posts","description":"API pública /public/v1 — posts (escopos posts:*)"},{"name":"public-publications","description":"API pública /public/v1 — feed de publicações (posts:read)"},{"name":"public-channels","description":"API pública /public/v1 — canais (channels:*)"},{"name":"public-media","description":"API pública /public/v1 — mídia (media:write)"},{"name":"public-webhooks","description":"API pública /public/v1 — webhooks (webhooks:manage)"}],"components":{"securitySchemes":{"clerkAuth":{"type":"http","scheme":"bearer","description":"sessão Clerk humana — Authorization: Bearer <token>"},"apiKeyAuth":{"type":"http","scheme":"bearer","description":"API key mp_live_ para superfícies de máquina — Authorization: Bearer <chave>"}},"schemas":{"Me":{"type":"object","properties":{"kind":{"type":"string","enum":["user"]},"orgId":{"type":"string"},"role":{"type":"string"},"user":{"type":["object","null"],"properties":{"id":{"type":"string"},"email":{"type":"string"},"name":{"type":["string","null"]},"avatarUrl":{"type":["string","null"]}},"required":["id","email","name","avatarUrl"]}},"required":["kind","orgId","role","user"]},"Error":{"type":"object","properties":{"type":{"type":"string","example":"about:blank"},"title":{"type":"string","description":"código estável do erro","example":"common.not_found"},"status":{"type":"integer","example":404},"detail":{"type":"string","example":"post não encontrado"},"extra":{"description":"campos extras (ex.: issues de validação)"}},"required":["type","title","status"]},"ApiKey":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"lastUsedAt":{"type":["string","null"]},"revokedAt":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","name","prefix","scopes","lastUsedAt","revokedAt","createdAt"]},"Channel":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string","example":"mastodon"},"externalId":{"type":"string"},"name":{"type":["string","null"]},"username":{"type":["string","null"]},"avatarUrl":{"type":["string","null"]},"status":{"type":"string","example":"ACTIVE"},"scopes":{"type":["array","null"],"items":{"type":"string"}}},"required":["id","provider","externalId","name","username","avatarUrl","status","scopes"]},"ProviderMediaRule":{"type":"object","properties":{"maxCount":{"type":"integer"},"mimeTypes":{"type":"array","items":{"type":"string"}},"maxBytes":{"type":"number"},"minWidth":{"type":"number"},"minHeight":{"type":"number"},"maxDurationSec":{"type":"number"}},"required":["maxCount","mimeTypes"]},"ChannelProviderInfo":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"editor":{"type":"string","enum":["plain","rich","markdown","html"]},"threads":{"type":"boolean"},"twoStepConnect":{"type":"boolean"},"requiresMedia":{"type":"boolean","description":"rede que não aceita post só-texto (ex.: TikTok exige vídeo/foto)"},"connectType":{"type":"string","enum":["fields","oauth"],"description":"fields = credenciais; oauth = redirect"},"maxLength":{"type":"integer","description":"limite base de caracteres (sem settings do canal — ex.: X verified pode ser maior)"},"media":{"type":"object","properties":{"images":{"$ref":"#/components/schemas/ProviderMediaRule"},"videos":{"$ref":"#/components/schemas/ProviderMediaRule"}},"required":["images","videos"]},"settingsSchema":{"type":"object","additionalProperties":{},"description":"JSON Schema dos settings de publicação do provider — é o formato aceito em `settingsByChannel` do POST /v1/posts; a UI renderiza o formulário \"Configurações\" por canal a partir dele"},"connectionFieldsSchema":{"type":"object","additionalProperties":{},"description":"JSON Schema dos campos de conexão — é o formato aceito em `fields` do POST /v1/channels/connect; presente só quando o provider pede credenciais/instância (a UI renderiza o formulário de conexão a partir dele; ausente = OAuth sem campos)"},"requiredFeature":{"type":"string","description":"feature de plano exigida por esta rede no serviço gerenciado (ex.: `x_network`); ausente = incluída em todos os planos. Em self-hosted nunca é imposta"},"available":{"type":"boolean","description":"false = rede implementada, mas SEM as credenciais de app no env desta instalação; `POST /connect` responde 404 até configurar"},"setupEnv":{"type":"array","items":{"type":"string"},"description":"variáveis de ambiente que faltam para habilitar a rede (ex.: `[\"THREADS_APP_ID\"]`). Só vem em instalação self-hosted, onde quem opera é quem usa; no serviço gerenciado é omitido"}},"required":["id","name","editor","threads","twoStepConnect","requiresMedia","connectType","maxLength","media","settingsSchema","available"]},"MediaRef":{"type":"object","properties":{"mediaId":{"type":"string"},"type":{"type":"string","example":"image"},"url":{"type":"string"},"mime":{"type":"string","example":"image/png"},"alt":{"type":["string","null"]}},"required":["type","url","mime"]},"Publication":{"type":"object","properties":{"id":{"type":"string"},"channelId":{"type":"string"},"state":{"type":"string","example":"SCHEDULED"},"media":{"type":"array","items":{"$ref":"#/components/schemas/MediaRef"}},"itemCount":{"type":"integer","description":"itens da thread (1 = post simples)"},"lastPublishedIndex":{"type":["integer","null"],"description":"cursor da thread: itens ≤ este índice já estão na rede"},"attemptCount":{"type":"integer"},"externalId":{"type":["string","null"]},"releaseUrl":{"type":["string","null"]},"errorClass":{"type":["string","null"]},"errorMessage":{"type":["string","null"]}},"required":["id","channelId","state","media","itemCount","lastPublishedIndex","attemptCount","externalId","releaseUrl","errorClass","errorMessage"]},"PostGroup":{"type":"object","properties":{"id":{"type":"string"},"state":{"type":"string","example":"SCHEDULED"},"publishAt":{"type":["string","null"],"format":"date-time"},"publications":{"type":"array","items":{"$ref":"#/components/schemas/Publication"}}},"required":["id","state","publishAt","publications"]},"PublicationThreadItem":{"type":"object","properties":{"text":{"type":"string"},"delaySec":{"type":"integer"},"media":{"type":"array","items":{"$ref":"#/components/schemas/MediaRef"}}},"required":["text","delaySec","media"]},"PublicationDetail":{"allOf":[{"$ref":"#/components/schemas/Publication"},{"type":"object","properties":{"text":{"type":"string","description":"texto do item 0 neste canal (override incluído)"},"settings":{"type":"object","additionalProperties":{},"description":"settings de publicação do canal (inclui defaults do settingsSchema)"},"thread":{"type":"array","items":{"$ref":"#/components/schemas/PublicationThreadItem"},"description":"réplicas encadeadas (posições ≥ 1; vazio = post simples)"}},"required":["text","settings","thread"]}]},"PostGroupDetail":{"type":"object","properties":{"id":{"type":"string"},"state":{"type":"string","example":"SCHEDULED"},"publishAt":{"type":["string","null"],"format":"date-time"},"text":{"type":"string","description":"texto base do grupo (sem overrides por canal)"},"publications":{"type":"array","items":{"$ref":"#/components/schemas/PublicationDetail"}}},"required":["id","state","publishAt","text","publications"]},"ApprovalLink":{"type":"object","properties":{"token":{"type":"string","description":"token opaco ≥256 bits — só aparece aqui"},"url":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"}},"required":["token","url","expiresAt"]},"ApprovalLinkStatus":{"type":["object","null"],"properties":{"status":{"type":"string","example":"PENDING"},"feedback":{"type":["string","null"]},"approverName":{"type":["string","null"]},"expiresAt":{"type":"string","format":"date-time"},"resolvedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}},"required":["status","feedback","approverName","expiresAt","resolvedAt","createdAt"]},"FeedItem":{"type":"object","properties":{"id":{"type":"string"},"groupId":{"type":"string"},"channelId":{"type":"string"},"state":{"type":"string","example":"PUBLISHED"},"publishAt":{"type":["string","null"],"format":"date-time"},"text":{"type":"string"},"mediaCount":{"type":"integer"},"externalId":{"type":["string","null"]},"releaseUrl":{"type":["string","null"]},"errorClass":{"type":["string","null"]},"errorMessage":{"type":["string","null"]},"attemptCount":{"type":"integer"},"group":{"type":"object","properties":{"state":{"type":"string"},"origin":{"type":"string","example":"WEB"},"awaitingApproval":{"type":"boolean"}},"required":["state","origin","awaitingApproval"]},"channel":{"type":"object","properties":{"provider":{"type":"string"},"name":{"type":"string"},"username":{"type":["string","null"]},"avatarUrl":{"type":["string","null"]}},"required":["provider","name","username","avatarUrl"]}},"required":["id","groupId","channelId","state","publishAt","text","mediaCount","externalId","releaseUrl","errorClass","errorMessage","attemptCount","group","channel"]},"PublicationFeed":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/FeedItem"}},"nextCursor":{"type":["string","null"],"description":"cursor keyset da próxima página"}},"required":["items","nextCursor"]},"Media":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string","description":"URL pública (as redes baixam por aqui)"},"mime":{"type":"string","example":"image/png"},"source":{"type":"string","description":"`ai` = gerada por IA; `upload` = enviada por alguém"},"byteSize":{"type":"integer"},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"alt":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","url","mime","source","byteSize","width","height","alt","createdAt"]},"Webhook":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"channelIds":{"type":"array","items":{"type":"string"}},"disabledAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","name","url","events","channelIds","disabledAt","createdAt"]},"WebhookCreated":{"type":"object","properties":{"secret":{"type":"string","description":"whsec_… — só aparece nesta resposta"},"webhook":{"$ref":"#/components/schemas/Webhook"}},"required":["secret","webhook"]},"Notification":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","example":"approval.resolved"},"title":{"type":"string"},"body":{"type":["string","null"]},"link":{"type":["string","null"]},"readAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","kind","title","body","link","readAt","createdAt"]},"Capabilities":{"type":"object","properties":{"billingEnabled":{"type":"boolean"},"selfHosted":{"type":"boolean"},"plan":{"type":"object","properties":{"tier":{"type":"string","enum":["FREE","PRO","PREMIUM"]},"status":{"type":["string","null"],"enum":["ACTIVE","TRIALING","PAST_DUE","CANCELED","INCOMPLETE"]},"period":{"type":["string","null"],"enum":["MONTHLY","YEARLY"]},"currentPeriodEnd":{"type":["string","null"]},"cancelAt":{"type":["string","null"]},"features":{"type":"array","items":{"type":"string"}},"limits":{"type":"object","properties":{"channels":{"type":"integer","description":"-1 = ilimitado"},"postsPerMonth":{"type":"integer"},"webhooks":{"type":"integer"},"apiKeys":{"type":"integer"}},"required":["channels","postsPerMonth","webhooks","apiKeys"]},"usage":{"type":"object","properties":{"channels":{"type":"integer"},"postsThisMonth":{"type":"integer"},"webhooks":{"type":"integer"},"apiKeys":{"type":"integer"}},"required":["channels","postsThisMonth","webhooks","apiKeys"]},"enforced":{"type":"boolean"}},"required":["tier","status","period","currentPeriodEnd","cancelAt","features","limits","usage","enforced"]},"ai":{"type":"object","properties":{"enabled":{"type":"boolean"},"canDescribeImages":{"type":"boolean"},"canGenerateImages":{"type":"boolean"},"credits":{"type":["object","null"],"properties":{"granted":{"type":"integer"},"used":{"type":"integer"},"reserved":{"type":"integer","description":"gerações em voo"},"remaining":{"type":"integer"},"periodEnd":{"type":"string"},"enforced":{"type":"boolean","description":"false = self-hosted: contabiliza, mas nunca recusa"}},"required":["granted","used","reserved","remaining","periodEnd","enforced"]}},"required":["enabled","canDescribeImages","canGenerateImages","credits"]},"endpoints":{"type":"object","properties":{"restBaseUrl":{"type":"string","example":"https://api.manypost.com.br/v1"},"mcpUrl":{"type":"string","example":"https://mcp.manypost.com.br"}},"required":["restBaseUrl","mcpUrl"]}},"required":["billingEnabled","selfHosted","plan","ai","endpoints"]},"ChannelNeedingAction":{"type":"object","properties":{"channelId":{"type":"string"},"provider":{"type":"string"},"name":{"type":["string","null"]},"status":{"type":"string","description":"REFRESH_REQUIRED | PENDING_ACCOUNT_SELECTION | DISABLED"}},"required":["channelId","provider","name","status"]},"InsightsSummary":{"type":"object","properties":{"timezone":{"type":"string","description":"fuso em que as fronteiras de dia foram resolvidas — a resposta diz qual usou"},"attention":{"type":"object","properties":{"failed":{"type":"integer"},"needsReview":{"type":"integer","description":"desfecho incerto: nunca retentado sozinho, espera decisão humana"},"awaitingApproval":{"type":"integer","description":"por GRUPO, não por publicação"},"partial":{"type":"integer","description":"saiu em algumas redes e não em outras"},"channels":{"type":"array","items":{"$ref":"#/components/schemas/ChannelNeedingAction"}},"total":{"type":"integer","description":"0 = a interface esconde o bloco inteiro"}},"required":["failed","needsReview","awaitingApproval","partial","channels","total"]},"today":{"type":"object","properties":{"scheduled":{"type":"integer"},"published":{"type":"integer"},"failed":{"type":"integer"}},"required":["scheduled","published","failed"]},"week":{"type":"object","properties":{"scheduled":{"type":"integer"},"byDay":{"type":"array","items":{"type":"integer"},"description":"7 posições, índice 0 = hoje no fuso pedido"}},"required":["scheduled","byDay"]},"firstRun":{"type":["string","null"],"enum":["no_channels","no_posts"],"description":"organização sem o que operar. A interface troca os blocos operacionais por próximos passos em vez de mostrar uma grade de zeros. null = já está operando."}},"required":["timezone","attention","today","week","firstRun"]},"AiVariant":{"type":"object","properties":{"channelId":{"type":"string"},"text":{"type":"string"},"maxLength":{"type":"integer"},"shortened":{"type":"boolean","description":"true = o texto passou do limite do canal e foi cortado"}},"required":["channelId","text","maxLength","shortened"]},"AiRewriteResult":{"type":"object","properties":{"channelId":{"type":["string","null"],"description":"null = reescrita sem canal"},"text":{"type":"string"},"maxLength":{"type":["integer","null"]},"overLimit":{"type":"boolean","description":"true = passou do limite do canal, e nada foi removido por isso"}},"required":["channelId","text","maxLength","overLimit"]},"AiPlannedSlot":{"type":"object","properties":{"channelId":{"type":"string"},"topic":{"type":"string"},"text":{"type":"string"},"publishAt":{"type":"string","format":"date-time"},"maxLength":{"type":"integer"},"shortened":{"type":"boolean"}},"required":["channelId","topic","text","publishAt","maxLength","shortened"]},"AiGeneratedMedia":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"mime":{"type":"string"},"byteSize":{"type":"integer"},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"alt":{"type":["string","null"]},"source":{"type":"string","description":"`ai` = gerada; `upload` = enviada por alguém"}},"required":["id","url","mime","byteSize","width","height","alt","source"]},"AiBestTimes":{"type":"object","properties":{"channelId":{"type":"string"},"timezone":{"type":"string"},"slots":{"type":"array","items":{"type":"object","properties":{"weekday":{"type":"integer","description":"0 = domingo … 6 = sábado"},"hour":{"type":"integer"},"score":{"type":"number"}},"required":["weekday","hour","score"]}},"confidence":{"type":"string","enum":["low","medium","high"]},"sampleSize":{"type":"integer","description":"publicações da própria organização que sustentam a resposta"},"fromBaseline":{"type":"boolean","description":"true = veio da linha de base da rede, sem histórico próprio"},"signal":{"type":"string","enum":["network_baseline","own_posting_history","own_engagement"],"description":"o que sustenta a resposta. `own_posting_history` = os horários que a organização MAIS USA neste canal, não uma medição de desempenho — enquanto for esse o sinal, `confidence` não passa de `medium`. `own_engagement` depende da coleta de métricas, que ainda não existe."}},"required":["channelId","timezone","slots","confidence","sampleSize","fromBaseline","signal"]},"ApprovalPreview":{"type":"object","properties":{"status":{"type":"string","example":"PENDING"},"feedback":{"type":["string","null"]},"approverName":{"type":["string","null"]},"expiresAt":{"type":"string","format":"date-time"},"resolvedAt":{"type":["string","null"],"format":"date-time"},"publishAt":{"type":["string","null"],"format":"date-time"},"timezone":{"type":"string"},"publications":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string"},"channelName":{"type":"string"},"channelUsername":{"type":["string","null"]},"channelAvatarUrl":{"type":["string","null"]},"items":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string"},"media":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"url":{"type":"string"},"mime":{"type":"string"},"alt":{"type":"string"}},"required":["type","url","mime"]}},"delaySec":{"type":"integer"}},"required":["text","media","delaySec"]}}},"required":["provider","channelName","channelUsername","channelAvatarUrl","items"]}}},"required":["status","feedback","approverName","expiresAt","resolvedAt","publishAt","timezone","publications"]},"ApprovalResolution":{"type":"object","properties":{"status":{"type":"string","example":"APPROVED"},"resolvedAt":{"type":["string","null"],"format":"date-time"},"alreadyResolved":{"type":"boolean"}},"required":["status","resolvedAt"]},"PubMediaRef":{"type":"object","properties":{"mediaId":{"type":"string"},"type":{"type":"string","example":"image"},"url":{"type":"string"},"mime":{"type":"string","example":"image/png"},"alt":{"type":["string","null"]}},"required":["type","url","mime"]},"PubPublication":{"type":"object","properties":{"id":{"type":"string"},"channelId":{"type":"string"},"state":{"type":"string","example":"SCHEDULED"},"media":{"type":"array","items":{"$ref":"#/components/schemas/PubMediaRef"}},"itemCount":{"type":"integer"},"lastPublishedIndex":{"type":["integer","null"]},"attemptCount":{"type":"integer"},"externalId":{"type":["string","null"]},"releaseUrl":{"type":["string","null"]},"errorClass":{"type":["string","null"]},"errorMessage":{"type":["string","null"]}},"required":["id","channelId","state","media","itemCount","lastPublishedIndex","attemptCount","externalId","releaseUrl","errorClass","errorMessage"]},"PubPostGroup":{"type":"object","properties":{"id":{"type":"string"},"state":{"type":"string","example":"SCHEDULED"},"publishAt":{"type":["string","null"],"format":"date-time"},"publications":{"type":"array","items":{"$ref":"#/components/schemas/PubPublication"}}},"required":["id","state","publishAt","publications"]},"PubFeedItem":{"type":"object","properties":{"id":{"type":"string"},"groupId":{"type":"string"},"channelId":{"type":"string"},"state":{"type":"string","example":"PUBLISHED"},"publishAt":{"type":["string","null"],"format":"date-time"},"text":{"type":"string"},"mediaCount":{"type":"integer"},"externalId":{"type":["string","null"]},"releaseUrl":{"type":["string","null"]},"errorClass":{"type":["string","null"]},"errorMessage":{"type":["string","null"]},"attemptCount":{"type":"integer"},"group":{"type":"object","properties":{"state":{"type":"string"},"origin":{"type":"string"},"awaitingApproval":{"type":"boolean"}},"required":["state","origin","awaitingApproval"]},"channel":{"type":"object","properties":{"provider":{"type":"string"},"name":{"type":"string"},"username":{"type":["string","null"]},"avatarUrl":{"type":["string","null"]}},"required":["provider","name","username","avatarUrl"]}},"required":["id","groupId","channelId","state","publishAt","text","mediaCount","externalId","releaseUrl","errorClass","errorMessage","attemptCount","group","channel"]},"PubPublicationFeed":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PubFeedItem"}},"nextCursor":{"type":["string","null"]}},"required":["items","nextCursor"]},"PubChannel":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string","example":"mastodon"},"externalId":{"type":"string"},"name":{"type":["string","null"]},"username":{"type":["string","null"]},"avatarUrl":{"type":["string","null"]},"status":{"type":"string","example":"ACTIVE"},"scopes":{"type":["array","null"],"items":{"type":"string"}}},"required":["id","provider","externalId","name","username","avatarUrl","status","scopes"]},"PubMedia":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"mime":{"type":"string","example":"image/png"},"byteSize":{"type":"integer"},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"alt":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","url","mime","byteSize","width","height","alt","createdAt"]},"PubWebhook":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"channelIds":{"type":"array","items":{"type":"string"}},"disabledAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","name","url","events","channelIds","disabledAt","createdAt"]}},"parameters":{}},"paths":{"/health":{"get":{"tags":["health"],"summary":"Status e providers de rede disponíveis","responses":{"200":{"description":"health check","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]},"providers":{"type":"array","items":{"type":"string"}}},"required":["status","providers"]}}}}}}},"/v1/auth/me":{"get":{"tags":["auth"],"security":[{"clerkAuth":[]}],"summary":"Identidade Clerk com autorização Manypost","responses":{"200":{"description":"usuário Clerk com papel Manypost","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"provedor externo temporariamente indisponível","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/api-keys":{"get":{"tags":["api-keys"],"security":[{"clerkAuth":[]}],"summary":"Lista as API keys da organização","responses":{"200":{"description":"API keys da organização (sem hash — chave só aparece na criação)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["api-keys"],"security":[{"clerkAuth":[]}],"summary":"Cria uma API key (mp_live_…) com escopos","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":60},"scopes":{"type":"array","items":{"type":"string","enum":["posts:read","posts:write","channels:read","channels:write","media:write","analytics:read","webhooks:manage","mcp","mcp:read","mcp:write"]},"minItems":1}},"required":["name","scopes"]}}}},"responses":{"201":{"description":"a apiKey em claro aparece SOMENTE nesta resposta","content":{"application/json":{"schema":{"type":"object","properties":{"apiKey":{"type":"string"},"record":{"$ref":"#/components/schemas/ApiKey"}},"required":["apiKey","record"]}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/api-keys/{id}":{"delete":{"tags":["api-keys"],"security":[{"clerkAuth":[]}],"summary":"Revoga uma API key","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"chave revogada"},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/channels":{"get":{"tags":["channels"],"security":[{"clerkAuth":[]}],"summary":"Lista os canais conectados (tokens nunca são expostos)","responses":{"200":{"description":"canais","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Channel"}}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/channels/providers":{"get":{"tags":["channels"],"security":[{"clerkAuth":[]}],"summary":"Catálogo de providers desta instalação","description":"Lista **todas** as redes implementadas, com `available` dizendo se as credenciais de app estão no env. Em self-hosted, a indisponível ainda traz `setupEnv` com as variáveis que faltam — assim a UI mostra \"precisa de credencial\" em vez de esconder a rede.","responses":{"200":{"description":"providers","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ChannelProviderInfo"}}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/channels/connect":{"post":{"tags":["channels"],"security":[{"clerkAuth":[]}],"summary":"Inicia a conexão de um canal","description":"Provider por credenciais (connectType=fields) conecta direto e devolve o canal (201). Provider OAuth devolve a URL de autorização (200) — o navegador segue e volta em /callback.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"string","minLength":1},"fields":{}},"required":["provider"]}}}},"responses":{"200":{"description":"URL de autorização OAuth","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]}}}},"201":{"description":"canal conectado (fluxo por credenciais)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Channel"}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/channels/callback/{provider}":{"get":{"tags":["channels"],"security":[{"clerkAuth":[]}],"summary":"Callback OAuth — troca o code pelo token e conecta o canal","description":"Alvo do redirect OAuth (aberto num popup pela UI). Conecta o canal e devolve uma página HTML que avisa o opener via postMessage (`manypost:oauth:success`) e fecha a janela.","parameters":[{"schema":{"type":"string"},"required":true,"name":"provider","in":"path"},{"schema":{"type":"string"},"required":true,"name":"code","in":"query"},{"schema":{"type":"string"},"required":true,"name":"state","in":"query"}],"responses":{"200":{"description":"página HTML que fecha o popup e notifica o opener — o canal já foi conectado","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/channels/{id}":{"delete":{"tags":["channels"],"security":[{"clerkAuth":[]}],"summary":"Desconecta um canal (soft delete)","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"canal desconectado"},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/channels/{id}/sub-accounts":{"get":{"tags":["channels"],"security":[{"clerkAuth":[]}],"summary":"Lista sub-contas ou canais (ex: canais de texto do servidor Discord)","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"lista de canais/sub-contas","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"externalId":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"},"avatarUrl":{"type":"string"}},"required":["externalId","name"]}}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/posts":{"post":{"tags":["posts"],"security":[{"clerkAuth":[]}],"summary":"Agenda um post (1 grupo → 1 publicação por canal)","description":"Valida texto/mídia por canal e enfileira. `textByChannel` personaliza o texto do post principal por canal; `thread` cria réplicas encadeadas; `requireApproval` nasce DRAFT aguardando aprovação por link.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":10000},"channelIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":20},"publishAt":{"type":"string","format":"date-time"},"timezone":{"type":"string","default":"UTC"},"settingsByChannel":{"type":"object","additionalProperties":{}},"textByChannel":{"type":"object","additionalProperties":{"type":"string","minLength":1,"maxLength":10000}},"mediaIds":{"type":"array","items":{"type":"string","format":"uuid"},"maxItems":10},"thread":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":10000},"mediaIds":{"type":"array","items":{"type":"string","format":"uuid"},"maxItems":10},"delaySec":{"type":"integer","minimum":0,"maximum":600}},"required":["text"]},"maxItems":24},"requireApproval":{"type":"boolean"}},"required":["text","channelIds","publishAt"]}}}},"responses":{"201":{"description":"grupo agendado (ou DRAFT se requireApproval)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostGroup"}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/posts/{groupId}":{"get":{"tags":["posts"],"security":[{"clerkAuth":[]}],"summary":"Detalhe de um grupo de post","description":"Além dos estados, expõe o conteúdo completo (texto base, texto/settings por canal e réplicas de thread) — é o que o composer usa p/ duplicar um post.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"groupId","in":"path"}],"responses":{"200":{"description":"grupo","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostGroupDetail"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["posts"],"security":[{"clerkAuth":[]}],"summary":"Edita texto, horário e/ou settings por canal (re-agenda com nova versão de job)","description":"Editar `text` sobrescreve o content de TODAS as publicações (overrides resetam). `settingsByChannel` (chave = channelId) faz merge nos settings de publicação do canal e é validado por provider.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"groupId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":10000},"publishAt":{"type":"string","format":"date-time"},"settingsByChannel":{"type":"object","additionalProperties":{}}}}}}},"responses":{"200":{"description":"grupo re-agendado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostGroup"}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/posts/{groupId}/cancel":{"post":{"tags":["posts"],"security":[{"clerkAuth":[]}],"summary":"Cancela um grupo agendado (job antigo morre por versão)","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"groupId","in":"path"}],"responses":{"200":{"description":"grupo cancelado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostGroup"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/posts/{groupId}/retry":{"post":{"tags":["posts"],"security":[{"clerkAuth":[]}],"summary":"Tenta publicar de novo (FAILED/NEEDS_REVIEW → SCHEDULED)","description":"Kanban \"tentar novamente\". Com `channelId` no corpo, retenta só aquele canal.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"groupId","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"channelId":{"type":"string","format":"uuid"}}}}}},"responses":{"200":{"description":"grupo re-agendado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostGroup"}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/posts/{groupId}/approval-link":{"post":{"tags":["posts","approvals"],"security":[{"clerkAuth":[]}],"summary":"Cria o link público de aprovação de um rascunho","description":"Só para grupos DRAFT (requireApproval). Criar de novo revoga o link anterior.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"groupId","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"expiresInHours":{"type":"integer","minimum":1,"maximum":720}}}}}},"responses":{"201":{"description":"link criado (token só aqui)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApprovalLink"}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["posts","approvals"],"security":[{"clerkAuth":[]}],"summary":"Status do link de aprovação mais recente (ou null)","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"groupId","in":"path"}],"responses":{"200":{"description":"status do link (null se nunca houve)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApprovalLinkStatus"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["posts","approvals"],"security":[{"clerkAuth":[]}],"summary":"Revoga o link de aprovação pendente","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"groupId","in":"path"}],"responses":{"200":{"description":"resultado","content":{"application/json":{"schema":{"type":"object","properties":{"revoked":{"type":"boolean"}},"required":["revoked"]}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/publications":{"get":{"tags":["publications"],"security":[{"clerkAuth":[]}],"summary":"Feed flat de publicações (calendário/kanban)","description":"Uma linha por publicação (o cliente agrupa por groupId). Cada item embute grupo (state/origin/awaitingApproval) e canal. Paginação keyset por (publishAt, id).","parameters":[{"schema":{"type":"string","format":"date-time","description":"início do período (ISO 8601)"},"required":false,"name":"from","in":"query"},{"schema":{"type":"string","format":"date-time","description":"fim do período (ISO 8601)"},"required":false,"name":"to","in":"query"},{"schema":{"type":"string","description":"csv de estados de publicação","example":"SCHEDULED,PUBLISHED"},"required":false,"name":"state","in":"query"},{"schema":{"type":"string","description":"csv de ids de canal"},"required":false,"name":"channelId","in":"query"},{"schema":{"type":"string","description":"cursor keyset da página anterior"},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","description":"1–200 (default 100)","example":"100"},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"página do feed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicationFeed"}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/events":{"get":{"tags":["events"],"security":[{"clerkAuth":[]}],"summary":"Stream SSE de eventos em tempo real","description":"Eventos nomeados: post.scheduled, post.published, post.failed, channel.refresh_required, notification.created (+ hello no handshake e ping a cada 25s). Sem Redis o stream fica só com keepalive — a UI cai no polling.","responses":{"200":{"description":"stream text/event-stream","content":{"text/event-stream":{"schema":{"type":"string"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/media/upload":{"post":{"tags":["media"],"security":[{"clerkAuth":[]}],"summary":"Envia mídia (multipart) — MIME real por magic bytes","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"arquivo (MIME real detectado por magic bytes)"},"alt":{"type":"string"}},"required":["file"]}}}},"responses":{"201":{"description":"mídia criada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Media"}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/media/from-url":{"post":{"tags":["media"],"security":[{"clerkAuth":[]}],"summary":"Importa mídia por URL (anti-SSRF, re-valida a cada redirect)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"alt":{"type":"string","maxLength":1500}},"required":["url"]}}}},"responses":{"201":{"description":"mídia criada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Media"}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/media":{"get":{"tags":["media"],"security":[{"clerkAuth":[]}],"summary":"Lista a biblioteca de mídia","parameters":[{"schema":{"type":"string","example":"50"},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"itens de mídia","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Media"}}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/media/{id}":{"patch":{"tags":["media"],"security":[{"clerkAuth":[]}],"summary":"Atualiza o texto alternativo (alt) de uma mídia","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"alt":{"type":["string","null"],"maxLength":1500}},"required":["alt"]}}}},"responses":{"204":{"description":"alt atualizado"},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["media"],"security":[{"clerkAuth":[]}],"summary":"Remove uma mídia (soft — arquivo fica p/ posts já agendados)","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"mídia removida"},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/webhooks":{"get":{"tags":["webhooks"],"security":[{"clerkAuth":[]}],"summary":"Lista os webhooks de saída","responses":{"200":{"description":"webhooks","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["webhooks"],"security":[{"clerkAuth":[]}],"summary":"Cria um webhook (entrega assinada HMAC)","description":"O secret whsec_ é retornado só aqui — o receptor valida a assinatura com ele.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":60},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string","enum":["post.scheduled","post.published","post.failed","channel.refresh_required","channel.disconnected","mention.received"]},"minItems":1},"channelIds":{"type":"array","items":{"type":"string","format":"uuid"}}},"required":["name","url","events"]}}}},"responses":{"201":{"description":"webhook criado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreated"}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/webhooks/{id}":{"delete":{"tags":["webhooks"],"security":[{"clerkAuth":[]}],"summary":"Remove um webhook","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"webhook removido"},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/notifications":{"get":{"tags":["notifications"],"security":[{"clerkAuth":[]}],"summary":"Lista as notificações da organização","responses":{"200":{"description":"notificações","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Notification"}}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/notifications/read-all":{"post":{"tags":["notifications"],"security":[{"clerkAuth":[]}],"summary":"Marca todas as notificações como lidas","responses":{"200":{"description":"quantas foram marcadas","content":{"application/json":{"schema":{"type":"object","properties":{"read":{"type":"integer"}},"required":["read"]}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/notifications/{id}/read":{"post":{"tags":["notifications"],"security":[{"clerkAuth":[]}],"summary":"Marca uma notificação como lida (idempotente)","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"lida","content":{"application/json":{"schema":{"type":"object","properties":{"read":{"type":"boolean","enum":[true]}},"required":["read"]}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/capabilities":{"get":{"tags":["capabilities"],"security":[{"clerkAuth":[]}],"summary":"Plano, features liberadas, limites e uso da organização","responses":{"200":{"description":"capacidades da organização","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Capabilities"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/insights/summary":{"get":{"tags":["insights"],"security":[{"clerkAuth":[]}],"summary":"Resumo operacional da organização (tela inicial)","description":"Contagens agregadas: o que precisa de atenção (falhas, revisão, aprovação, entrega parcial, canais a reconectar), o que sai hoje e a semana por dia. **Não traz métrica de desempenho** — a plataforma não coleta engajamento, então todo número aqui vem do registro do que ela mesma pediu e entregou. As fronteiras de dia são resolvidas no fuso informado em `tz`.","parameters":[{"schema":{"type":"string","description":"IANA; inválido responde 400","example":"America/Sao_Paulo"},"required":false,"name":"tz","in":"query"}],"responses":{"200":{"description":"resumo","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsightsSummary"}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/ai/caption":{"post":{"tags":["ai"],"security":[{"clerkAuth":[]}],"summary":"Gera uma legenda por canal, adaptada a cada rede","description":"Requer a feature `ai_caption` (plano Pro). O texto devolvido SEMPRE respeita o limite do canal: se o modelo passar, cortamos numa fronteira de frase e marcamos `shortened`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"brief":{"type":"string","minLength":1,"maxLength":4000},"channelIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":20,"description":"canais de destino — todos precisam ser desta organização"},"tone":{"type":"string","maxLength":120},"settings":{"type":"object","additionalProperties":{},"description":"settings da publicação, mergeados com os do canal — mesma semântica do agendamento (uma conta verificada do X, por exemplo, valida contra o limite maior)"}},"required":["brief","channelIds"]}}}},"responses":{"200":{"description":"legendas por canal","content":{"application/json":{"schema":{"type":"object","properties":{"variants":{"type":"array","items":{"$ref":"#/components/schemas/AiVariant"}}},"required":["variants"]}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"plano atual não inclui — `extra.requiredTier` diz o plano mínimo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/ai/rewrite":{"post":{"tags":["ai"],"security":[{"clerkAuth":[]}],"summary":"Reescreve um texto seguindo uma instrução","description":"Requer a feature `ai_caption` (plano Pro). **Nunca encurta o texto**: reescrever é a única operação cuja entrada é o texto que a pessoa escreveu, e cortá-lo para caber num limite que ela não escolheu perderia trabalho. Quando um canal é informado e o resultado passa do limite dele, `overLimit` vem true e o texto vem inteiro — o limite continua sendo imposto no agendamento.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":20000},"instructionId":{"type":"string","enum":["shorten","expand","formal","casual","with_emoji","without_emoji","fix_grammar"],"description":"instrução do catálogo do servidor"},"instruction":{"type":"string","minLength":1,"maxLength":500,"description":"instrução em texto livre — alternativa a `instructionId`"},"channelId":{"type":"string","format":"uuid","description":"opcional: sem canal a reescrita roda e nenhum limite é imposto nem reportado"},"settings":{"type":"object","additionalProperties":{},"description":"settings da publicação, mergeados com os do canal — mesma semântica do agendamento (uma conta verificada do X, por exemplo, valida contra o limite maior)"}},"required":["text"]}}}},"responses":{"200":{"description":"texto reescrito","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiRewriteResult"}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"plano atual não inclui — `extra.requiredTier` diz o plano mínimo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/ai/hashtags":{"post":{"tags":["ai"],"security":[{"clerkAuth":[]}],"summary":"Sugere hashtags para um texto e uma rede","description":"Requer a feature `ai_caption` (plano Pro).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":20000},"channelId":{"type":"string","format":"uuid"},"count":{"type":"integer","minimum":1,"maximum":30}},"required":["text","channelId"]}}}},"responses":{"200":{"description":"hashtags sugeridas","content":{"application/json":{"schema":{"type":"object","properties":{"hashtags":{"type":"array","items":{"type":"string"}}},"required":["hashtags"]}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"plano atual não inclui — `extra.requiredTier` diz o plano mínimo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/ai/alt-text":{"post":{"tags":["ai"],"security":[{"clerkAuth":[]}],"summary":"Descreve uma imagem da biblioteca para leitores de tela","description":"Requer a feature `ai_caption` (plano Pro) **e** um modelo que enxergue imagem. Sem essa capacidade responde 501 `ai.capability_unavailable` — descrever pelo nome do arquivo seria pior que não descrever.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mediaId":{"type":"string","format":"uuid"},"context":{"type":"string","maxLength":2000},"maxLength":{"type":"integer","minimum":60,"maximum":1000}},"required":["mediaId"]}}}},"responses":{"200":{"description":"descrição da imagem","content":{"application/json":{"schema":{"type":"object","properties":{"alt":{"type":"string"}},"required":["alt"]}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"plano atual não inclui — `extra.requiredTier` diz o plano mínimo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"501":{"description":"capacidade não disponível nesta instalação","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/ai/draft":{"post":{"tags":["ai"],"security":[{"clerkAuth":[]}],"summary":"Transforma uma ideia em um rascunho por canal","description":"Requer a feature `ai_multichannel_draft` (plano Premium).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"idea":{"type":"string","minLength":1,"maxLength":4000},"channelIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":20,"description":"canais de destino — todos precisam ser desta organização"},"tone":{"type":"string","maxLength":120},"settings":{"type":"object","additionalProperties":{},"description":"settings da publicação, mergeados com os do canal — mesma semântica do agendamento (uma conta verificada do X, por exemplo, valida contra o limite maior)"}},"required":["idea","channelIds"]}}}},"responses":{"200":{"description":"rascunhos por canal","content":{"application/json":{"schema":{"type":"object","properties":{"drafts":{"type":"array","items":{"$ref":"#/components/schemas/AiVariant"}}},"required":["drafts"]}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"plano atual não inclui — `extra.requiredTier` diz o plano mínimo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/ai/week-plan":{"post":{"tags":["ai"],"security":[{"clerkAuth":[]}],"summary":"Propõe uma semana de publicações","description":"Requer a feature `ai_calendar` (plano Premium). **Propõe, não agenda**: nada é criado, agendado ou publicado por esta rota — o resultado é material que uma pessoa aceita.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"goal":{"type":"string","minLength":1,"maxLength":2000},"channelIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":20,"description":"canais de destino — todos precisam ser desta organização"},"weekStart":{"type":"string","format":"date-time","description":"início da semana, ISO 8601"},"slots":{"type":"integer","minimum":1,"maximum":21},"settings":{"type":"object","additionalProperties":{},"description":"settings da publicação, mergeados com os do canal — mesma semântica do agendamento (uma conta verificada do X, por exemplo, valida contra o limite maior)"}},"required":["goal","channelIds","weekStart"]}}}},"responses":{"200":{"description":"semana proposta","content":{"application/json":{"schema":{"type":"object","properties":{"slots":{"type":"array","items":{"$ref":"#/components/schemas/AiPlannedSlot"}}},"required":["slots"]}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"plano atual não inclui — `extra.requiredTier` diz o plano mínimo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/ai/image":{"post":{"tags":["ai"],"security":[{"clerkAuth":[]}],"summary":"Gera uma imagem e guarda na biblioteca de mídia","description":"Requer a feature `ai_image` (plano Premium) **e** um provedor que gere imagem; sem essa capacidade responde 501 `ai.capability_unavailable`. O modo `economy` custa 2 créditos e usa renderização baixa; `quality` custa 5 e usa alta. O padrão é `economy`. Uma requisição gera uma imagem. A forma pedida é **proporção**, nunca resolução: quem traduz é o adapter. Os bytes devolvidos pelo provedor são validados por assinatura de arquivo (o `content-type` declarado não é confiável) e entram na biblioteca marcados como gerados, com o prompt e o modelo. Aceita `Idempotency-Key`: repetir uma geração paga não pode cobrar duas vezes.","parameters":[{"schema":{"type":"string","description":"Identifica a tentativa lógica. Repetir a mesma chave e o mesmo corpo devolve a resposta original sem nova cobrança.","example":"0198f0d8-5038-7c4e-a46f-243c2495f949"},"required":false,"name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":"string","minLength":1,"maxLength":2000},"aspect":{"type":"string","enum":["1:1","4:5","9:16","16:9","1.91:1"],"description":"proporção; sem ela, `channelId` decide; sem os dois, 1:1"},"channelId":{"type":"string","format":"uuid","description":"a proporção vira a que a rede deste canal trata melhor no feed"},"mode":{"type":"string","enum":["economy","quality"],"description":"economy = 2 créditos/low; quality = 5 créditos/high; padrão economy"},"alt":{"type":"string","maxLength":1000,"description":"descrição para leitor de tela"}},"required":["prompt"]}}}},"responses":{"200":{"description":"mídia gerada","content":{"application/json":{"schema":{"type":"object","properties":{"media":{"$ref":"#/components/schemas/AiGeneratedMedia"}},"required":["media"]}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"plano atual não inclui — `extra.requiredTier` diz o plano mínimo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"conflito","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"501":{"description":"capacidade não disponível nesta instalação","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/ai/best-times":{"get":{"tags":["ai"],"security":[{"clerkAuth":[]}],"summary":"Sugere horários de publicação para um canal","description":"Requer a feature `ai_best_time` (plano Pro). **Não usa modelo e não consome franquia**: é estatística sobre o histórico da própria organização mais uma linha de base por rede. `confidence` e `sampleSize` dizem o quanto a resposta vale — com pouco histórico, `fromBaseline` vem true e a confiança é baixa. Responde mesmo sem IA configurada.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"channelId","in":"query"},{"schema":{"type":"string","example":"America/Sao_Paulo"},"required":false,"name":"timezone","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":21},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"horários sugeridos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiBestTimes"}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"plano atual não inclui — `extra.requiredTier` diz o plano mínimo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/uploads/{org}/{file}":{"get":{"tags":["media"],"summary":"Serve um arquivo de mídia (público, sem auth — chaves UUID)","parameters":[{"schema":{"type":"string"},"required":true,"name":"org","in":"path"},{"schema":{"type":"string"},"required":true,"name":"file","in":"path"}],"responses":{"200":{"description":"bytes do arquivo","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"não encontrado"}}}},"/public/approval/{token}":{"get":{"tags":["approvals"],"summary":"Preview público do post (sem login, por token)","description":"404 uniforme para token inválido/expirado/revogado — sem enumeração.","parameters":[{"schema":{"type":"string"},"required":true,"name":"token","in":"path"}],"responses":{"200":{"description":"como o post será publicado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApprovalPreview"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/approval/{token}/approve":{"post":{"tags":["approvals"],"summary":"Cliente aprova — agenda o post de verdade (idempotente)","parameters":[{"schema":{"type":"string"},"required":true,"name":"token","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120}}}}}},"responses":{"200":{"description":"resolução","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApprovalResolution"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/approval/{token}/request-changes":{"post":{"tags":["approvals"],"summary":"Cliente pede ajustes (feedback obrigatório) — mantém rascunho","parameters":[{"schema":{"type":"string"},"required":true,"name":"token","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"feedback":{"type":"string","minLength":1,"maxLength":2000},"name":{"type":"string","minLength":1,"maxLength":120}},"required":["feedback"]}}}},"responses":{"200":{"description":"resolução","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApprovalResolution"}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/v1/posts":{"post":{"tags":["public-posts"],"summary":"Agenda um post (escopo posts:write)","description":"Idempotente com o header `Idempotency-Key`. `requireApproval` nasce DRAFT aguardando link de aprovação; `thread` cria réplicas encadeadas.","security":[{"apiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":10000},"channelIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":20},"publishAt":{"type":"string","format":"date-time"},"timezone":{"type":"string","default":"UTC"},"settingsByChannel":{"type":"object","additionalProperties":{}},"textByChannel":{"type":"object","additionalProperties":{"type":"string","minLength":1,"maxLength":10000}},"mediaIds":{"type":"array","items":{"type":"string","format":"uuid"},"maxItems":10},"thread":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":10000},"mediaIds":{"type":"array","items":{"type":"string","format":"uuid"},"maxItems":10},"delaySec":{"type":"integer","minimum":0,"maximum":600}},"required":["text"]},"maxItems":24},"requireApproval":{"type":"boolean"}},"required":["text","channelIds","publishAt"]}}}},"responses":{"201":{"description":"grupo agendado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PubPostGroup"}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/v1/posts/{groupId}":{"get":{"tags":["public-posts"],"summary":"Detalhe de um grupo (escopo posts:read)","security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"groupId","in":"path"}],"responses":{"200":{"description":"grupo","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PubPostGroup"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["public-posts"],"summary":"Edita texto/horário/settings e re-agenda (escopo posts:write)","security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"groupId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":10000},"publishAt":{"type":"string","format":"date-time"},"settingsByChannel":{"type":"object","additionalProperties":{}}}}}}},"responses":{"200":{"description":"grupo re-agendado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PubPostGroup"}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["public-posts"],"summary":"Cancela um grupo agendado (escopo posts:write)","security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"groupId","in":"path"}],"responses":{"200":{"description":"grupo cancelado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PubPostGroup"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/v1/posts/{groupId}/retry":{"post":{"tags":["public-posts"],"summary":"Tenta publicar de novo (escopo posts:write)","description":"FAILED/NEEDS_REVIEW → SCHEDULED. Com `channelId` no corpo, retenta só o canal.","security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"groupId","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"channelId":{"type":"string","format":"uuid"}}}}}},"responses":{"200":{"description":"grupo re-agendado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PubPostGroup"}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/v1/posts/{groupId}/approval-link":{"post":{"tags":["public-posts"],"summary":"Cria o link público de aprovação de um rascunho (escopo posts:write)","security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"groupId","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"expiresInHours":{"type":"integer","minimum":1,"maximum":720}}}}}},"responses":{"201":{"description":"link criado (token só aqui)","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"url":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"}},"required":["token","url","expiresAt"]}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["public-posts"],"summary":"Revoga o link de aprovação pendente (escopo posts:write)","security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"groupId","in":"path"}],"responses":{"200":{"description":"resultado","content":{"application/json":{"schema":{"type":"object","properties":{"revoked":{"type":"boolean"}},"required":["revoked"]}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/v1/publications":{"get":{"tags":["public-publications"],"summary":"Feed flat de publicações — status por canal (escopo posts:read)","description":"Uma linha por publicação; paginação keyset por (publishAt, id).","security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","format":"date-time"},"required":false,"name":"from","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"to","in":"query"},{"schema":{"type":"string","example":"SCHEDULED,PUBLISHED"},"required":false,"name":"state","in":"query"},{"schema":{"type":"string"},"required":false,"name":"channelId","in":"query"},{"schema":{"type":"string"},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","example":"100"},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"página do feed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PubPublicationFeed"}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/v1/channels":{"get":{"tags":["public-channels"],"summary":"Lista os canais conectados (escopo channels:read)","security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"canais","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PubChannel"}}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/v1/channels/providers":{"get":{"tags":["public-channels"],"summary":"Catálogo de providers disponíveis + capacidades (escopo channels:read)","security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"providers","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":{}}}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/v1/channels/{id}":{"delete":{"tags":["public-channels"],"summary":"Desconecta um canal (escopo channels:write)","security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"canal desconectado"},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/v1/media/upload":{"post":{"tags":["public-media"],"summary":"Envia mídia multipart — MIME real por magic bytes (escopo media:write)","security":[{"apiKeyAuth":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"alt":{"type":"string"}},"required":["file"]}}}},"responses":{"201":{"description":"mídia criada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PubMedia"}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/v1/media/from-url":{"post":{"tags":["public-media"],"summary":"Importa mídia por URL (anti-SSRF) (escopo media:write)","security":[{"apiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"alt":{"type":"string","maxLength":1500}},"required":["url"]}}}},"responses":{"201":{"description":"mídia criada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PubMedia"}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/v1/media":{"get":{"tags":["public-media"],"summary":"Lista a biblioteca de mídia (escopo media:write)","security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","example":"50"},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"itens de mídia","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PubMedia"}}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/v1/webhooks":{"get":{"tags":["public-webhooks"],"summary":"Lista os webhooks de saída (escopo webhooks:manage)","security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"webhooks","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PubWebhook"}}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["public-webhooks"],"summary":"Cria um webhook — entrega assinada HMAC (escopo webhooks:manage)","description":"O secret whsec_ é retornado só aqui.","security":[{"apiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":60},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string","enum":["post.scheduled","post.published","post.failed","channel.refresh_required","channel.disconnected","mention.received"]},"minItems":1},"channelIds":{"type":"array","items":{"type":"string","format":"uuid"}}},"required":["name","url","events"]}}}},"responses":{"201":{"description":"webhook criado (secret só aqui)","content":{"application/json":{"schema":{"type":"object","properties":{"secret":{"type":"string"},"webhook":{"$ref":"#/components/schemas/PubWebhook"}},"required":["secret","webhook"]}}}},"400":{"description":"requisição fora do contrato (problem+json)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/v1/webhooks/{id}":{"delete":{"tags":["public-webhooks"],"summary":"Remove um webhook (escopo webhooks:manage)","security":[{"apiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"webhook removido"},"401":{"description":"não autenticado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"papel insuficiente (requer ADMIN/OWNER)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"não encontrado","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate limit — aguarde e tente de novo","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/.well-known/oauth-authorization-server":{"get":{"tags":[".well-known"],"summary":"GET /.well-known/oauth-authorization-server","responses":{"default":{"description":"resposta — veja o código / TESTING.md"}}}},"/.well-known/oauth-protected-resource":{"get":{"tags":[".well-known"],"summary":"GET /.well-known/oauth-protected-resource","responses":{"default":{"description":"resposta — veja o código / TESTING.md"}}}},"/.well-known/oauth-protected-resource/mcp":{"get":{"tags":[".well-known"],"summary":"GET /.well-known/oauth-protected-resource/mcp","responses":{"default":{"description":"resposta — veja o código / TESTING.md"}}}},"/oauth/register":{"post":{"tags":["oauth"],"summary":"POST /oauth/register","responses":{"default":{"description":"resposta — veja o código / TESTING.md"}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}}}}},"/oauth/authorize":{"get":{"tags":["oauth"],"summary":"GET /oauth/authorize","responses":{"default":{"description":"resposta — veja o código / TESTING.md"}}}},"/oauth/consent/context":{"get":{"tags":["oauth"],"summary":"GET /oauth/consent/context","responses":{"default":{"description":"resposta — veja o código / TESTING.md"}}}},"/oauth/consent/approve":{"post":{"tags":["oauth"],"summary":"POST /oauth/consent/approve","responses":{"default":{"description":"resposta — veja o código / TESTING.md"}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}}}}},"/oauth/consent/deny":{"post":{"tags":["oauth"],"summary":"POST /oauth/consent/deny","responses":{"default":{"description":"resposta — veja o código / TESTING.md"}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}}}}},"/oauth/token":{"post":{"tags":["oauth"],"summary":"POST /oauth/token","responses":{"default":{"description":"resposta — veja o código / TESTING.md"}},"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}}}}},"/mcp/.well-known/oauth-protected-resource":{"get":{"tags":["mcp"],"summary":"GET /mcp/.well-known/oauth-protected-resource","responses":{"default":{"description":"resposta — veja o código / TESTING.md"}}}},"/mcp/.well-known/oauth-protected-resource/mcp":{"get":{"tags":["mcp"],"summary":"GET /mcp/.well-known/oauth-protected-resource/mcp","responses":{"default":{"description":"resposta — veja o código / TESTING.md"}}}}},"webhooks":{}}