1 · It is checked
Leaked passwords and keys, a bundled .env,
an app listening only to itself — each stops the deploy, and your agent is told
how to fix it.
Connect your agent to Erawan once. After that you ask for what you want, and it writes the code, sends it, watches the build, and gives you a link.
One account works everywhere. Most clients open a browser to sign in, so there is no key to copy or store.
Add the server once, in the project you work in:
claude mcp add --transport http erawan https://api.erawan.cloud/mcp
A browser opens, you sign in, and you approve the connection. Codex and OpenCode take the same URL in their own MCP settings.
Settings → Connectors → Add custom connector, then paste
https://api.erawan.cloud/mcp
and connect. Chat has no filesystem, so your agent sends the files it wrote
straight from the conversation.
A first deploy needs nothing from you — no email, no password, no confirmation link. Any agent with a shell can ask for a guest session and put a site online in a minute:
curl -sX POST https://api.erawan.cloud/v1/guest
It returns an access token to deploy with, and a keep_url
to hand the person it was built for. Opening that link is how the session
becomes an account — the same apps, at the same address.
There are no API keys to store. An agent that connects through the browser holds a token of its own, which you can revoke by name in the console.
You do not call anything yourself. Describe what you want and let the agent work.
Build a FastAPI service that shortens URLs and deploy it on Erawan.
Call it shortener, and tell me the address when it is live.
Leaked passwords and keys, a bundled .env,
an app listening only to itself — each stops the deploy, and your agent is told
how to fix it.
The deploy returns straight away and the build runs in the background. Your agent watches it and reads the error if something breaks.
Every app gets its own address with HTTPS. Deploy again for a new version; roll back if the new one is worse.
Apps live at {name}.erawan.app.
The console
shows the same apps, versions and logs — plus every agent connected to your
account, each one revocable.
Three ways in, and your agent picks the one that fits where it is running. They all end up in the same place, with the same checks and the same history.
| Where your agent runs | What it uses |
|---|---|
| On the same machine as the project | deploy_app with a folder path |
| Somewhere else, but it has a terminal | Upload the project first, then deploy_app with the upload |
| In a browser, with no files at all | deploy_files — the code goes inline |
The upload step, for agents with a terminal:
tar -czf - . | curl -sS -X POST https://api.erawan.cloud/upload \
-H "Authorization: Bearer erw_at_…" --data-binary @-
Erawan works out how to run your project. A Dockerfile wins if you wrote one, then Python, then Node, then a plain website.
| Kind | Recognised by | How it starts |
|---|---|---|
| Docker | a Dockerfile | your image, as written |
| Python | main.py, app.py or server.py | FastAPI runs under uvicorn; anything else runs the file |
| Node | package.json | npm start, or index.js / server.js / app.js |
| Website | index.html and no build | served directly — no container, nothing to keep running |
Domain=.erawan.app — from your server, or from
document.cookie in the browser — is sent to every other
customer's app on it, and their pages can set one yours will receive.
Leave the domain off: the cookie then belongs to your app's own address
and nothing else. The pre-deploy check refuses a deploy that does
this. If two apps you own genuinely need to share a session, pass a
token in a header between them instead. The check reads your files and
cannot see a string your code builds while it runs, so treat it as a
rule rather than something we catch for you.__Host-something — the prefix makes the browser refuse any
attempt by a neighbouring app to set or replace it, and it is the one
protection here that does not depend on us. And do not rely on
SameSite to stop cross-site request forgery: every app on
the shared domain counts as the same site, so those cookies are still
sent on requests coming from a neighbour. Check a token your app issued.
Anything with a login belongs on your own domain — a custom
domain is its own site to a browser, which is where the boundary
actually is.0.0.0.0, not 127.0.0.1 —
otherwise nothing outside the container can reach it. And it has to actually
start serving: a Flask app needs app.run(host="0.0.0.0", port=8000),
or tell your agent to pass a start command such as
gunicorn app:app -b 0.0.0.0:8000.Plain websites are the cheap path: they go up in seconds and cost nothing to keep online. In exchange they have no environment variables, no rollback and no running logs — there is no process to have them.
Ask for one and it appears next to your app, already connected. Your app is restarted with the settings it needs, so there are no connection strings to wire up by hand.
A real database with its own storage that survives deploys. Your
app reads DATABASE_URL.
pgvector is included on every plan — CREATE EXTENSION IF NOT EXISTS vector works as written.
For caching, queues and sessions. Your app reads
REDIS_URL.
S3-compatible object storage with a bucket ready to use, for uploads and generated files.
Receipts, sign-in codes, notifications. Your app posts the message to Erawan — no provider account, no keys of your own.
Logins for the people who use your app, run by us. See Logins for your users.
Add the addon, then send your dump straight in. It travels over the same authenticated connection as everything else and is never published.
gzip -c dump.sql > dump.sql.gz
erawan addon add postgres myapp
erawan db import myapp dump.sql.gzNever put a dump on a public URL to fetch it back, however briefly. A CDN cache and the access logs at both ends keep the file after you delete the link, and a random token in a path is not a password.
The import stops at the first statement that fails
rather than reporting success over half a database, and refuses a
database that already has tables unless you pass --force.
Import before the app runs its own migrations.
Most apps are a single process and need nothing here. When a repository runs a
web server and a background worker, put an erawan.yaml at
its root and deploy as usual — the file is read on our side, so no particular
version of the CLI is needed.
version: 1
addons: [postgres, redis]
components:
web:
path: .
port: 8000
public: true
resources:
requests: {memory: 256Mi, cpu: 100m}
limits: {memory: 1Gi, cpu: 500m}
worker:
path: .
args: ["rq", "worker"]A component can also carry its own name, which
is how a product made of a site, an admin panel and an API stays one app.
Two ways to give it one — subdomain: claims a name on
erawan.app, domain: points a name you own at a part:
components:
site:
path: web/dist
domain: [example.com, www.example.com]
admin:
path: admin/dist
subdomain: acme-admin # acme-admin.erawan.app
api:
path: backend
port: 8080
public: true
worker:
path: backend
args: ["rq", "worker"]Four parts, four addresses: site
on two names you own, admin on one of ours, api on the
app's own .erawan.app, and worker on none — it is
reached by the others and by nobody else, which is what a worker is for.
There is no automatic <app>-<component>.erawan.app —
that name is not created for you. A part is reachable from outside only if it
has public: true, a subdomain: or a
domain:. A part with none of them is reachable
only by the other parts, at
http://<app>-<component> inside the cluster.
That is right for a worker and wrong for something people were
visiting, so check erawan status after a deploy: a part that
answers nowhere prints inside the app only. Somebody read a
blank there as "not filled in yet", deleted the app that had been serving that
traffic, and took their own front end down.
Add {C}healthcheck: /healthz to a part that answers HTTP. Without one we check that something is listening on its port, which is the right question for most things and the wrong one for an app that opens its socket and then spends a few seconds connecting to a database — that pod looks ready, gets traffic, and the request hangs.
It matters most during a deploy: the new part is added to the rotation as soon as it is ready and the old one is removed, so "ready" being early is a window where visitors wait on a process that is not answering yet. With a path, that window closes.
Pick a path your app actually implements. If it serves
a single-page app, an unknown path returns index.html with a 200 —
so a healthcheck pointing at one you never wrote passes forever, including
while the part behind it is dead. A customer found this on their own
deploy: curl /healthz answered 200 and the real endpoint was
/health. erawan status prints a
ready by column, and erawan deploy says so when the path
answers HTML.
subdomain: acme-admin claims
acme-admin.erawan.app in the same flat namespace app names come
from, so it is refused if anything already answers on it — and the deploy says
so before it builds. There is no DNS to set up and no certificate to wait for:
it is live as soon as the release is.
public: true still names exactly one part, and that
part keeps the app's own address. A name is released when the app is deleted or
when you take the line out of erawan.yaml and deploy again — so a
name your users have is only as safe as the file that claims it.
A part that is not the public one can hold up to four:
subdomain: [acme-admin, acme-console]. That is how a name written
into somebody else's configuration — a webhook URL, an endpoint compiled into a
mobile build — is retired without a cutover: claim both, repoint whoever holds
the old one, then delete the line.
It is served as files by the same shared server that serves an ordinary
static site — no container, and nothing against your plan's memory. It gets
the same single-page fallback, so a refresh on /settings returns
index.html rather than a 404.
Such a component needs an address to be served on:
domain:, or public: true. A deploy says so rather
than leaving it unreachable.
args replaces the image's command, which is how
two components share one directory and one image. Prefer it to
start_command when the repository has its own Dockerfile:
start_command only shapes a Dockerfile we generate, so with one in
the tree it is read and then ignored.resources for every component. What you
leave out claims your plan's whole per-app allowance, so four silent components
ask for four times what one app is sold and the fourth is refused.addons in the file are re-created on every deploy.
Removing one with erawan addon rm and deploying again brings it
back — take it out of the file instead.public: true belongs to exactly one component
— the one that answers on the app's own .erawan.app address. Names
in domain: are as many as you like, on as many components as you
like.domain: does not claim it. It says
which component the name belongs to; whether it points at Erawan is still
erawan domain add and the DNS check, so a deploy never fails
because somebody has not finished editing their DNS. A name already on another
app of the same account is moved by the deploy — no removal, no gap.Components accept path,
port, public, disk, image,
user, domain, args, start_command,
resources, secrets and addons.
For an app with customers of its own. Erawan runs the sign-in, so your app never stores a password and never handles a reset. It is an addon like any other: ask for it and the app restarts with what it needs already set.
erawan addon add myapp auth
Your app is a normal OpenID Connect client. Six variables arrive in its environment and nothing else has to be configured:
| Variable | What it is |
|---|---|
AUTH_ISSUER_URL | The issuer, for validating tokens. |
AUTH_URL | Where to send somebody to sign in. |
AUTH_CLIENT_ID | This app's client id. |
AUTH_CLIENT_SECRET | Its secret. Rotate with erawan auth rotate-secret. |
AUTH_JWKS_URL | The signing keys. |
AUTH_WELLKNOWN_URL | Discovery, if your library prefers it. |
erawan auth callback add myapp http://localhost:3000/callback
erawan auth providers set myapp google,email
erawan auth users myapp
A redirect URI has to be allowed before it is used, including the one you develop against. Providers are google, email, github and line. The user list is masked by default — the addresses of your users are not something a terminal should print by accident.
Sign-in and consent are served by Erawan, and by default they carry your app's name and nothing of ours but one line saying which account is being used. Two things are yours to set:
erawan auth theme show myapp
erawan auth theme set myapp --color blue --name "Suea Thong Gym"
The colour is a name from a fixed list — show prints it — and
not a hex value: the list is shadcn's, every entry chosen to carry white
text, so there is no combination that produces a button nobody can read.
It moves the primary button and the focus ring, and nothing else. The name
is what appears above the form, up to 32 characters, and cannot mention
Erawan: your users type an Erawan password on that page, so a name
claiming to be us is the one thing it must not be able to say.
erawan.app,
and a browser treats all of them as one site — so a neighbour's page is
not cross-site to yours. A
custom domain
is its own site, which is where the boundary actually is. Name the session
cookie __Host-something either way.Environment is non-sensitive configuration: feature flags, modes, URLs and log levels. Credentials are app secrets. Declare their names at deploy and enter each value once on Erawan's trusted setup page; an agent never carries it.
erawan deploy . --name myapp --secret OPENAI_API_KEY
erawan env set myapp LOG_LEVEL=info
erawan env list myapp
erawan env unset myapp LOG_LEVEL
--secret pauses and returns
secrets_url, the app's own page in the console. The owner
enters the value there, or through hidden CLI input when it is already on
their machine. The console, CLI and API expose names and status only;
there is no reveal endpoint.--env or chat.env baked into an image is one of the
things it looks for. Declare only the name with --secret and
let the owner complete trusted setup.Plain websites have no environment at all — there is no process to hold one. A site that needs a key needs to be an app.
Attach a name you own. Erawan prints the DNS records to create, watches for them, then issues the certificate itself — there is nothing to upload and nothing to renew. Included on Hobby and up.
erawan domain add myapp app.example.com
erawan domain ls myapp
erawan domain rm myapp app.example.com
A subdomain gets one CNAME. An apex — example.com
with no prefix — cannot be a CNAME at all, so that answer is an
A record with the address in it, plus a CNAME for
www. Some providers call the apex form ALIAS, ANAME or CNAME
flattening; if yours offers one, it works as well.
erawan domain ls says what each domain is waiting for, which is
usually DNS that has not propagated yet. The
.erawan.app address keeps serving throughout, and keeps serving
after you remove a custom domain.
Some things you do not want to write. Point Erawan at the project's own repository and it goes through the same path your own code does — the pre-deploy check, the build, the history, the rollback.
erawan deploy https://github.com/owner/repository --name my-app
unreviewed
in their own review field. A promise nobody keeps is worse than no
promise, so the entries are gone. erawan catalog and
GET /v1/catalog still answer, and are empty while the
replacement is built from what people actually deploy successfully.A build takes minutes. These take seconds, and they are the three ways deploying somebody else's project actually fails.
COPY --link,
a --chmod
with letters in it, RUN --mount
— cannot be built here, and it fails at that step, which may be twenty
minutes in.docker run
something, use that image in a two-line
Dockerfile
rather than building the source.
Then find what it needs told to it — the README, a
docker-compose.yml,
an .env.example.
A missing key is a container that starts, crashes and explains nothing.
For code you wrote yourself there is a cheaper check:
erawan assess .
runs the same gate a deploy runs and changes nothing. It reads a
directory, so it cannot check a GitHub URL — for those, deploying is the
check, and a failed deploy never replaces a running version.
Deployed something worth other people knowing about?
erawan catalog request <name or URL> --why "…"
— what people run here is what the list will be built from.
Some apps should not be on the open internet — an admin panel, a tool you run for
yourself, software that has no sign-in of its own. Put one line in your
erawan.yaml
and the URL stops answering strangers.
version: 1
access: owner
Anyone opening the app is sent to Erawan's sign-in, and only the account that owns it is let through. Your app is not changed and gets no extra container: the check happens at the front door, before a request reaches it, and what arrives is an ordinary request from a person Erawan has already identified.
One path can stay open. A webhook, a health check or a payment callback is a machine with no session, and the door would send it to a sign-in page. Name the exceptions when you deploy — up to five, matched exactly:
erawan deploy . --access owner --public-path /callback
Exactly, not by prefix: /callback
does not also open /callback-admin.
/ is refused — that is the whole app.
It is a door, not a disguise. The app behind it still has whatever permissions it always had, and anyone you share the account with can open it. Software with no sign-in of its own is single-user software; this makes it yours, and it does not separate one person's data from another's.
One-off work — a database migration, a script — runs with your app's own code and settings, and you get the output back.
Repeating work is a task: say when, in cron form, and what to run. Nightly reports and clean-ups keep going after you close the laptop. Schedules are in UTC, and deploying a new version moves the tasks across with it.
A job and a scheduled task run with the app's image, its settings and its
disk, mounted at $DATA_DIR — so
du -sh $DATA_DIR finally answers.
It is read-only unless you pass
--write. The app is writing to that same volume while your
command runs, and two writers on one filesystem is how a database file is
ruined. Reading, counting and copying out need no write.
Failures come back with the reason and the end of the log, written so your agent can act on them without you. Usually it just fixes the code and deploys again.
0.0.0.0
on the port you gave, and that the start command actually starts a server..env was about
to ship. Move those into settings instead of the code, then deploy again.What your agent can do once it is connected. You will rarely name these — ask in your own words and it picks.
| Tool | What it does |
|---|---|
assess_app | Run the pre-deploy check on its own. |
deploy_app | Deploy from an upload, a public GitHub URL, or a folder path on the control plane. |
deploy_files | Deploy code sent inline, for agents with no files. |
get_status | Whether it is running, which version, and the deploy history. |
get_logs | The app's own logs, or the last build's log. |
list_deployments | Every app with its status and address. |
set_env | Add or change settings and restart. |
stop_app / start_app | Turn an app off without deleting it, and back on. |
rollback | Go back to the previous working version. |
delete_deployment | Take an app down and remove it. |
provision_addonremove_addon | Attach or detach Postgres, Redis, file storage, email. |
run_job | Run one command with the app's code and settings. |
schedule_tasklist_tasksdelete_taskget_task_logs | Set up and inspect repeating work. |
get_metricsget_errors | Traffic, error rate, p95 and memory; and why a container that died died, with its last output. |
get_rewind_windowrewind_database | How far back the database can go, and taking it there. |
add_domainlist_domainsremove_domain | Point a domain you own at the app. The erawan.app address keeps answering. |
change_plan | Move the account between plans. Where payments are configured, a plan is bought in the console — an agent cannot buy one. |
list_catalog | Software known to have run here. Empty today — deploy a repository by its own URL. |
Everything the tools above do, an ordinary HTTP request does too — same
account, same pre-deploy check, same history. It is what the
erawan
command talks to, and it is the path for CI, a script, or an agent that has
curl
and nothing else.
https://api.erawan.cloud/v1
Authorization: Bearer erw_at_…
Every credential here ends. There is no key that lives for ever — a secret nobody has to renew is one nobody notices — so an access token is good for an hour and refreshes itself, and a token you make by hand carries an expiry you chose. Four ways to hold one:
POST /v1/guest
takes nothing and returns an access token, a refresh token and a
keep_url.
Websites only, and gone in 24 hours unless somebody opens that link.erawan login
signs in through a browser and keeps the tokens in
~/.erawan/cli.json.erawan login --device prints a short code and a URL;
you enter the code on any device that does have a browser, and the machine
signs itself in. It is the default when the CLI can see there is no browser to
open, so an agent usually needs no flag. Do not copy
~/.erawan/cli.json into a box instead: that file's refresh token does
not expire and reaches every app in the account.erw_pat_…, for CI or for a
machine you trust less than your laptop. erawan token create ci, or
Settings in the console. It expires (90 days by default), it does only what you
chose — deploy, admin, or both — and
--app limits it to named apps. Revoke one without touching the
others. A token cannot create another token: minting needs a person signed
in.
ERAWAN_TOKEN, or with
ERAWAN_TOKEN_FILE pointing at a file holding it — the file is read
again before every request, so a token rotated by whoever wrote it is picked
up without a restart. Either beats a stored login and neither is written to
disk, so a box that is handed a token needs no credential file at all.
When an hour is up, exchange the refresh token at
POST /token
with grant_type=refresh_token
and the client_id
you were given — guest
for a guest session. Refresh tokens are single use: each exchange returns the next one.
Building the CLI into an image? Pin the version rather than following the
latest, and verify it. https://erawan.cloud/dist/SHA256SUMS gives the
digest of the wheel currently published, in the format
sha256sum -c reads; /dist/latest names the file and
/dist/ lists what exists.
# what is published right now, and its digest
curl -s https://erawan.cloud/dist/latest
curl -s https://erawan.cloud/dist/SHA256SUMS
# pin that filename in your image, and move it when you mean to
uv tool install --force "https://erawan.cloud/dist/<the filename above>"
Only the current release is kept under {C}/dist/ — an old wheel served from there is an old wheel somebody installs — so a pinned filename stops resolving when we publish the next one. That is the intended signal: it is a version you chose, and moving it is a decision rather than a side effect of rebuilding.
--force is the part that matters:
uv tool upgrade cannot move an install pinned to a wheel URL — uv's
receipt holds the old one — so it bumps dependencies and leaves the command
missing. The installer verifies this checksum itself; against somebody who
controls the site it proves nothing, since they would publish both, but it
catches a truncated download, a stale cache, and a wheel that moved under a
version you pinned.
TOKEN=$(curl -sX POST https://api.erawan.cloud/v1/guest | jq -r .access_token)
curl -sX POST https://api.erawan.cloud/v1/apps \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"name":"hello","files":{"index.html":"<h1>hello</h1>"}}'
curl -s https://api.erawan.cloud/v1/apps/hello -H "Authorization: Bearer $TOKEN"
curl -s "https://api.erawan.cloud/v1/apps/hello/logs?kind=build" \
-H "Authorization: Bearer $TOKEN"
POST /v1/apps answers 202 with a
Location header and does the work in the background. Poll
GET /v1/apps/{name} until status is
running or failed; a failure carries
last_error, and the build log has the rest. A failed deploy never
replaces the version that is already running.
POST /v1/apps
takes a name
and exactly one source. Re-posting the same name makes a new version
and keeps the old one for rollback.
| Field | What it is |
|---|---|
files | Text files inline, {"path": "contents"} — for a caller with no filesystem. |
upload_id | From POST /v1/uploads: a gzipped tar of the project. |
github_url | A public repository, optionally with github_ref and github_subdirectory. |
catalog | An entry by name. Empty today — see Ready-made software. |
path | A directory on the control plane — development servers only, refused in production. |
port · start_command · env | Optional. Port defaults to 8000; the runtime is detected if no start command is given. |
| Endpoint | What it does |
|---|---|
POST /v1/guest | A session with no account. Unauthenticated, rate-limited per address. |
GET /v1/mePUT /v1/me/plan | Who this token belongs to, and keep_url while the account is still a guest; move the account between plans. Where payments are configured a plan is bought in the console, not here. |
GET /v1/appsPOST /v1/apps | Every app with its status, address and parts — the live release's parts, several for an erawan.yaml with more than one; start a deploy (202). |
POST /v1/uploads | Gzipped tar in the body → an upload_id. |
POST /v1/assess | Run the pre-deploy check without deploying. |
GET /v1/apps/{name}DELETE /v1/apps/{name} | Status, version, URL and deploy history; take the app down. |
POST /v1/apps/{name}/restartPOST /v1/apps/{name}/rollback | Restart it; or go back to the previous version. |
POST /v1/apps/{name}/stopPOST /v1/apps/{name}/start | Turn the app off and on. Nothing is deleted — the disk, the database, the settings and every version stay, and its address answers a page saying it is stopped. Stopping frees the memory for another app. Both are idempotent. |
GET /v1/apps/{name}/export | The running version's code as a .tar.gz — ?version= for an earlier one. erawan pull <app> is this. |
GET /v1/apps/{name}/logs | ?kind=runtime|build, ?tail=, ?component= for one part of a multi-part app — left out, the public part answers and the reply names it. |
GET /v1/apps/{name}/metricsGET /v1/apps/{name}/errors | Traffic, error rate, p95, memory; and why a container died, with its last output. ?window=1h|6h|24h|7d|30d. |
GET /v1/apps/{name}/envPUT /v1/apps/{name}/env | Variable names only — values are encrypted at rest and never read back. A null value removes one; anything unmentioned is left alone. |
GET /v1/apps/{name}/secretsPOST /v1/apps/{name}/secret-requests | Secret names, status and bindings; or one app-level trusted setup request. Values never appear in a GET or agent deploy call. |
POST /v1/apps/{name}/addonsDELETE /v1/apps/{name}/addons/{type} | postgres, redis, minio, email. Connection settings are injected and the app restarts. |
POST /v1/apps/{name}/jobs | One command with the app's own image and settings. Waits for the result. |
POST /v1/apps/{name}/rename | Change an app's name. Its disk, add-ons and URL are untouched — the name is what commands take, the address is what people visit. |
POST /v1/apps/{name}/db/import | Restore a gzipped pg_dump into the app's postgres addon. The request body is the file. Refused if the database already has tables unless ?force=true. |
GET /v1/apps/{name}/rewindPOST /v1/apps/{name}/rewind | What the database can be rewound to, and rewinding it. Destructive — the state being replaced is saved first. |
GET /v1/apps/{name}/domainsPOST /v1/apps/{name}/domainsDELETE /v1/apps/{name}/domains/{domain} | A domain you own, pointed at the app (202 — poll the GET). The erawan.app address keeps answering either way. |
GET /v1/apps/{name}/tasksPOST /v1/apps/{name}/tasksDELETE /v1/apps/{name}/tasks/{task}GET /v1/apps/{name}/tasks/{task}/logs | Scheduled work, in cron form and in UTC. |
GET /v1/catalogGET /v1/catalog/{name}POST /v1/catalog/requests | The list of known software (empty for now) and asking for an addition. The two reads need no credential. |
POST /v1/askGET /v1/askGET /v1/ask/{ref} | Ask a question, request a feature, or say what is wrong — a person answers. ?mode=sync (the default) holds the connection for a while and returns 200 with the answer; otherwise 202 with a Location to poll. Needs an account: deploying does not, being answered does. Erawan answers some questions itself in seconds, from its own documentation and your account's state, and the reply says answered_by: erawan; send "human": true to wait for a person and keep the question away from a model provider. |
Handing a secret to an app without an agent ever seeing its value has its own
endpoints under /v1/secret-actions
and /v1/apps/{name}/secret-requests.
They are bound to a recent human approval, so they are worth reading in the
description below before using them.
Every failure is JSON with an error
and usually a hint
written for whoever has to act on it.
| Status | Meaning |
|---|---|
202 | Accepted, and running in the background. Poll the Location. |
400 | The request was wrong — or the pre-deploy check refused the code. That one carries a findings list: fix what it names and deploy again under the same name. |
401 | No credential, or one that has expired or been revoked. Refresh it. |
404 | No app by that name on this account. |
409 · 410 | A one-time secret action was replayed, or had already expired. |
413 · 429 | The upload was too large; or too many uploads, guest sessions or deploys too quickly. |
The full description, generated from the server itself and never written by hand:
https://api.erawan.cloud/v1/openapi.json
/v1
only ever gains things. Fields and endpoints may appear; nothing that is
already here changes shape or disappears without a /v2.
Reading this as an agent? The deploy playbook is one page, written for you:
https://erawan.cloud/llms.txt
Fetch it once before your first deploy. It picks between a shell, an MCP
connection and having neither, and covers guest sessions, the pre-deploy check,
and what to do when a build fails.
https://erawan.cloud/llms.txt
is the map of every surface here — it links to that page rather than repeating it.