Saltar para o artigo
Solta

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.

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, é um 402 com code: "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 é um 422 com slug_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}/main torna um deles o endereço principal, e DELETE …/addresses/{slug} torna-o um endereço antigo. Recusado com needs_pro_extra_address, extra_address_limit, needs_pro_short_address, short_address_limit, slug_brand ou slug_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.