Stacktree
Publique HTML em uma URL privada e impossível de adivinhar a partir de qualquer cliente MCP. Proteja por senha ou domínio de e-mail; substitua no lugar.
Documentação
Documentação da API Stacktree — recursos para desenvolvedores
Fonte: https://stacktr.ee/docs
[
Documentação Stacktree
](https://stacktr.ee/)
[Agentes](https://stacktr.ee/agents)
[Docs](https://stacktr.ee/docs)
[Casos de uso](https://stacktr.ee/use-cases)
[Preços](https://stacktr.ee/pricing)
[Blog](https://stacktr.ee/blog)
[Painel](https://app.stacktr.ee)
Pesquisar na documentação⌘K
Primeiros passos
- [Conectar um agente](#connect)
[Instalador CLI](#installer)
- [Claude.ai](#claude-ai)
- [Claude Code · Codex](#claude-code)
- [Arquivo de configuração MCP](#mcp-config)
- [Slack](#slack)
Referência
- [API HTTP](#api)
[Autenticação](#auth)
- [POST /sites](#post-sites)
- [POST /publish](#post-publish)
- [PUT /sites/:id](#put-sites)
- [PATCH /sites/:id](#patch-sites)
- [Listar · bruto · excluir · restaurar](#more-sites)
- [Tokens de compartilhamento](#share-tokens)
- [Chaves de API](#keys)
- [Código de dispositivo](#device-code)
- [Servidor MCP](#mcp)
- [Pagamentos de agentes](#payments)
- [Domínios personalizados](#domains)
- [OAuth](#oauth)
- [Limites](#limits)
[Erros de plano](#plan-errors)
Legível por máquina
- [llms.txt](https://stacktr.ee/llms.txt)
- [pricing.md](https://stacktr.ee/pricing.md)
- [auth.md](https://stacktr.ee/auth.md)
- [x402.md](https://stacktr.ee/x402.md)
[Registro de alterações](https://stacktr.ee/changelog) · [Blog](https://stacktr.ee/blog)
[Painel](https://app.stacktr.ee) · [Preços](https://stacktr.ee/pricing)
Documentação da API Stacktree
A primitiva de publicação para HTML feito por agentes.
privado por padrão nativo de MCP substituição no local
Entregando isto a um agente de programação? Passe a ele a especificação OpenAPI e pronto: api.stacktr.ee/openapi.json para a API HTTP. Sem conta? POST /sites sem chave cria uma página que dura 24 horas, e POST /publish cria uma permanente por US$ 0,50 via x402, ambas em api.stacktr.ee. A mesma publicação paga também está na porta de entrada: agents.stacktr.ee/openapi.json. Servidor MCP: https://api.stacktr.ee/mcp. Índice legível por máquina: stacktr.ee/llms.txt.
Para um passo a passo guiado com sua chave de API real embutida nos trechos de código, abra app.stacktr.ee/connect.
Conectar um agente
Entregue ao agente
Se você tem um agente aberto, entregue isto a ele e ele faz o resto — instala, verifica e aprende a superfície de ferramentas:
`Fetch and follow the setup instructions at https://stacktr.ee/prompt.md`
Funciona em qualquer agente que consiga buscar uma URL. As instruções são Markdown simples em stacktr.ee/prompt.md — leia-as antes de executá-las, se quiser.
npx stacktree-install — recomendado
Um comando configura todos os agentes de uma vez — Claude Code, Cursor, Codex, OpenCode, Amp — e instala a skill stacktree-publish para o Claude:
`npx stacktree-install`
Ele faz login via um código de uso único em app.stacktr.ee/connect/cli e gera uma chave de API automaticamente; passe uma chave existente como argumento (npx stacktree-install stk_live_…) para pular o login.
Claude.ai — conector personalizado
Sem CLI, sem copiar e colar chave de API.
- Abra [claude.ai/settings/connectors](https://claude.ai/settings/connectors) → Add custom connector.
- Cole https://api.stacktr.ee/mcp como a URL do servidor MCP remoto.
- Deixe OAuth Client ID/Secret em branco — a Stacktree se registra automaticamente via Dynamic Client Registration (RFC 7591).
- Clique em Add → o Claude.ai redireciona você para a Stacktree para fazer login e aprovar. As ferramentas ficam então disponíveis em qualquer conversa.
Claude Code · Codex — CLI
Instalação em uma linha. Sintaxe idêntica entre os dois:
`claude mcp add stacktree -- npx -y stacktree-mcp
codex mcp add stacktree -- npx -y stacktree-mcp`
Ambos expõem as mesmas 42 ferramentas (veja Servidor MCP). Defina STACKTREE_API_KEY no seu shell — gere uma em app.stacktr.ee/api-keys.
Prefere uma skill? Instala SKILL.md + script auxiliar no diretório de skills do seu agente:
`npx skills@latest add stevysmith/stacktree-skill
export STACKTREE_API_KEY=stk_live_...`
Fonte: github.com/stevysmith/stacktree-skill · a coleção completa em stacktr.ee/skills
Arquivo de configuração MCP — Cursor / Claude Desktop / Windsurf / Zed
`{
"mcpServers": {
"stacktree": {
"command": "npx",
"args": ["-y", "stacktree-mcp"],
"env": { "STACKTREE_API_KEY": "stk_live_..." }
}
}
}`
Coloque em ~/.cursor/mcp.json, ~/Library/Application Support/Claude/claude_desktop_config.json, ~/.codeium/windsurf/mcp_config.json, ou na chave context_servers nas configurações do Zed.
Slack
Adicione o app do Slack (uma aprovação; a instalação gera para o workspace sua própria identidade gratuita, sem precisar de conta Stacktree). Depois, ⋮ → Host on Stacktree em qualquer mensagem com um arquivo .html ou .md — canais ou DMs — publica um link privado de volta na conversa. Reenviar o mesmo nome de arquivo republica na mesma URL. /stacktree link migra os sites do workspace para uma conta de painel. Detalhes: stacktr.ee/slack.
API HTTP
Autenticação
Três métodos, todos resolvem para o mesmo contexto de usuário:
Authorization: Bearer stk_live_…API key
Crie em [app.stacktr.ee/api-keys](https://app.stacktr.ee/api-keys) — ou deixe um agente comprar a própria via [x402 ou MPP](#payments).
Authorization: Bearer <clerk-session-jwt>session
Token de sessão Clerk, para chamadas originadas do painel.
Authorization: Bearer <oauth-jwt>OAuth
Token de acesso de `/oauth/token`, usado por conectores personalizados.
POST/sites
Envie um único arquivo HTML/markdown ou um zip. multipart/form-data. Envios anônimos funcionam — sem cabeçalho de autenticação — e duram 24 horas.
filefilerequired
.html / .htm / .md / .zip
public_slugstring
Subdomínio público opcional; apenas autenticado.
passwordstring
Proteção por senha na entrega. Funciona em todos os planos, incluindo todas as 3 páginas do plano gratuito e publicações anônimas.
expires_in_hoursnumber | "never"
Padrão: 24h anônimo, sem expiração em plano pago. O gratuito é limitado a 7 dias. Um número acima do teto é limitado a ele e a resposta informa isso (`expiry_clamped: true`). `"never"` em um plano com teto é recusado, não encurtado silenciosamente: `409 expiry_clamped`, nada publicado, e o corpo traz `would_expire_at_iso`. Isso é intencional, porque um 201 é lido como sucesso e a permanência é repetida a uma pessoa antes que alguém verifique uma flag. Envie `accept_clamp=true` para aceitar o teto. Omita o campo por completo (ou envie-o vazio) e o padrão do próprio plano se aplica, o mesmo via MCP e via API bruta. Um valor que não seja um número de horas ou a palavra `"never"` — `"7d"`, `-5`, `"soon"` — é `400 invalid_expiry` e nada é publicado: antes era lido como "nenhum prazo solicitado", o que em um plano pago significava uma página permanente que o chamador não havia pedido.
accept_clamp"true"
Necessário apenas junto com `expires_in_hours=never` em um plano que limita o tempo de vida da página: indica que o teto é aceitável e publica.
Idempotency-Keyheader
Torna uma nova tentativa segura. Qualquer string única, de 1 a 255 caracteres ASCII visíveis. A mesma chave com o mesmo corpo dentro de 24 horas retorna a página original (mesmo id, mesma URL, mesmos tokens) com `Idempotent-Replay: true`, e não consome uma segunda página contra o limite de vida útil do plano Gratuito. Um corpo diferente sob a mesma chave é `422 idempotency_key_reused`, nunca a página antiga. Duas requisições com uma mesma chave não podem ambas publicar: a perdedora recebe `409 idempotency_key_in_progress` e deve tentar novamente. As chaves têm escopo por chamador. Em uma publicação anônima não há conta para escopo e o escopo é o endereço de rede, então a própria chave deve ser impossível de adivinhar (um UUID): uma curta é `400 idempotency_key_too_weak`, porque dois agentes atrás de um mesmo IP de escritório ambos usando uma chave de template compartilhado `"1"` de outra forma entregariam ao segundo a página do primeiro e seu `claim_token`. Se a página criada por uma chave tiver sido excluída ou consumida desde então, a nova tentativa é `409 idempotent_page_gone` em vez de um 201 para um link morto.
burn_after_read"true"
Excluir após a primeira visualização.
agentation"true"
Injetar barra de feedback na entrega.
csp_strict"false"
Desativar CSP estrito (padrão ativado). A política estrita executa scripts e bibliotecas inline do cdnjs, do CDN do Tailwind e de pacotes npm no jsDelivr ou unpkg (para que artefatos do Claude e do ChatGPT sejam renderizados como eram lá), permite Google Fonts e outros serviços de fontes, imagens do Unsplash e embeds do Loom / YouTube / Vimeo / Wistia / Descript / Streamable / Calendly, e bloqueia outros scripts e imagens remotos e qualquer requisição a outro site. Uma publicação que contenha algo que a política bloquearia ainda tem sucesso e retorna um array `warnings` informando o que não será renderizado.
e2e"true"
Tratar o envio como texto cifrado; a chave de descriptografia fica no fragmento da URL e nunca é enviada à Stacktree.
pii_checkoff | warn | block
Padrão warn (a camada MCP substitui por block).
`curl -F file=@page.html \
-F password=hunter2 \
-F expires_in_hours=72 \
-H "Authorization: Bearer stk_live_..." \
https://api.stacktr.ee/sites`
`{
"id": "…",
"url": "https://stacktr.ee/p/abc123…/",
"visibility": "unlisted",
"expires_at": 1781234567,
"expires_at_iso": "2026-06-08T12:02:47Z",
"ttl_seconds": 604800,
"expiry_clamped": false,
"expiry_ceiling_hours": 168,
"expiry_source": "plan_ceiling",
"file_count": 1,
"size_bytes": 1234,
"has_password": true,
"agentation": false
}`
POST/publish
Uma página permanente sem conta e sem chave: o pagamento é a credencial. Envie {"html": "<!doctype html>…"} como JSON. A primeira chamada retorna um 402 informando o preço (US$ 0,50) e o beneficiário; pague via x402 (USDC na Base ou Solana) ou MPP e envie a mesma requisição com o pagamento. A resposta tem o mesmo formato de POST /sites, sem expiração e com um claim_token que revisa a página gratuitamente. O corpo é verificado antes que o pagamento seja liquidado, então uma requisição que não podemos publicar nunca é cobrada. Qualquer cliente x402 faz o pagamento: um exemplo em Node está em x402.md, e todo o fluxo está em Pagamentos de agentes.
PUT/sites/:idOrSlug
Substitui os arquivos de um site no local — a URL nunca muda, e nenhuma segunda página é criada. Dois formatos de corpo: application/json com uma string html, que é o que as vias pagas já falam, ou multipart/form-data com os mesmos campos do POST (o único formato que carrega um zip, um PDF ou texto cifrado e2e). Três credenciais, uma por requisição: uma chave de conta ou token OAuth; o próprio claim_token da página enquanto ela não foi reivindicada (Authorization: Claim <claim_token>, veja abaixo); ou, em uma página paga por carteira, um desafio de carteira assinado. Qualquer outra coisa é um 401. Sites criptografados com e2e devem ser substituídos com envios multipart e2e=true (sem rebaixamento silencioso para texto simples).
`curl -X PUT https://api.stacktr.ee/sites/my-deck \
-H "Authorization: Bearer stk_live_..." \
-H "Content-Type: application/json" \
-d '{"html":"<!doctype html><h1>v2</h1>"}'`
Os campos opcionais são os mesmos de qualquer forma: expected_updated_at (recusar a escrita se a página mudou por baixo de você), pii_check, e em multipart e2e. Em JSON, filename substitui uma página publicada como ativo de máquina em seu próprio caminho em vez de realocá-la para index.html.
PATCH/sites/:idOrSlug
Atualiza configurações sem reenviar arquivos. Corpo JSON.
passwordstring | null
Define ou remove a proteção por senha. Funciona em todos os planos; remover uma sempre funciona também.
expires_in_hoursnumber | null
`null` (ou a string `"never"`) cancela a expiração em um plano pago. Em um plano com teto é recusado (`409 expiry_clamped`, nada do PATCH aplicado) em vez de silenciosamente virar 7 dias; adicione `accept_clamp: true` para aceitar o teto. Um número acima do teto é limitado, com `expiry_clamped: true` na resposta. Números podem ser enviados como strings (`"24"`) e significam o mesmo aqui e na publicação; qualquer coisa que não seja legível como horas nem `"never"` é `400 invalid_expiry` e nada é alterado.
allowed_email_domainstring | null
Os visualizadores verificam um e-mail antes que a página seja renderizada. Domínios e/ou endereços individuais, separados por vírgulas (`"acme.com, jane@globex.com"`): um domínio admite todos nele, um endereço apenas aquela pessoa. Apenas planos pagos; o Gratuito recebe `402 plan_viewer_gate_not_available`.
public_slugstring | null
Reivindica ou libera um subdomínio público.
agentation · burn_after_read · csp_strictboolean
Alterna os comportamentos de entrega documentados em POST.
`curl -X PATCH \
-H "Authorization: Bearer stk_live_..." \
-H "Content-Type: application/json" \
-d '{"agentation": true, "expires_in_hours": null}' \
https://api.stacktr.ee/sites/my-deck`
Listar · buscar · bruto · excluir · restaurar
GET/sites
Liste seus sites. Páginas que expiraram ou foram excluídas permanecem na lista em vez de desaparecer: elas carregam deleted_at, delete_reason e restorable_until. Verifique deleted_at antes de entregar a alguém um url: uma linha que o tenha definido é um link morto.
GET/sites/:idOrSlug
Um site, com manifesto de arquivos + `preview_url` absoluto.
GET/raw/:token
A página como Markdown (títulos, listas, tabelas e links mantidos) para realimentar um agente. Recusa páginas com restrição; para essas, use o endereço próprio da página abaixo.
GET{page URL} com `Accept: text/markdown`
O mesmo link que uma pessoa recebeu, lido como Markdown. Também em `{page URL}/_page.md`, e anunciado por um cabeçalho `Link: rel="alternate"` e um `<link>` no head. Por ser o endereço próprio da página, um código de acesso ou restrição de espaço se aplica exatamente como no HTML: um agente envia o código de acesso como autenticação HTTP Basic e recebe o Markdown.
POST{page URL}/_ask
"Pergunte a esta página", onde o proprietário ativou o recurso: `{ "q": "..." }` na entrada, um fluxo de JSON delimitado por quebras de linha na saída (chunks `{"t": text}`, `{"c": cited passage}`, depois `{"done": true, "grounded": bool}`). As respostas vêm apenas da página e dizem isso quando a página não cobre a pergunta. Também exposto a agentes de navegação como a ferramenta WebMCP `stacktree_ask_page`. O que os leitores perguntam é listado para o proprietário em `GET /sites/:idOrSlug/questions`.
DELETE/sites/:idOrSlug
Derruba a página agora: o link fica morto para todos que o possuem e a vaga do plano é liberada imediatamente. O conteúdo é mantido por 30 dias, e `restorable_until` na resposta é o prazo após o qual ele é destruído definitivamente. No plano Free, isso não devolve uma vaga de página vitalícia, porque esse limite conta publicações, não páginas ativas. Excluir uma página que já está fora do ar retorna `already_deleted: true` em vez de um erro.
POST/sites/:idOrSlug/restore
Recoloca uma página excluída ou expirada na mesma URL, com o mesmo id, token, slug e histórico de leituras, para que os links já enviados voltem a funcionar. Esta, e não outra publicação, é a resposta para um `409 site_deleted`: republicar gera uma URL diferente e gasta outra página vitalícia, enquanto um restore não gasta nenhuma. É um resgate, não uma renovação, então leia `expires_at_iso` e `restored_for` na resposta: uma página que esgotou o tempo em um plano com teto de expiração volta por 48 horas (`restored_for: "grace"`, e `restore_grace_hours` informa o número), não um novo ciclo vitalício completo. Uma página excluída pelo proprietário mantém o prazo que já tinha (`"kept"`), inclusive nenhum prazo. 404 quando os 30 dias terminam, e para uma remoção por denúncia, que nunca é restaurável.
Os números de visualização dependem do plano. `GET /sites` e `GET /sites/:idOrSlug` retornam `metrics_locked: true`, com `view_count`, `unique_viewers` e `last_viewed_at` como `null`, quando o plano não tem métricas de visualização (anônimo e Free). Um booleano `opened` vem em todos os planos, para que você saiba que alguém leu a página sem ver quantos leram. A ocultação é feita no servidor, então um agente que lê este JSON vê exatamente o que o painel vê.
Tokens de compartilhamento
`POST https://api.stacktr.ee/sites/:idOrSlug/share-tokens { "label": "alice", "max_uses": 5, "expires_in_hours": 168 }
GET https://api.stacktr.ee/sites/:idOrSlug/share-tokens
DELETE https://api.stacktr.ee/share-tokens/:tokenId`
Retorna uma URL com ?t=… anexado. Ignora a restrição de senha quando válido; revogável por token; contador opcional de usos máximos e expiração.
Feedback
`GET https://api.stacktr.ee/sites/:idOrSlug/feedback
POST https://api.stacktr.ee/feedback/:id/resolve { "note": "fixed the header spacing" }
POST https://api.stacktr.ee/sites/:idOrSlug/feedback/apply { "ids": ["k3m9x2"] }
DELETE https://api.stacktr.ee/feedback/:id`
Anotações de visualizadores deixadas pela barra de ferramentas Agentation na página (ative com agentation no upload ou no PATCH). Cada item traz o comentário mais o elemento anotado, o texto selecionado, a intenção e a severidade. Não resolvidos primeiro. O ciclo: ler o feedback → corrigir a página com update_site (mesma URL) → resolver.
Edições sugeridas: com os comentários da página ativados, um revisor pode selecionar palavras e sugerir uma nova redação. O item tem intenção edit, as palavras em anchor.exact e a nova redação em suggested_text, além de apply_check indicando se ela pode ser aplicada na página como está agora. POST /sites/:idOrSlug/feedback/apply troca as palavras como texto simples, apenas onde o revisor deixou a sugestão e apenas quando elas ocorrem ali uma única vez, mantém o conteúdo anterior como uma versão (version na resposta a restaura) e resolve cada uma com uma nota que o revisor vê. O que não puder ser aplicado volta em not_applied com o motivo.
Mais de um revisor: por padrão, cada revisor vê apenas os próprios comentários. PATCH /sites/:idOrSlug com {"comments_shared": true} (ou a chave em comentários na tela da página) permite que todos a quem a página dá acesso vejam o que os outros comentaram e sugeriram, somente leitura. Um comentário deixado a partir de uma página aberta em um link de compartilhamento nomeado carrega o nome desse link, em from no list_feedback, no e-mail do proprietário e, quando compartilhado, para os outros revisores. Desativado de novo, cada revisor volta a ver apenas os próprios.
Parágrafos inteiros: quando as palavras selecionadas estão em um único parágrafo, item ou título, o revisor pode, em vez disso, sugerir um novo parágrafo depois dele (intenção add_after, o novo texto em suggested_text) ou removê-lo (intenção remove, sem suggested_text). Para ambos, anchor.exact é o texto completo do parágrafo como eles o viram. A mesma rota de aplicação os recebe: uma adição vai logo após o parágrafo no mesmo tipo de elemento (após um título, um parágrafo simples), e uma remoção só acontece enquanto o parágrafo ainda diz o que o revisor viu.
Placeholders e limites: na caixa de sugestão, o revisor é avisado quando sua redação deixa de fora um placeholder ou campo de mesclagem ([brand phone], {first_name}, {{first_name}}, *|FNAME|*, %%FirstName%%), altera palavras dentro de um elemento marcado com data-stacktree-keep, ou torna o texto de um elemento marcado com data-stacktree-max="160" mais longo que essa quantidade de caracteres, o que a caixa conta enquanto ele digita. O mesmo aparece como apply_check.warnings antes de você aplicar, e em cada item de applied depois. Eles avisam e nunca recusam. Antes de um comentário ou sugestão ser publicado, a caixa também o verifica em busca de dados pessoais que a página ainda não mostra (um endereço de e-mail, um número de telefone ou documento, uma data de nascimento) e pede confirmação ao revisor.
Aprovação e o texto aprovado
`POST https://api.stacktr.ee/sites/:idOrSlug/approvals { "approvers": ["Priya Shah <priya@acme.com>", "Tom Reid"], "note": "..." }
GET https://api.stacktr.ee/approvals/:id
GET https://api.stacktr.ee/sites/:idOrSlug/approved-copy ?approval_id=apr_...
POST https://api.stacktr.ee/sites/:idOrSlug/approved-copy/check { "text": "...", "sections": { "follow-up-1": "..." } }`
Uma solicitação de aprovação dá a cada pessoa um link nomeado para a página, onde ela aprova ou pede alterações, para a página inteira ou seção por seção (cada <h2> é uma seção, ou marque-as com data-stacktree-section). Cada resposta registra quem, quando e a versão exata que leu. É um registro da decisão, não uma assinatura eletrônica legal.
GET /sites/:idOrSlug/approved-copy devolve o que foi aprovado como palavras: por seção, quem aprovou e quando, se essa redação ainda é a que está na página, e text, as palavras aprovadas. Uma seção é aprovada quando todos na solicitação aprovaram as mesmas palavras. Quando ela mudou depois, as palavras aprovadas são lidas do histórico de versões da página e from nomeia a versão; a redação que está na página agora nunca é reportada como aprovada. Seções ainda não aprovadas voltam com seu status e sem texto. markdown é o mesmo que um documento com um carimbo de versão, para um arquivo ou um pull request.
POST /sites/:idOrSlug/approved-copy/check compara o texto que você tem com essas palavras: text (um arquivo-fonte, um template, um e-mail renderizado) é pesquisado em busca de cada seção aprovada, e sections compara uma seção por vez. Cada um retorna exact, differs (com onde as palavras divergem) ou missing. Palavras são comparadas, não bytes: tags, entidades, escapes de código-fonte, espaços em branco e aspas curvas são normalizados, e a resposta lista cada regra em normalized. Placeholders como {first_name} são comparados como escritos. Até 500 KB de texto por chamada. No MCP, estes são get_approved_copy e check_approved_copy, em /mcp-labs por enquanto.
Reações e engajamento
`GET https://api.stacktr.ee/sites/:idOrSlug/reactions { counts, total, reactors, messages }
GET https://api.stacktr.ee/sites/:idOrSlug/engagement { sessions, median_active_seconds, avg_scroll, read_to_end_pct, buckets }`
Como uma página se saiu, lido pelo proprietário. Reações: ative a barra de reações na página por site (Configurações do painel) e os visualizadores reagem com um emoji ou deixam uma nota privada curta, sem conta. Engajamento (Studio e acima): métricas de leitura agregadas e sem PII: tempo típico na página, profundidade de rolagem, taxa de leitura até o fim e um mapa de calor de atenção em 10 faixas de permanência por profundidade da página. Sem gravação, sem replay de sessão. Ambos alimentam o sino de atividades do painel e o resumo diário opcional por e-mail.
Chaves de API
`POST https://api.stacktr.ee/api-keys { "label": "claude desktop" }
GET https://api.stacktr.ee/api-keys
DELETE https://api.stacktr.ee/api-keys/:id`
Código de dispositivo — obtenha uma chave em um agente sem navegador
OAuth 2.0 Device Authorization Grant (RFC 8628). É o que o npx stacktree-install executa, e é chamável diretamente por qualquer agente: o agente imprime uma URL e um código curto, uma pessoa aprova no próprio dispositivo, e o agente faz polling até uma chave retornar. Use quando a conta já pertence a uma pessoa — um agente que não tem humano a quem perguntar deve comprar a própria chave via x402 em POST https://api.stacktr.ee/provision.
`POST https://api.stacktr.ee/api-keys/device-code { "client_hint": "my-agent" }
→ { device_code, user_code, verification_url, verification_url_complete, interval, expires_in }
# print verification_url_complete for the human, then poll every "interval" seconds:
POST https://api.stacktr.ee/api-keys/device-code/poll { "device_code": "…" }
→ { "status": "pending" } keep polling
→ { "status": "authorized", "api_key": "stk_live_…" } store it; shown once
→ { "status": "denied" | "expired" } stop`
Sem autenticação em nenhuma das chamadas. Os códigos duram 10 minutos, a aprovação da pessoa gera a chave na conta dela, e a chave é entregue exatamente uma vez — um polling que a perde significa reautorizar. Limitado por IP. A chave stk_live_ resultante funciona tanto na API REST quanto no MCP.
Servidor MCP
Servidor MCP Streamable HTTP em https://api.stacktr.ee/mcp (spec 2025-11-25), expondo 42 ferramentas. Duas credenciais, ambas aceitas no mesmo endpoint:
- Authorization: Bearer stk_live_… — uma chave de API. Sem navegador, sem dança OAuth, nada a registrar: a mesma chave que opera a API REST opera o MCP. Este é o caminho para um agente não assistido, e para qualquer cliente que só consiga enviar um cabeçalho estático. Um agente sem nenhuma chave pode comprar uma via [x402](#payments) em POST https://api.stacktr.ee/provision, ou vincular-se à conta existente de uma pessoa com o [fluxo de código de dispositivo](#device-code) abaixo.
- OAuth 2.1 + Dynamic Client Registration — para conectores que agem em nome de uma pessoa autenticada (claude.ai, Cursor e afins). Nada a pré-registrar; veja [OAuth](#oauth).
Cookies de sessão deliberadamente NÃO são aceitos aqui, para que um navegador não possa ser induzido a operar o MCP entre sites. Envie exatamente uma credencial.
Resultados: uma chamada bem-sucedida retorna seus dados como structuredContent e como texto JSON, com o que fazer em seguida em linhas // depois. Essas linhas também estão em structuredContent.agent_notes, para clientes que mostram ao modelo apenas os dados estruturados. Uma ferramenta que declara um esquema de saída fechado (get_site, get_content, get_passcode) não tem notas ali.
`curl -X POST https://api.stacktr.ee/mcp \
-H "Authorization: Bearer stk_live_..." \
-H "Accept: application/json, text/event-stream" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'`
Ferramentas (42)
publish_html→ { id, url, slug, unlisted_token, … }
Transforma HTML em um link que uma pessoa pode abrir no navegador. Ele só cria uma nova página: nunca altera nem exclui uma que já existe (update_site substitui uma página no lugar).
update_site→ { id, url, file_count, size_bytes }
Substitui o HTML de um site existente no lugar. A URL permanece a mesma, então todos a quem você já a enviou veem a nova versão sem receber nada.
delete_site→ { ok, restorable_until, already_deleted }
Derruba uma página. O link fica morto imediatamente para todos que o possuem, e o conteúdo é mantido por 30 dias: restore_site a recoloca na mesma URL, com o mesmo id, token, slug e histórico de leituras, a qualquer momento nessa janela.
restore_site→ { ok, id, url, expires_at, … }
Restaura uma página que foi excluída ou expirou nos últimos 30 dias.
claim_site→ { ok, id, expires_at, expires_at_iso, … }
Adota uma página que foi publicada sem conta na conta à qual esta conexão está autenticada.
set_password→ { ok }
Define ou limpa (null) o código de acesso que os visualizadores digitam para abrir uma página.
set_expiry→ { ok, expires_at, expires_at_iso, ttl_seconds, … }
Define expiração em horas a partir de agora, ou null para nunca. Um número maior do que o plano permite é reduzido ao teto e a resposta informa isso (expiry_clamped: true).
create_share_link→ { id, url, label, expires_at, … } Cria um link separado para uma das páginas do usuário, destinado a um único destinatário, rotulado com quem é.
list_share_links→ { links, metrics_locked }
Lista os links de compartilhamento de uma das páginas do usuário: o id, o rótulo, a expiração, o limite de uso e se foi revogado de cada link, com quantas vezes foi aberto e quando foi aberto pela última vez.
revoke_share_link→ { ok }
Elimina um link de compartilhamento. A página e todos os outros links continuam funcionando — é assim que você corta o acesso de um destinatário sem reemitir nada para os demais.
set_client_feedback→ { ok }
Altera como as pessoas para quem você envia uma página podem responder nela, sem conta. comments: elas selecionam palavras, ou clicam em uma imagem, vídeo ou seção, e deixam um comentário que só o proprietário vê (leia-os com list_feedback, corrija a página com update_site e então…
set_email_gate→ { ok }
Restringe o acesso do visualizador a um domínio de e-mail, a pessoas nomeadas, ou ambos.
list_sites→ { sites }
Lista os sites de propriedade do usuário autenticado, com os atualizados mais recentemente primeiro.
get_me→ { clerk_user_id, auth_method, plan, email, … }
Em nome de quem esta conexão está publicando, e o que o plano dela realmente permite.
list_client_spaces→ { spaces }
Lista os espaços de cliente desta conta: slug, nome, page_count, última atividade, hostname (o endereço próprio do espaço, por exemplo acme.theiragency.com, quando um está conectado) e portal_enabled (se o espaço serve um portal de cliente gerado nesse endereço).
set_client→ { ok }
Arquiva um site existente em um espaço de cliente (por nome ou slug, criado automaticamente), ou passe client: null para desanexá-lo como página flutuante.
create_client_space→ { ok, space }
Cria um espaço de cliente antes que qualquer coisa seja publicada nele. Raramente necessário: publish_html com um argumento client cria o espaço automaticamente seguindo as mesmas regras de capitalização e slug, então use isto apenas quando o usuário estiver configurando um cliente antes do trabalho.
update_client_space→ { ok }
Renomeia um espaço de cliente, arquiva ou desarquiva, ou define o gate de visualizador que cobre todas as páginas do espaço.
delete_client_space→ { ok, detached_pages }
Exclui um espaço de cliente. As páginas arquivadas nele NÃO são excluídas: elas se desanexam como páginas flutuantes e continuam funcionando em suas URLs existentes, então o trabalho entregue permanece acessível.
get_design_guide→ { guide, version }
Busca o guia de design da casa Stacktree para melhorar uma página publicada.
get_site→ { html }
Lê o código-fonte HTML atual de um site que você possui, para que você possa editá-lo e chamar update_site para alterá-lo no local.
get_content→ { content, format }
Lê o conteúdo de uma página de volta. format "html" (o padrão) retorna o index.html armazenado exato, byte a byte, que é a única forma que você pode editar e entregar ao update_site; format "text" retorna a mesma página reduzida a texto simples — sem marcação, CSS, scripts ou…
get_passcode→ { passcode }
Mostra o código de acesso definido em uma página que esta conta possui, para que possa ser enviado ao cliente novamente.
get_activity
Alguém abriu uma página? Responde para uma página que esta conta possui: se foi aberta e por quem (links de compartilhamento nomeados), se o assistente de IA do próprio leitor a leu, comentários abertos, se está ativa ou expirada, e quando deixa de funcionar, além de `said`…
list_versions→ { versions }
Lista versões anteriores de uma página que esta conta possui, mais recentes primeiro.
restore_version→ { ok, restored, new_version, size_bytes, … }
Restaura uma versão anterior de uma página na mesma URL, após uma atualização ruim.
list_feedback→ { feedback }
Lê os comentários dos visualizadores deixados em um site, não resolvidos primeiro.
resolve_feedback→ { ok }
Marca um item de feedback como tratado após corrigir a página. Passe o id do item de feedback de list_feedback, e uma nota curta descrevendo o que você alterou: o revisor que deixou o comentário a vê na página.
link_wallet→ { ok }
Vincula sua carteira a uma conta Stacktree para que as páginas que você publica sejam de propriedade dela — e adota as que você já publicou.
whos_it_for→ { name, link, recorded }
Registra para quem uma página publicada se destina, por nome, e recebe de volta o link próprio dessa pessoa para entregar.
unopened_pages→ { items }
As páginas, e as pessoas nomeadas, que não abriram o que lhes foi enviado uma hora após a publicação, mais recentes primeiro.
get_revision_brief→ { brief }
Tudo o que voltou em uma página, como o briefing para sua próxima versão: os pedidos de alteração do revisor, suas perguntas agrupadas por tema, suas aprovações, as notas que o proprietário deixou em sua própria página como lista de correções, cada uma com a passagem ou imagem exata a que se…
publish_revision→ { id, url, resolved, skipped, … }
Publica a nova versão de uma página que tem feedback e resolve os comentários que ela responde, em uma única etapa.
resolve_comments→ { resolved, skipped, open_left }
Resolve vários comentários em uma página em uma única chamada, cada um com uma nota curta, além de uma mensagem opcional de volta ao revisor.
request_approval→ { approval_id, site_id, page_url, approvers, … }
Obtém o "sim" de uma pessoa nomeada em uma página. Publica o HTML (ou usa uma página já publicada) com um painel de aprovação, e cria um link privado por aprovador que carrega o nome dele.
get_approval_status→ { state, summary, approvers }
Onde está uma solicitação de aprovação. state é "approved" apenas quando todos os aprovadores aprovaram a versão que está no ar agora (a página inteira, ou cada seção); "changes_requested" quando alguém pediu uma alteração; caso contrário, "pending".
list_responses→ { total, forms, responses }
O que os leitores enviaram pelos formulários de uma página. Um formulário no HTML publicado marcado com <form data-stacktree-form="name"> envia para a própria página; apenas os campos que o formulário declara são mantidos.
resolve_addressed_notes→ { resolved, ids }
Após update_site: resolve as notas abertas que a nova versão parece responder (comentários cujas palavras desapareceram, seções que foram solicitadas a mudar e foram editadas), com uma linha que cada revisor vê ao lado da própria nota.
open_html_file→ { file }
O visualizador Stacktree para um arquivo .html que o usuário abre no ChatGPT desktop: visualize-o, publique-o em um link privado, adicione um código de acesso, envie-o a um cliente nomeado e mantenha a página sincronizada conforme o arquivo muda.
stacktree_pages→ { sites }
Lista as páginas que você publicou no Stacktree, mais recentes primeiro, com seus links, visualizadores e expiração.
publish_card_status→ { card }
Usado pelo cartão de publicação do Stacktree no chat para se atualizar: o link da página, se e por quem foi aberta, seu código de acesso, expiração e cliente.
publish_card_open→ { url, expires_at }
Usado pelo cartão de publicação do Stacktree no chat: um link de pré-visualização do proprietário de curta duração para uma página, que a abre sem contar como visita e sem pedir seu código de acesso.
Padrões MCP com privacidade em primeiro lugar
Agentes agem de forma autônoma sem um humano revisando cada flag. A camada MCP aplica padrões mais rígidos que a API bruta:
- Expiração ciente do plano. Omitir expires_in_hours usa o padrão do plano — 24h anônimo, 7 dias no Free, sem expiração em um plano pago. Passe "never" para permanência, que um plano pago honra e um plano limitado recusa (409 expiry_clamped) em vez de silenciosamente virar 7 dias; accept_clamp aceita o teto. Seja o que for que você receba, expires_at_iso e ttl_seconds estão na resposta.
- Verificação de segurança em modo block (API bruta: warn) — bloqueia a publicação acidental de dados pessoais ou segredos. A verificação protege seu conteúdo; nada é coletado ou armazenado.
- URL de token não listada, CSP estrita (CDNs de bibliotecas, Google Fonts e embeds de vídeo nomeados permitidos; outros scripts e imagens remotos bloqueados), X-Robots-Tag: noai — os mesmos padrões da API bruta.
WebMCP (no navegador)
O painel registra os mesmos verbos em document.modelContext onde o navegador oferece suporte (origin trial do Chrome), para que um agente no navegador ajudando um humano conectado possa chamá-los sem uma chave de API. Como e por quê.
Quer o mesmo padrão no seu próprio app? A paleta e o registro WebMCP são construídos sobre o agentk, nossa extensão cmdk de código aberto: defina ferramentas uma vez como JSON Schema, humanos recebem formulários gerados, agentes recebem os schemas.
Pagamentos de agentes
Um agente paga sem humano e sem conta, de duas formas: por publicação, ou com uma chave persistente comprada uma vez. O pagamento por publicação aceita x402 (USDC na Base ou Solana) e MPP (USDC.e na Tempo na porta de entrada, o método evm na Base); a chave persistente é x402 (USDC na Base ou Solana) ou MPP na Base. Sem gas para o pagador de qualquer forma. Leia o array accepts do 402 em vez de codificar um trilho fixo. Versão legível por máquina: x402.md.
Pagar por publicação (sem chave)
O caminho mais simples, quando não há chave e nem humano para criá-la. Faça POST do seu HTML para api.stacktr.ee/publish ou para a porta de entrada em agents.stacktr.ee/api/publish, receba um 402, pague $0.50 via x402 (USDC na Base ou Solana) ou MPP, e a página é publicada em um link privado com a URL na resposta. Nenhuma etapa de provisionamento. Leia o array accepts do 402 em vez de codificar um trilho fixo: a Base é sempre a primeira entrada, e o requisito difere por rede. Revisões gratuitas depois disso são sem chave para um pagador EVM (a carteira que pagou assina por elas) e baseadas em claim-token para um pagador Solana. Os endpoints estão listados no x402scan e no mppscan; especificações em api.stacktr.ee/openapi.json e agents.stacktr.ee/openapi.json, com um registro gratuito de prova de serviço em api.stacktr.ee/.well-known/x402-service.
Chave persistente: provisione uma vez, depois pague conforme o uso
GET/provision
Lista os trilhos de pagamento aceitos.
POST/provision
402 → pague $1.00 via x402 (USDC na Base ou Solana) ou MPP → chave `stk_live_` persistente, sem conta. A chave carrega limites do nível gratuito: 3 páginas no total, cada uma expirando após 7 dias, sem gates de e-mail (códigos de acesso funcionam). Remova-os com um unlock abaixo.
GET/unlock
O catálogo à la carte: tornar permanente $5 por página, domínio personalizado $5/30d, limites maiores $25/30d.
POST/unlock
402 → pague → direito ao recurso na sua chave.
POST/pay/sessions
Sem carteira? Retorna um link de pagamento + QR de terminal. Um humano paga por cartão em dois toques — ou um agente com um cartão virtual aprovado por humano (Stripe link-cli, contas US Link) preenche o Stripe Checkout padrão sozinho. Faça polling em `GET /pay/sessions/:code/poll`.
Pagar acima do preço em uma sessão de pagamento (até $20) deixa um saldo pré-pago na chave do qual ações pagas posteriores sacam silenciosamente. Saldos nunca expiram e são reembolsáveis mediante solicitação.
Reivindique o que um agente publicou
A carteira que paga na porta de entrada é registrada com cada página, então ela também funciona como um ticket de reivindicação. Um humano pode vincular essa carteira a partir do painel (gere um código, o agente o assina), ou o agente pode se autovincular com a ferramenta MCP link_wallet. Toda página que a carteira publicou então passa a ser de propriedade e gerenciável.
Atualize com o próprio claim token da página (sem chave, qualquer chain)
Toda página não reivindicada carrega um claim_token, retornado na sua resposta de publicação. Até que a página seja reivindicada, esse token é a credencial de atualização da página: PUT /sites/:id com Authorization: Claim <claim_token> e um corpo JSON {"html": "…"} (multipart também funciona). Mesma URL, revisões gratuitas, sem conta, sem carteira e sem assinatura, então é o caminho tanto para um pagador Solana quanto para uma publicação anônima gratuita. Ele cobre exatamente o conteúdo daquela página: não a reivindicação, não a exclusão, não as configurações, não nenhuma outra página. A reivindicação rotaciona o token para a conta e o cabeçalho deixa de funcionar; ele também é recusado quando a página expira. Trate o token como sendo tão sensível quanto a página: quem o detém pode substituir o conteúdo por trás de um link que você já enviou.
PUT/sites/:idOrSlug
Manter uma página anônima: o link de manutenção, ou e-mail
Uma página anônima não pertence a ninguém até ser reivindicada, e a maioria delas é publicada por um agente cujo humano nunca vê a resposta. Toda resposta de publicação anônima carrega keep_url: entregue-o à pessoa para quem você publicou. Ele abre uma página que informa quando a página deixa de funcionar e oferece as duas formas de mantê-la, sem necessidade de login para lê-la. Abri-la não mantém nada até que essa pessoa escolha uma opção. Ela também marca aquele navegador para que a própria página mostre a ele, e somente a ele, um botão Manter até que ela expire. Ela carrega o token de reivindicação, então compartilhe url, nunca keep_url.
Ou envie por e-mail: POST /sites/:id/keep com {"claim_token": "…", "email": "…", "consent": true} envia para esse endereço o link de reivindicação e o prazo da página, e um lembrete quando a página estiver acabando. Pergunte à pessoa primeiro e envie consent: true somente quando ela disser sim. Sem conta e sem chave: o claim_token é a credencial. O endereço é armazenado na página, usado apenas para esses dois e-mails, e apagado pela reivindicação; uma página pode enviar no máximo três e-mails de manutenção. Uma reivindicação continua funcionando durante os 30 dias em que uma página expirada é mantida, e reivindicar uma página expirada a traz de volta no mesmo endereço, então uma página que expirou em 24 horas não se perde até o fim do período de retenção.
POST/sites/:idOrSlug/keep
Atualizar com a própria carteira (sem chave)
A carteira pagadora também é a credencial de atualização da página — sem reivindicação, sem conta, sem chave de API. POST /wallet-auth/challenge com {"wallet":"0x…"}, personal_sign a mensagem retornada, depois PUT /sites/:id com Authorization: Wallet challenge=…,sig=0x… e o corpo JSON. Mesma URL, revisões gratuitas; os desafios são de uso único com TTL de 5 minutos. Apenas carteiras EVM EOA por enquanto (sem carteiras de contrato inteligente).
POST/wallet-auth/challenge
Tudo o que uma carteira pagou
Uma publicação paga a partir de uma carteira retorna next.receipt_url, um link estável para uma página que lista todas as páginas que aquela carteira pagou: título, URL, data, a liquidação on-chain, se o token de reivindicação ainda está ativo, a requisição exata que revisa cada uma, e um botão para movê-las todas para uma conta. O link é a credencial e o endereço do pagador nunca é aceito em seu lugar: endereços são públicos on-chain, então uma página indexada por endereço tornaria os títulos privados das páginas de todo cliente x402 enumeráveis a partir de um explorador de blocos. Ele não é retornado por POST /wallet-auth/challenge pelo mesmo motivo, e está deliberadamente ausente do desafio 402, que os rastreadores indexam e republicam.
Domínios personalizados
Planos pagos (Solo 1 domínio, Studio 10, Firm 25), ou o desbloqueio x402 custom_domain. Traga seu próprio hostname (docs.acme.com e outros), aponte um CNAME para nossa origem de fallback do Cloudflare for SaaS, comprove a propriedade via registro TXT, e o tráfego para esse hostname serve seu site via HTTPS.
POST/custom-domains
`curl -X POST https://api.stacktr.ee/custom-domains \
-H "Authorization: Bearer stk_live_..." \
-H "Content-Type: application/json" \
-d '{"hostname":"docs.acme.com","site_id":"abc123"}'`
A resposta inclui um verify_token e os registros DNS que você ainda precisa adicionar. Dois para um hostname novo; se uma reivindicação de domínio pai já verificada o cobre, instructions é null (subdomínio: o CNAME curinga faz o roteamento) ou somente CNAME (o próprio nome reivindicado, cuja propriedade já está comprovada):
`{
"hostname": "docs.acme.com",
"site_id": "abc123",
"verified": false,
"instructions": {
"cname": { "name": "docs.acme.com", "value": "proxy.stacktr.ee", "type": "CNAME" },
"txt": { "name": "_stacktree-verify.docs.acme.com", "value": "verify_", "type": "TXT" }
}
}`
POST/custom-domains/:hostname/verify
Depois de adicionar os registros DNS, chame verify. Fazemos a consulta DNS do registro TXT; em caso de correspondência, registramos o hostname no CF for SaaS e o provisionamento de SSL começa (~60 s).
`curl -X POST https://api.stacktr.ee/custom-domains/docs.acme.com/verify \
-H "Authorization: Bearer stk_live_..."`
Atenção — CNAME somente DNS. Se o seu DNS está no Cloudflare, o CNAME deve ser definido como somente DNS (nuvem cinza), não Proxied (laranja). Um CNAME com proxy faz o Cloudflare reivindicar o hostname para a sua própria zona e o roteamento SaaS do Stacktree nunca vê o SNI.
Reassociar ou excluir
`PATCH https://api.stacktr.ee/custom-domains/:hostname # { "site_id": "..." } — re-bind
DELETE https://api.stacktr.ee/custom-domains/:hostname # unregister + drop row`
Liste seus domínios com GET https://api.stacktr.ee/custom-domains. Linhas não verificadas são removidas automaticamente após 7 dias.
O design do seu próprio portal
O portal de um espaço de cliente se constrói a partir das páginas arquivadas nele. Você pode substituí-lo inteiramente pelo seu próprio design e manter a lista de documentos atualizada.
A forma mais rápida é fazer sua IA projetá-lo. O cartão Portal do espaço no painel tem um botão Copiar o prompt: o prompt já nomeia o cliente, o endereço, seu kit de marca e a página sobre a qual publicar, então você o cola no Claude, ChatGPT ou Cursor e adiciona a aparência que deseja. Com o conector Stacktree, a IA publica o design sozinha; sem ele, cole o arquivo que ela fornecer no editor da página.
Publicar HTML que carrega o marcador de documento abaixo em um portal que ainda está se construindo torna o portal seu no mesmo passo, com a lista ativa desde o primeiro momento. Qualquer outra coisa enviada a esse portal é recusada, porque a próxima alteração no espaço a sobrescreveria. Para editar manualmente sem o marcador, assuma o portal primeiro (o cartão Portal, Avançado, Assumir o HTML, ou POST https://api.stacktr.ee/spaces/:idOrSlug/portal/customize); ele então é seu para substituir com PUT https://api.stacktr.ee/sites/:id.
Para manter a lista de documentos atualizada dentro do seu próprio design, coloque este marcador onde a lista deve ir:
`<!-- stacktree:documents -->`
Toda vez que uma página é publicada no espaço, arquivada, renomeada, reordenada ou removida, a lista entre o marcador e seu <!-- /stacktree:documents --> de fechamento é reescrita. O marcador de fechamento é adicionado para você na primeira vez. Nada fora dos marcadores é jamais alterado, e um portal sem marcador nunca é tocado. Mais dois marcadores preenchem um número: <!-- stacktree:count --> (quantos documentos) e <!-- stacktree:updated --> (a data do mais recente).
A lista é HTML simples sem estilos próprios, então seu CSS decide como ela aparece:
`<ul class="st-docs">
<li class="st-doc"><a href="https://portal.acme.com/june-report">
<img class="st-thumb" src="/og/<token>.png" alt="">
<span class="st-title">June report</span>
<span class="st-desc">The description you set</span>
<time class="st-date" datetime="2026-06-30">Jun 30</time>
</a></li>
</ul>`
Ela segue a ordem que você escolheu para o portal. Uma página com seu próprio código de acesso em um portal sem um é listada como li.st-doc.st-private, com um span.st-lock e sem descrição ou prévia. Um espaço vazio mostra p.st-docs-empty. Devolver o portal (POST https://api.stacktr.ee/spaces/:idOrSlug/portal/reset) substitui seu design pelo gerado.
OAuth (autores de conectores personalizados)
Para implementadores de hosts MCP — se você está usando um cliente mantido (Claude.ai, Cursor, etc.) pule esta seção.
Descoberta
`GET https://api.stacktr.ee/.well-known/oauth-authorization-server
GET https://api.stacktr.ee/.well-known/oauth-protected-resource`
Ambos retornam documentos de metadados padrão RFC 8414 / RFC 9728.
Fluxo
OAuth 2.1 com PKCE (S256 obrigatório) e Registro Dinâmico de Cliente (RFC 7591). Endpoints:
POST/oauth/register
DCR — limitado a 10/IP/hora.
GET/oauth/authorize
Redireciona para a página de consentimento protegida pelo Clerk em app.stacktr.ee.
POST/oauth/token
Código → token de acesso (JWT HS256, TTL de 30 dias).
POST/oauth/revoke
Revogação RFC 7009.
Callback para superfícies hospedadas do Claude: https://claude.ai/api/mcp/auth_callback.
Limites
Cada número abaixo é aplicado no servidor a partir de uma única tabela. GET /me retorna o próprio objeto limits do chamador; leia-o de lá em vez de codificar um limite fixo em um cliente.
LimiteAnônimoGratuitoSolo $19Studio $79Firm $249
Páginas—3 no total25 ativasilimitadoilimitado
Tempo de vida da página24h7 dias, semprepermanentepermanentepermanente
Publicações / 24h20 por IP501.0001.000ilimitado
Tamanho por site10 MB25 MB250 MB250 MB1 GB
Arquivos / arquivo1.0001.0001.0001.0001.000
Códigos de acesso · portões de e-mailapenas código de acessoapenas código de acesso✓✓✓
Números de visualizadores——aberturas, visualizações, última abertura+ engajamento completo+ engajamento completo
Slug personalizado—✓✓✓✓
Domínios personalizados——11025
Espaços de cliente——110ilimitado
Selo Stacktreepermanecepermaneceremovidoremovido
A linha de espaços de cliente conta espaços ATIVADOS, ou seja, aqueles com um hostname associado ou um portal habilitado. Arquivar páginas sob um cliente é gratuito em todos os planos, inclusive o Gratuito: a unidade paga é o endereço, não o rótulo. Arquivar um espaço libera a vaga, e um hostname de espaço não consome uma vaga de domínio personalizado.
O Gratuito conta publicações, não páginas ativas. O 3 é lifetime_publishes, um contador que só sobe: excluir uma página ou deixá-la expirar não devolve a vaga. O 25 do Solo é o outro modelo: páginas ativas, liberadas ao excluir. Toda página gratuita expira 7 dias após ser publicada, e ultrapassar expires_in_hours: "never" é recusado em vez de silenciosamente atingir o teto: um agente que recebe um 201 diz ao seu usuário que o link é permanente, então a resposta precisa ser um erro que ele não possa confundir com sucesso. accept_clamp a publica com os 7 dias.
Limite de DCR: 10 registros de cliente / IP / hora. Uma página expirada para de ser servida dentro de uma hora e a URL então informa que o link expirou, exatamente como antes; o que mudou é o que acontece depois. O conteúdo é mantido por 30 dias em vez de ser destruído naquele momento, então POST /sites/:idOrSlug/restore pode colocá-lo de volta na mesma URL, e só então ele é removido definitivamente do R2 e do D1.
Enterprise é personalizado e anual (auto-hospedagem, SLA, residência de dados). Um DPA padrão está disponível sob solicitação em qualquer plano pelo security@stacktr.ee. Pergunte em gm@stacktr.ee. Contas nos planos mais antigos Pro e Agent mantêm os limites com que se inscreveram; nenhum dos dois é mais vendido.
Sem plano? O desbloqueio higher_limits ($25 / 30 dias via x402) eleva uma identidade gratuita aos limites de frota: 1 GB por site, publicações diárias ilimitadas, sem limite de páginas, e páginas que não expiram. make_permanent ($5, uma página) cancela a expiração de uma única página.
Erros de plano
Cada um carrega um código error estável, o plan do chamador, um message legível por humanos, e, quando relevante, um limit. Exiba-os como um convite a fazer upgrade, não como uma string bruta:
CódigoStatusSignificado
`plan_lifetime_limit_exceeded`402Todas as 3 páginas gratuitas usadas. Excluir uma não ajuda.
`plan_site_limit_exceeded`402Limite de páginas ativas atingido (Solo). Exclua uma, ou suba de plano.
`plan_password_not_available`402Códigos de acesso não estão neste plano (apenas planos desconhecidos; todo plano real os tem).
`plan_password_limit_exceeded`402Limite de páginas protegidas por código de acesso atingido.
`plan_viewer_gate_not_available`402Portões de e-mail não estão neste plano.
`plan_viewer_gate_limit_exceeded`402Limite de páginas com portão de e-mail atingido.
`plan_domain_not_available`402Domínios personalizados não estão neste plano.
`plan_domain_limit_exceeded`402Limite de domínios personalizados atingido.
`plan_space_not_available`402Ativar um espaço de cliente (endereço ou portal) não está neste plano. Arquivar páginas sob um cliente ainda funciona.
`plan_space_limit_exceeded`402Limite de espaços de cliente ativados atingido. Arquive ou desative um, ou suba de plano.
`plan_upload_limit_exceeded`429Limite diário de publicações atingido; reinicia em uma janela móvel de 24h.
A expiração é meia exceção. Um tempo de vida de página maior que o teto do plano é limitado em vez de recusado: ele volta encurtado em expires_at, com expiry_clamped: true e sem erro. Pedir uma página que nunca expira em um plano com teto é recusado, 409 expiry_clamped, porque esse é o único caso em que a diferença é repetida a uma pessoa como "este link é permanente". accept_clamp assume o teto em um único campo.
Toda resposta que carrega uma página carrega seu prazo de seis formas: expires_at (segundos unix, inalterado), expires_at_iso (RFC 3339 UTC, o que mostrar a um humano), ttl_seconds (nunca negativo), expiry_clamped, expiry_ceiling_hours (24 anônimo, 168 Gratuito, null em planos sem teto) e expiry_source (request, plan_ceiling, plan_default ou stored).
Resumo completo em markdown da superfície de marketing do Stacktree: https://stacktr.ee/llms-full.txt