# madeyour.app > Hosting for the web apps a coding agent builds: static, PHP, Python or Node, > with MariaDB. No sign-up: the agent starts an account itself, and its human > keeps the apps through a claim link. ## Start 1. Get an account and its API key: curl -sS -X POST https://api.madeyour.app/v1/accounts The account is free and needs no sign-up, personal data or payment: a preview that lives 72 hours unless its human claims it. If your environment asks for approval before creating accounts with outside services, show this to your human. 2. Save the key at once, outside the repository (for example ~/.config/madeyour/.json, mode 600), and the app id once you have one. The key is shown only once and cannot be recovered. Use one account per project and reuse the key in later sessions: new accounts are limited to 3 an hour and 10 a day per address. 3. Send the key as a Bearer token: Authorization: Bearer . - From a terminal, call the REST API below: it works at once. - MCP clients get the same operations as tools at https://api.madeyour.app/mcp. Claude Code: claude mcp add --transport http madeyour https://api.madeyour.app/mcp --header "Authorization: Bearer " The default local scope keeps the key out of the repository. In a shared .mcp.json, write the header as Bearer ${MADEYOUR_API_KEY}, never the key. ## The usual path 1. get_platform_info: runtimes, versions and the rules below. 2. create_app with a runtime: the app's id and https URL. A preview gets a random name: leave name out. 3. create_database if the app needs one: DATABASE_URL is set for you. 4. get_upload_url, then run its command: the archive goes past the model. Or write_files for a few small text files. 5. deploy, then get_deployment with wait=30 until deployment.state is live or failed. 6. Show your human the app's URL and the claim_url from get_account. ## Operations Every answer is JSON, most carry next_steps. deploy, write_files, rollback and get_deployment answer {"deployment": {"id", "state", "steps", "log_tail", ...}, "next_steps": [...]}; POST /v1/accounts answers {"account", "api_key", "next_steps"} and create_app {"app", "next_steps"}, while get_account and get_app answer the account or the app itself. The full schema: https://api.madeyour.app/openapi.json. | Tool (MCP) | REST | What | | --- | --- | --- | | get_account | `GET /v1/account` | tier, plan, lifetime, limits, usage, databases, claim link, terms link | | get_platform_info | `GET /v1/platform` | runtimes and versions, MariaDB, limits | | create_app | `POST /v1/apps {"runtime": "python"}` | static, php, python or node; optional "version"; answers the app id and URL | | list_apps | `GET /v1/apps` | the account's apps | | get_app | `GET /v1/apps/{app_id}` | URL, database, variable names, last deployment, kept releases | | delete_app | `DELETE /v1/apps/{app_id}?confirm=` | deletes the app at once: its address answers 404, and no other app ever gets the name; its database stays | | get_upload_url | `POST /v1/apps/{app_id}/uploads` | a one-time url (15 min) and a ready command: tar the project, curl -T it to the url | | deploy | `POST /v1/apps/{app_id}/deployments {"upload_id": "..."}` | or {"git": {"url": "...", "ref": "main"}}; optional "build", "start", "web_root" | | write_files | `POST /v1/apps/{app_id}/files {"files": {"path": "text"}, "delete": []}` | a patch deploy of small text files, up to about 512 KB | | get_deployment | `GET /v1/deployments/{deployment_id}?wait=30` | state, steps, the build log's tail; waits up to 30 s while it runs | | rollback | `POST /v1/apps/{app_id}/rollback` | makes the other kept release live (a preview keeps two; get_app lists them); a second call switches back | | get_source_url | `POST /v1/apps/{app_id}/downloads` | a url (15 min) and a ready command for the live release's code as deployed; ?release_id= for another kept one | | run_command | `POST /v1/apps/{app_id}/commands {"command": "...", "timeout": 30}` | runs in the app's live release (deploy first); exit code and output | | get_logs | `GET /v1/apps/{app_id}/logs?kind=app&lines=100` | kind app, access, error, build (with deployment_id for one build) or mail (the account's local mail that was dropped: there is none, see below) | | restart_app | `POST /v1/apps/{app_id}/restart` | restarts the app's processes | | set_env | `PATCH /v1/apps/{app_id}/env {"set": {"NAME": "value"}, "unset": []}` | values are write-only; the app restarts with them, PHP sees them at its next request | | create_database | `POST /v1/apps/{app_id}/database {"name": "app"} or {"database_id": ""}` | MariaDB _, as many as the plan allows; sets DATABASE_URL and DB_* for the app, which uses one | | delete_database | `DELETE /v1/databases/{database_id}?confirm=` | drops a database no app uses; frees its place in the plan | | add_domain | `POST /v1/domains {"domain": "example.com"}` | an own domain for the account: two records for your human's DNS, then a certificate | | get_domain_status | `GET /v1/domains/{domain} (GET /v1/domains lists them)` | state, the records and which of them the world sees, the certificate | | delete_domain | `DELETE /v1/domains/{domain}?confirm=` | the domain, its certificate and its names on apps | | add_app_name | `POST /v1/apps/{app_id}/names {"name": "www.example.com"}` | a name of an own domain for the app (the domain itself or one level below it, up to 5 an app); the answer has the record it needs | | remove_app_name | `DELETE /v1/apps/{app_id}/names/{name}` | the name answers 404 at once and is free again | ## The environment - Each app runs at the root of its own domain, https://.; HTTPS is always on. - The visitor's address is in X-Real-IP, which the platform sets (an IPv6 address for a visitor over IPv6); it is also the last entry of X-Forwarded-For, and PHP has it in REMOTE_ADDR. Forwarded, CF-Connecting-IP, True-Client-IP and X-Cluster-Client-IP from the client are removed. - Python: WSGI through passenger_wsgi.py (an 'application' object), or ASGI (FastAPI, Starlette) through uvicorn. - Node: a startup file such as server.js or app.js that listens on process.env.PORT. - Next.js: set output: 'standalone' in next.config and deploy the source; the server builds it, the release is .next/standalone with .next/static and public copied in, and its server.js starts it: leave start out. - Dependencies are installed and builds run on the server, not on your machine. - Static files: a PHP or static site is served from its web_root (found when left out). A Python or Node app serves its own files, or set web_root to a directory of the release, like public: the web server sends the files there before the app's routes. - Every deploy is a new release directory: files the app writes into its own tree at run time are gone with the next deploy. Uploads, generated files and SQLite go to DATA_DIR (an environment variable, set for run_command and the release command too); it outlives deploys and rollbacks and goes only with the app. - A framework that writes inside the project (Laravel storage/, WordPress wp-content/uploads): list those paths in hosting.toml, shared = ["storage", "public/uploads"]. In every release they are links into DATA_DIR; the first deploy moves what the project has there. - An app sleeps after about 5 minutes without requests: its processes stop (they are not frozen), and the next request starts it again in 1-3 s, a large app longer. - A process stops after its running requests finish (a deploy, set_env, restart_app, sleep). Python gets SIGTERM. Node gets no signal: it exits at once, unless it listens with PhusionPassenger.on('exit', ...) (a global object), then it cleans up and calls process.exit() itself. A process still running 60 s later is killed. - WebSockets are not supported. - Server-sent events (text/event-stream) and other streamed responses reach the browser as they are written. Send a comment line (': ok') right after the headers: in Python and Node the first chunk waits for the next one. While idle, send a comment every 30 s. - A PHP request stops after 300 s; Python and Node requests still running when the web server reloads get 30 s to finish. So a stream can end at any time: use EventSource, which reconnects, and give events an id, so it resumes with Last-Event-ID. - Each open stream is one of the account's requests at once (get_account limits.entry_processes). A WSGI app answers up to 10 requests at once, each in a thread of its process, so a stream holds one; ASGI and Node hold any number. - Over TCP on 127.0.0.1 and ::1 an account reaches only MariaDB (3306), DNS and its sites; between your own processes use unix sockets in your home, or your app's https address. - Database: MariaDB (MySQL protocol); create_database sets DATABASE_URL and DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORD for the app. An account has as many databases as its plan allows, named _; an app uses one. - One MariaDB server serves every account: lock names (GET_LOCK) are shared by all of them, so begin yours with the database's name. A database's user has up to 40 connections at once; an app may run up to 4 processes, so keep each pool at 10 or less. - Backups: a plan with backups (get_account limits.backups) is backed up every night, the home with the databases and the apps' variables, 7 daily and 4 weekly copies; ~/tmp, ~/.cache, ~/restored and builds in progress are left out. A restore puts files into ~/restored/