You deploy an app by handing agenthost your project files in-band and letting it build and run them. This page is the contract: the one entrypoint each runtime looks for, the file it installs dependencies from, how the runtime is chosen, and how to confirm a deploy actually came up. Match these conventions and a deploy just works.
Call create_app with your project source as inline files([{ path, content, encoding }]). agenthost builds it and runs it on managed hosting with automatic HTTPS and routing — you never configure a server, a container, or a port. Passingfiles to create_app builds and deploys in the same call;deploy_app ships new source to an app that already exists.
Builds are asynchronous. Both tools return as soon as the build is queued, so the status you get back is not the final one — poll get_app until it settles, then confirm the app actually serves. See Deploying & checking status.
start_command, and no port setting. The way an app is started is fixed per runtime — it's the convention described below, not a parameter. If you pass a field the tool schema doesn't define, it is silently ignored. To control how your app starts, match the entrypoint your runtime expects.The runtime decides how your source is built and started. Pass it explicitly asruntime (e.g. "node:22"), or omit it on create_app and agenthost infers it from your files. Each runtime looks for one entrypoint:
| Runtime | Inferred from | Entrypoint it looks for | Installs deps from |
|---|---|---|---|
static | index.html | index.html at the project root | — |
node | package.json | npm start (the start script) | package.json |
python | requirements.txt, .py | app imported from app.py | requirements.txt |
php | composer.json, .php | index.php at the project root | composer.json (optional) |
Inference is a convenience for the common case; when in doubt, set runtime explicitly. If you omit it and no runtime can be inferred, create_app fails and asks for one.
Your files are served exactly as handed, over HTTPS. index.html at the project root is served at /; other files are served at their paths. There is no build step and nothing to start — good for a landing page, a docs site, or any pre-built single-page-app bundle.
# minimal static site
index.html # served at /
styles.css
app.jsA package.json is required — it both selects the Node runtime and holds the command that starts your app. On deploy, agenthost installs your dependencies, runs yourbuild script if you have one, then starts the app with npm start.
start script — it is the entrypoint. Without one, npm start has nothing to run and the app won't come up.build script runs automatically before start, if present (e.g. to compile TypeScript or bundle a frontend). It's skipped when absent.package.json (using the lockfile when you include one). Don't ship node_modules."scripts": {
"build": "tsc", // optional — runs if present
"start": "node server.js" // required — how your app starts
}Python apps are served as a WSGI application. agenthost imports the nameapp from app.py — so your project must have a file namedapp.py at its root that exposes a WSGI callable named app. This is a fixed convention, not something you can rename with a parameter.
app in app.py. A Flask or Django app object named app in that file is exactly what's expected.requirements.txt — and include gunicorn there, since that's what serves the app. Omit it and the app fails to start.# app.py — must expose `app`
from flask import Flask
app = Flask(__name__)
@app.route("/health")
def health():
return {"ok": True}
# requirements.txt
flask
gunicornapp.py, or the WSGI object inside it isn't named app — so the import fails and the container crash-loops even though provisioning "succeeded". If you need a different framework or an async (ASGI) server, adapt it to expose a WSGI app in app.py, or wrap it so that name resolves.PHP apps are served from your project root with index.php as the front controller. Requests for real files (assets, other .php scripts) are served directly; everything else is handled by your index.php.
index.php at the project root.composer.json is present, its dependencies are installed for you (production, optimized). No composer.json is fine for a plain PHP app — omit it.Append a version to the runtime — "<language>:<version>". Leave it off and a supported default is used. static takes no version.
| Runtime | Supported versions | Default |
|---|---|---|
node | 20, 22, 24 | 22 |
python | 3.11, 3.12, 3.13 | 3.12 |
php | 8.2, 8.3, 8.4 | 8.3 |
Examples: "node:24", "python:3.13", "php:8.4". An unsupported version is rejected with the list of supported ones.
Pass runtime configuration and secrets as env (a map of name → value) oncreate_app. They're stored securely and injected into your app's environment at run time — never written into your source. Read them the normal way for your language (process.env.X, os.environ["X"], getenv('X')). Only thenames of the variables you set are ever echoed back (as envKeys); values are not.
Because builds are asynchronous, a successful tool call means "queued", not "live". Confirm a deploy in this order:
get_app until status settles (e.g. running or error). A freshly created app may report running from a previous deploy while the new build is still coming up, so don't stop at the first read./health endpoint is ideal) and check the response body, not just that a status says "running".get_app_logs shows recent build/runtime output when you need to see why something crashed. It's best-effort and can be briefly unavailable — if it is, fall back to hitting the URL.start_command (or port) field exists. It doesn't — those parameters are silently ignored. How an app starts is the per-runtime entrypoint convention above.app.py / app. The import is app:app. A file called main.py, or an object named application, won't be found.gunicorn in requirements.txt. It's what serves a Python app; without it the app can't start.start script. npm start needs one; add it to package.json.get_app and confirm with a real request before calling a deploy done.node_modules, vendor, and virtualenvs — dependencies are installed for you from your manifest.