API do Solta
Tudo o que a interface faz, fá-lo através disto. Não há outro caminho, e é também por isso que um agente ligado à sua conta não chega a nada a que o dono da conta não chegaria.
Autenticação
Uma chave criada em Definições → Chaves e ligações → Chaves de API, enviada como bearer token. Só é mostrada uma vez e é guardada como hash, por isso uma fuga da nossa base de dados não é uma lista de chaves que funcionam.
curl https://solta.now/v1/me \
-H "Authorization: Bearer solta_…"Os conectores usam OAuth: veja Ligar o Claude ou o ChatGPT. Ambos levam à mesma conta, com a mesma autoridade.
Publicar um ficheiro
curl -X POST https://solta.now/v1/sites \
-H "Authorization: Bearer solta_…" \
-H "Content-Type: application/json" \
-d '{"source": "export default () => <h1>hi</h1>",
"filename": "App.tsx",
"title": "Hello",
"channel": "api"}'Devolve o slug, o número da versão e o link. Envie um cabeçalho Idempotency-Key e repetir o mesmo pedido não publica duas vezes.
Uma pessoa no Free tem 3 sites online de cada vez, somando a sua própria biblioteca e os espaços de trabalho Free de que é dona, e no Plus 15 sites nas suas bibliotecas Plus. Pro e Team não têm limite. No limite, um site novo (um ficheiro, uma pasta ou um remix) recebe um 402 com o código site_limit: nada é publicado, e a resposta lista os sites online, cada um com a sua library, quantos têm de sair (toUnpublish), e a libraryUrl e a upgradeUrl. Em alternativa, acrescente uma versão a um dos sites, o que mantém o link dele, ou liberte espaço primeiro: POST /v1/sites/{id}/offline põe um site offline sem perder nada, e POST /v1/sites/{id}/online volta a pô-lo online, o que é recusado da mesma forma no limite. Uma versão nova de um site nunca é recusada por isto.
Publicar uma pasta
Um endpoint à parte, porque o corpo é um ficheiro e não JSON. Envie o ficheiro comprimido como archive, ou um url de onde o devemos ler.
curl -X POST https://solta.now/v1/sites/archive \
-H "Authorization: Bearer solta_…" \
-F "archive=@dist.zip" \
-F "title=My site"Os ficheiros que um site não usa ficam de fora e o resto é publicado: tudo o que pode conter segredos (.env e todos os .env.*, .npmrc, chaves privadas, credentials.json), ficheiros e pastas ocultos como .git, e tipos que não servimos, como código de servidor. A resposta lista-os em skipped, cada um com o seu path e um reason que é secret, hidden ou unsupported, e diz o mesmo em warnings, para uma pessoa ler. Uma pasta com o código-fonte de uma aplicação, e não com o build dela, recebe um 422 com o código needs_build.
Atualizar sem estragar o link
Uma versão nova é um rascunho. O link continua a mostrar o que mostrava até a publicar, e isso leva duas chamadas de propósito, para que nada fique online por acidente.
POST /v1/sites/{id}/versions → { number: 2, state: "draft" }
POST /v1/sites/{id}/versions/2/publish → { number: 2, state: "live" }Ou uma só chamada: "publish": true na primeira põe a versão online de imediato, a não ser que o site precise de aprovação. Nesse caso, a resposta tem awaitsApproval: true e a versão fica à espera como rascunho.
De vez em quando, as nossas verificações pedem que uma pessoa veja uma página antes de ficar online, normalmente no prazo de um dia. A resposta tem então held: { url }: o link do site diz que a página está a ser verificada até ser aprovada, e url é uma página que diz em que ponto está, onde quem publicou sem conta pode deixar um e-mail para ser avisado. Publicar outra vez não muda nada.
Uma pasta muda pelos seus ficheiros. POST /v1/sites/{id}/versions/files recebe multipart form data: files para acrescentar (um .zip entre eles é aberto, e um ficheiro com um nome que já existe substitui o anterior), um remove por cada caminho a retirar, e replace=true quando o envio é o site inteiro. Com publish=true, a versão fica online na mesma chamada, a não ser que o site precise de aprovação. Nesse caso, a resposta tem awaitsApproval: true e a versão fica à espera como rascunho. Entre vários ficheiros, ou num .zip, o que uma pasta deixa de fora também fica de fora aqui, e aparece em skipped. Já um ficheiro enviado sozinho que não publicaríamos é recusado.
O resto
- GET /v1/library
- Os seus sites.
- GET /v1/sites/{slug}
- Um site, a versão online e o histórico.
- PATCH /v1/sites/{id}/visibility
- Quem o pode abrir.
- PATCH /v1/sites/{id}
- O título, ou o endereço, com
{ "slug": "…" }. O endereço antigo continua a encaminhar as pessoas. Um endereço curto, de 4 caracteres ou menos, ou um endereço de uma só palavra, uma palavra comum sozinha, exige Pro ou Team: no Free, é um402comcode: "needs_pro_short_address", e depois dos endereços curtos ou de uma palavra que o plano permite,short_address_limit. Um endereço com o nome de uma empresa ou de um banco é um422comslug_brand, em qualquer plano. - GET, POST /v1/sites/{id}/addresses
- Os endereços do site, e mais um, com
{ "slug": "…" }, que leva os visitantes ao principal (Pro e Team).POST …/addresses/{slug}/maintorna um deles o endereço principal, eDELETE …/addresses/{slug}torna-o um endereço antigo. Recusado comneeds_pro_extra_address,extra_address_limit,needs_pro_short_address,short_address_limit,slug_brandouslug_taken. - GET /v1/sites/{id}/compare?from=1&to=2
- O que mudou entre duas versões.
- DELETE /v1/sites/{id}
- O site fica offline de imediato. O endereço fica reservado durante pelo menos um ano, e enquanto for usado. Depois de 6 meses sem visitas enviamos-lhe um e-mail, e um mês depois o endereço é libertado.
Erros
Um corpo JSON com error, e detail quando há algo útil a acrescentar. Um ficheiro que recusamos (uma chave deixada no ficheiro, um tipo não suportado) é um 422 com o motivo por palavras, não um stack trace.

