Author the manifest
Prompt:
Get this app ready to deploy. It has a database, users sign in, it calls Stripe, and anyone can use it.
Also works:
- "Let users upload their receipts."
- "Run the daily report every morning at 6am UTC, and show me how to tell whether it ran."
What your tool does
- Reads the manifest schema, published as the
context://manifest_schemaresource. It also reads the project'ssystem/manifest.json, which records what the project's library folder contains, and writes thepackagesmember from it as Use the library describes. - Follows the
manifest-authorskill. It writes the whole manifest against the schema, declares every managed service the application uses and every outbound host inegress, and submits it withsubmit_manifest. - When a submission is refused, corrects the JSON path the refusal names and resubmits.
- For a stored value the code reads itself, such as a webhook signing secret, binds the stored name to a setting in
settings. An outbound application programming interface (API) key goes through the egress gateway instead. - For an outside API the backend calls with a stored key, declares the upstream in
upstreams, so every copy of the application gets it from the manifest (Declare an upstream in the manifest). - For an
invitedorworkforceapplication that receives a provider's callback, such as a payment webhook, lists the callback's path in the audience'ssession_free_paths. The platform lets every request to that path through without a session, so the route checks the provider's signature itself. - Keeps the manifest in the project as
manifest.json, which goes into the artifact the deploy uploads. A deploy runs under the manifestsubmit_manifestrecorded. The copy in the artifact is optional: the deploy compares it with the recorded manifest and never uses it. Where the two differ the deploy returnsmanifest_noticeand refuses nothing, so the tool resubmits after each edit. - For a service added later, such as file uploads, follows the
add-serviceskill: it adds the entry toservicesand resubmits the whole manifest. Adding a service later lists what each kind provisions. The next deploy adds the new service's settings to the environment it reaches, such as the database connection string or the realm's public keys. - When a submission first declares the database kind, and only for a run on your machine, names
local_run: trueand runs the provision line the response returns. The line writes the two development credentials into your environment file, and neither value appears in the conversation. - For scheduled work, reads the plan's
schedule-minimum-intervalandschedule-count-limitwithread_plan_quotas. It then follows thescheduleskill: it adds ascheduleentry with a name, a five-field UTC cron expression, and an absolute handler path, submits, and deploys. On an application with one environment, that deploy starts the schedules in production. With two, it promotes to production only when you ask, never on its own, and the promote starts the schedules there. - Writes the handler at the declared path as an ordinary
POSTroute, which the platform invokes through the serving router. The Schedule package's router is one way to write it. The Node.js Runtime harness returns 404scheduled_handler_onlyto any other caller, as the package's router does. The handler records the run's key before it does its work and finishes inside thewindow_secondsthatread_schedulesreports. - Calls
read_schedulesfor each schedule's state, next due time, and recent runs. It reads run outcomes withread_logsandsource: "platform", and starts one run now withrun_schedulewhen you ask. - Asks for no browser approval:
submit_manifest,deploy, andrun_scheduleare reversible-tier actions.deployandrun_schedulestart work that finishes in the background. Withwait_seconds, each response waits until that work ends, for at most 45 seconds; otherwise the tool reads the outcome withread_statusandread_schedules.
What you need
- A description, in your own words, of what your application uses: which managed services, its health check address, the outside services it calls, and who is allowed to use it.
Before your AI starts
This section is for your AI tool: what it checks and gathers before it begins. You don't need to do these steps yourself.
- A connected, signed-in tool (Connect your tool).
- An application and its identifier. Create it with
create_applicationas step 1 of Deploy an application describes;list_applicationsreturns the identifier. - Your tool writes the members.
- For scheduled work, the application's plan, which sets the shortest interval and the number of schedules.
Steps
You never need to ask for the manifest by name. Your tool writes it from what you say about the application. It also changes the manifest as part of a larger task: when a feature needs a database, your tool declares the service as one step and resubmits. The manifest explains each member. This page shows a working manifest and how to read what your tool wrote.
A manifest that works
A test validates the sample below against the manifest schema.
{
"manifest_version": 1,
"services": [
{ "kind": "database" },
{ "kind": "accounts" }
],
"health": "/health",
"region": "usa",
"egress": ["api.stripe.com"],
"audience": { "kind": "public" },
"packages": [
{ "name": "database", "version": "0.10.2" },
{ "name": "account", "version": "0.17.1" }
],
"settings": {
"STRIPE_WEBHOOK_SECRET": { "secret": "stripe-webhook-secret" }
}
}
Your tool writes the packages member from the project's system/manifest.json at each submission; Use the library describes the copy and shows a row beside its entry. Each deploy compares the list with the package copies under the zip's lib/. Its manifest_notice lists each copy whose turnzero.entry and version the list does not contain, and each copy it left unread or could not compare (Build and upload the artifact). A package.json object with no turnzero, or with a turnzero object that has no entry, such as your own package's, triggers no notice.
The manifest declares no environments. A new application has one, production, and create_environment adds development (Manage versions and environments). A manifest with an environments member is refused environment_unsupported. Applications and environments explains what each environment has.
Paths that begin with /__account/ or /__router/ belong to the platform on every application address, so build no routes under them. A manifest that names such a path, as a scheduled job's handler or a path open without sign-in, is refused.
Choosing the health path
The health member is required. It is an absolute path on the application's own address, conventionally /health, and never a full URL. At each deploy and promote, the platform sends GET to it every three seconds for up to 180 seconds. Each probe waits at most ten seconds for the response to begin, and the check passes only on status 200 exactly. The check follows no redirect, so the path itself returns 200, and a 3xx counts as a failed probe.
The check reads only the status code. The body can hold APP_VERSION, other fields, or nothing. Where the check fails, the deploy's record keeps the first 512 bytes of the last non-200 body as evidence.
The path in the manifest must be the path your server routes, character for character. /health and /healthz are two different paths, and a server that routes one fails the check for the other.
The turnzero-cloud command warns before its upload where no file of the zip names the path's last part. The platform refuses a deploy to an environment that serves no version yet on a narrower reading than the command's warning. Such a deploy is refused health_path_unserved before anything is built, where no source file of the zip names the path's last segment.
The segment counts with its slash, as in '/health', or bare inside quotes, as in @Controller('health'). A file whose own path holds it, such as routes/health.js, counts too. package.json and the lock files never count. Documentation counts only where its own path ends with the whole health path, such as public/health.txt for /health.txt. Any source file that names the path passes, a comment included.
Serve the path from the backend's own server, from the moment the server listens. Return 503 until the application can take requests, never a 4xx, then 200. A route mounted late then never ends the check early, because a 503 never counts toward it. A server with a database can wait to listen until its migrations finish: until it listens, the check treats it as starting and keeps waiting.
The platform ends the check early on a stable client error from your own process. That happens when twenty probes in a row return the same 4xx status, other than 408, 425, or 429, and a control probe sent next shows that your own process is responding. So a path the backend does not serve fails about a minute in. A 5xx response or a transport error, such as a failed or timed-out connection, never counts toward the twenty.
Under an invited or workforce audience, the platform's own check still reaches the health path. It calls your application from inside the platform and never passes through the sign-in check. A request to the health path from outside, without a session, is treated like a request to any other path. It is refused 401 authentication_required, or sent to the sign-in page in a browser.
To open the health path to everyone, list it in the audience's session_free_paths. Anyone can then read whatever the path returns, so return nothing there you would not publish. Two limits apply to the list: / alone is refused, and a listed path also opens every path below it. Add sign-in to your app gives the list's other rules. The health member of submit_manifest's response says whether the path is gated: its gated is true where a request from outside without a session is refused.
Deploy an application describes the check and the evidence a failed check records.
Writing the egress list
List every host the backend calls on the open network. Write each as an exact lowercase hostname, or as a host family with one leading wildcard label. *.example.com covers every name below example.com at any depth, but not example.com itself; list that separately if the backend calls it.
Write no scheme, port, or address. The tunnel reaches port 443 only, and Egress firewall and request limits states its other rules. A host the application calls with a stored key does not go here: declare it as an upstream instead (Call an external API with an API key). The platform's own endpoints need no entry.
In observe mode, read_logs with source: "egress" and filter: "undeclared" shows the hosts the application reached that the list does not include. Add the ones that belong before platform staff move the application to enforce mode.
Bind a stored value to a setting
Some stored values your code reads itself, such as the sample's Stripe webhook signing secret. The optional settings member binds each one to a setting. Each key is the setting name, and secret is the name the value is stored under. The member has at most fifty entries.
Store the value with store_secret, as Store a secret describes, at each environment's scope of the application, under the same name (Bind a value to a setting). Each deploy and promote reads the value stored for its environment and injects it into the container. A replaced value takes effect at the environment's next deploy or promote, or at a restart_application where the running copy already has the binding.
The submission's settings rows say, per setting and environment the application has, whether the name is stored there yet. A deploy or promote is refused setting_secret_missing while its environment's value is missing.
A setting name is an upper-case letter, then upper-case letters, digits, or underscores, ending in a letter or a digit. A name the platform reserves is refused manifest_invalid at the setting's own path. That covers any name under TURNZERO_, APP_, HOSTING_, EGRESS_, ROUTER_, NODE_, or NPM_, and PORT, PATH, HOME, HTTPS_PROXY, HTTP_PROXY, and NO_PROXY.
Some stored names are never bound, and the Refusals table gives each cause:
- a name at account scope or at another application's scope;
- a name the platform created, such as the platform credential;
- an upstream's key, which the gateway applies and never gives to the application;
- a sign-in method's or a push provider's credential.
For an outbound API key, declare an upstream instead (Call an external API with an API key). The gateway keeps the key out of the container, and a replacement needs no redeploy.
Declare an upstream in the manifest
An upstream is an outside API your backend calls through the platform's gateway, which adds the stored key on the way. The optional upstreams member declares each one in the manifest. Each key is the upstream's name, and each entry takes the members declare_upstream takes except application. The member has at most fifty entries.
An entry for an upstream whose running copies have a key setting must include settings with its key. Otherwise the submission ends those keys in each environment, and its response says so.
A test validates the sample below against the manifest schema.
{
"manifest_version": 1,
"services": [],
"health": "/health",
"region": "usa",
"egress": [],
"audience": { "kind": "public" },
"packages": [],
"upstreams": {
"openai": {
"base_url": "https://api.openai.com",
"credential_name": "openai-key",
"auth_header": "Authorization",
"auth_format": "Bearer {value}",
"settings": { "base_url": "OPENAI_BASE_URL", "key": "OPENAI_API_KEY", "base_path": "/v1" }
}
}
}
Each submission declares every entry for the application, from that moment, as declare_upstream would. Each deploy and promote then sets the entry's settings in the new copy. So re-creating the application, or copying it into another account, takes this manifest, a store_secret for each key, and a deploy. A submission refused after the manifest is recorded lists in its detail the upstreams it declared and those it did not. Submitting again, with any change the detail asks for, declares the rest (Refusals).
A key not stored yet is accepted. The submission's upstreams rows say, per upstream and environment, whether the key is stored there. A deploy still succeeds, and its version's outcome.credentials_missing lists each upstream whose key its environment lacks. The gateway refuses that upstream's calls until store_secret stores the key.
Upstream names are unique within your account. An entry naming an upstream another application owns is refused at its own path, such as /upstreams/openai, and the line names that application. A copy beside the original in the same account gives its upstreams names of their own.
While the member names an upstream, declare_upstream refuses to change it, manifest_owned_field, and its detail names each field. Change the entry and resubmit instead. Removing an entry leaves the upstream declared, and declare_upstream can change it again.
No submission ends an upstream, so a running version that still calls it keeps working. To end one, remove its entry and submit the manifest, then deploy and promote the version that no longer calls it. Then call undeclare_upstream with its name, which manifest_owned_field refuses while the member names it. Within the gateways' short cache interval, its calls are refused in both environments (End an upstream).
Configure sign-in and push in the manifest
The optional realm member sets up sign-in for every end-user realm the application has at once. It takes the members of configure_realm that are the same in every environment: sign_in_methods, creation, entra, and apple. The manifest's services must include the accounts kind. A sign_in_methods list naming passkey names email beside it, as in the sample below; otherwise submit_manifest refuses it manifest_invalid, naming passkey_not_sole_route.
A push entry in services may also name its providers, apns and fcm, with the members configure_push takes. Apple's environment is left out, because the gateway differs by environment. Development uses sandbox and production uses production until configure_push sets another.
A test validates the sample below against the manifest schema.
{
"manifest_version": 1,
"services": [
{ "kind": "accounts" },
{
"kind": "push",
"apns": { "team_id": "ABCDE12345", "key_id": "KEYID12345", "bundle_id": "com.example.app", "key_secret_name": "apns-key" },
"fcm": { "project_id": "example-app-12345", "service_account_secret_name": "fcm-service-account" }
}
],
"health": "/health",
"region": "usa",
"egress": [],
"audience": { "kind": "public" },
"packages": [],
"realm": {
"sign_in_methods": ["google", "apple", "email", "passkey"],
"creation": "open",
"apple": { "services_id": "com.example.app.signin", "team_id": "ABCDE12345", "key_id": "SIGNKEY123", "key_secret_name": "apple-signin-key" }
}
}
For one company's staff, declare the workforce audience and the realm's entra route together, with the same tenant. The audience decides who may reach the app, and the route is what signs them in.
A test validates the sample below against the manifest schema.
{
"manifest_version": 1,
"services": [{ "kind": "accounts" }],
"health": "/health",
"region": "usa",
"egress": [],
"audience": { "kind": "workforce", "tenant": "00000000-0000-4000-8000-000000000001" },
"packages": [],
"realm": {
"sign_in_methods": ["entra"],
"entra": { "tenant": "00000000-0000-4000-8000-000000000001", "client_id": "00000000-0000-4000-8000-000000000002", "client_secret_name": "entra-client-secret" }
}
}
Each submission applies these members to each environment the application has, from that moment, as the two actions would. On an application with one environment, that is production alone, and create_environment applies them to development when it adds it. So re-creating the application, or copying it into another account, takes this manifest, a store_secret for each credential, and a deploy. Each credential member names a stored secret, never its value.
A credential not stored yet is accepted. The submission's detail names each credential not stored yet, with the environment where only one environment lacks it, and the detail of create_environment names each one development lacks. A deploy still succeeds, and its version's outcome.provider_credentials_missing lists each credential its environment cannot use yet. A sign-in credential stored after a submission is used once you submit the manifest again.
While the manifest names a field, configure_realm and configure_push refuse to change it, manifest_owned_field, and the detail names each field. Change the manifest and resubmit instead. A realm's limits, session lengths, invitation days, and native clients stay with configure_realm, and Apple's gateway with configure_push, because they differ by environment.
A submission refused after the manifest is recorded lists in its detail the realms, or the push configurations, it set up and those it did not. Submitting again, with any change the detail asks for, sets up the rest. A create_environment refused the same way says what became of the member on development, and whether to call it again or submit the manifest (Refusals).
Adding a service later
The add-service skill adds the entry to services and resubmits the whole manifest. What the submission provisions depends on the kind:
database: the development database and its role at the submission, which local runs use, and the production database at the first deploy or promote that reaches production while the manifest declares the kind.accounts: one end-user realm for each environment the application has, at the submission: production's alone on an application with one environment.issue_tracking: one issue-tracking space for both environments, at the first submission. An application that has a space for each environment keeps them, its production space created at the first promote, or at its first production deploy where it has one environment (Issue Tracking). The entry may instead name aspaceyour account created withcreate_issue_space, and alevel,reportorcontribute. The application is then bound to that space and nothing is created. Another account's space, or an application's own space, is refusedspace_not_owned.object_storage: nothing. Declare each storage area before use, withdeclare_storage_areaor from the application itself (Store files). The storage routes work for a declared area with or without this entry.push: nothing. The declaration allowsconfigure_pushand the device and send routes; configure each environment's providers before sending (Send push notifications).schedule: the schedules are updated at the submission. Each runs in an environment after that environment's next deploy or promote (Run scheduled work).
The schema also accepts email and custom_domain. Declaring either provisions nothing.
For the prompt "Let users upload their receipts", the entry is object_storage. The submission provisions nothing, and your tool declares the area and writes the upload route.
When a submission first declares the database kind, the platform creates the development database credential. Over the Hypertext Transfer Protocol (HTTP) API, that submission returns the credential once where it names no local_run. Through the Model Context Protocol (MCP) tool, and over the HTTP API where it names local_run, the response withholds the value. A submission naming local_run: true returns the provision line. The line fetches both credentials again and writes them to your environment file without printing them. The development platform credential is created at the application's first submission only. Run locally lists the file's settings.
Over the HTTP API, a response that withholds the values and returns the line says where they are in provisioning.next. The line writes them, a later submission naming local_run returns a new line, and rotate_secret on the development scope returns each new value once.
Removing a database or accounts entry does not delete the database or the end-user realm. The manifest states what a removal leaves in place. Removing a schedule stops its future runs, and its run records are kept for the run-history retention period.
Reading a refusal
A validation refusal lists the JSON paths to correct in its violations member, such as /egress/3 for a malformed hostname. Your tool corrects each path and resubmits. A service or an audience is checked against the shape its own kind names, so its violations are that shape's alone. Where no shape has that kind, one line at its kind path lists the kinds there are.
For example, a schedule named Hourly Tick is refused with one line, at /services/0/schedules/0/name, giving the naming rule. The refusal says nothing about the other kinds' members. The push kind has two shapes, with and without its providers. An entry is checked against the shape that defines more of the entry's members, so a provider's mistake is still reported beside a stray member.
An issue_tracking space is checked as one identifier or as a development and production pair, whichever form its value takes. A service entry that is not an object is refused with one line.
A path in the audience's session_free_paths that breaks a rule is refused at its own place, such as /audience/session_free_paths/0, and the line names the rule. Add sign-in to your app lists the rules.
An upstreams entry that breaks a rule declare_upstream applies is refused at its own place, such as /upstreams/openai/base_url. The line names the refusal declare_upstream would give, such as refused_platform_host, and its reason. Every entry's line comes in the same response, and nothing is recorded.
Some refusals give a cause instead of a path: region_unavailable, declared_tenant_contradicts_route, declared_audience_contradicts_route, and the refusals of a binding in settings. They come before anything is provisioned, and the Refusals table gives each cause and remedy. usa is the one region available during the beta. The schema also lists europe, uk, and global, which submission refuses as region_unavailable.
A provisioning failure can happen after the manifest is recorded. For example, database_provisioning_failed means the database setup did not finish. The response says the manifest is still recorded and that resubmitting completes the setup. Read the response before retrying.
A later submission does not return the database credential again. When the manifest declares the database kind, its note mentions rotate_secret on the development scope. That call creates a replacement development database credential and returns it once.
Run scheduled work
For the prompt "Run the daily report every morning at 6am UTC, and show me how to tell whether it ran", your tool adds a schedule service entry. Each schedule has:
- a unique name matching
^[a-z][a-z0-9_]*$, sohourly_heartbeatpasses andhourly-heartbeatis refusedmanifest_invalid. That takes underscores and no hyphens, the reverse of an application's name, which matches^[a-z][a-z0-9-]{0,39}$and takes hyphens and no underscores; - a five-field cron expression in UTC: minute, hour, day of month, month, and day of week, so
0 6 * * *means 06:00 UTC every day; - an absolute handler path.
In the entry, schedules lists them, each with name, cron, and path, the handler path. The sample below declares the prompt's daily report beside the Schedule package's row.
A test validates the sample below against the manifest schema.
{
"manifest_version": 1,
"services": [
{
"kind": "schedule",
"schedules": [
{ "name": "daily_report", "cron": "0 6 * * *", "path": "/jobs/daily-report" }
]
}
],
"health": "/health",
"region": "usa",
"egress": [],
"audience": { "kind": "public" },
"packages": [
{ "name": "schedule", "version": "0.2.9" }
]
}
The expression accepts numbers, ranges, lists, steps, and *. Month names, day names, macros, and a seconds field are not accepted. When both day fields are restricted, a date matching either one is eligible. An expression with no due time in the next year is refused.
Before the first submission, read the plan's shortest interval and schedule count with read_plan_quotas, as its schedule-minimum-interval row, in minutes, and its schedule-count-limit row. Choose a frequency within both. The platform checks both at submission and at a plan change:
- A schedule that runs more often than the minimum interval is refused
schedule_interval_below_plan. The interval applies in both environments. - More schedules than the count limit are refused
schedule_count_over_plan. - A plan change to a plan whose limits the current schedules exceed is refused
plan_schedule_conflict.
One schedule entry contains up to 25 schedules. Do not use the health path, or a path under /__account/ or /__router/, as a handler path.
The handler is an ordinary POST route, which the platform invokes through the serving router with an empty body and headers that identify the run. The Node.js Runtime harness returns 404 scheduled_handler_only to any other caller, so a declared handler path is not a public endpoint. A handler written without the Schedule package's router also checks for x-turnzero-cloud-invocation: schedule and returns 404 otherwise. A handler without the package shows one on plain node:http.
The handler must be safe to repeat for the same schedule and due time. It must finish within window_seconds, cold-start time included, which each schedule row of the submit_manifest response includes, as read_schedules does. When the request's abort signal fires, the handler stops its work, and it starts no timer, polling loop, or child process that outlives the response. The Schedule package's router is one way to write the handler: it reads the headers, routes the run to its handler, and supplies the key that makes it safe to repeat.
Each run includes four headers the serving router alone sets: x-turnzero-cloud-invocation with the value schedule, x-turnzero-cloud-schedule naming the schedule, x-turnzero-cloud-run containing the run identifier, and x-turnzero-cloud-schedule-due containing the due instant in UTC. Any other header whose name begins x-turnzero-cloud- is the platform's own: the handler never logs or echoes one, and never logs a request's headers whole. The platform removes the router's mark from every request before the handler runs.
The router deletes any copy of those four headers from a client's request and sets them only on the request it composes for a scheduled run, so a request from outside cannot include them. The header is all the handler itself checks: there is no signature or token for it to verify, because the platform has checked where the request came from before the handler runs. The handler's own check still matters where a framework serves the route more widely than the exact declared path, such as a prefix mount or a wildcard.
Submit the manifest, then deploy. Submitting starts no run. The platform keeps one row per schedule in each environment. A row runs only in an environment that has a deploy or promote made at or after the declaration. On an application with one environment, that is the deploy to production, so a new or changed schedule starts after the next deploy. With two environments, it is the deploy for development and the promote for production. The schedule starts in development after the next deploy, and in production after the next promote, which your tool makes only when you ask.
The submit_manifest response says when each row starts. While the row waits, its next_due is null and its starts_with names what it waits for. That is the next deploy to production on an application with one environment, and the next deploy to development or the next promote to production with two. Its value is already running once the environment has a deploy or promote made at or after the declaration, with next_due set. Where that environment is halted, its value is the environment's resume instead, with next_due null, since no row of a halted environment runs until the resume.
To check a cron, call read_schedules. Every row, before its deploy and after it, includes cron_preview: up to three run times the cron yields after the read, in UTC. An expression that yields fewer than three within a year shows fewer. For 0 * * * *, they are the next three hour marks. The member says what the cron yields, not that a run will start. A schedule fires only from its deploy, so a run time before that deploy is skipped, and a halted environment starts no run.
A due run wakes a stopped environment, such as a Free application after its idle stop, and the time to wake counts against window_seconds, so leave the handler room for it. On Free production, the hosting provider's own rule decides when an idle application stops. Measured on the platform, a run that woke its application took 23 to 42 seconds, and 94 seconds for the first run after a resume.
Keep a handler's state between runs in a JSON file in a declared storage area, written with a conditional put, or in the database where one is declared. Counters are write-only tallies, never state. The Schedule page shows a handler that keeps its state in a file.
A schedule runs against a deployed environment, never against a process on your machine. In a test, the schedule double starts a run (Test your application locally).
What an application must do on the Schedule page says what read_schedules shows, how run_schedule starts a run now, and how each waits for a run to end. Use the run details and the logs to investigate a failure.
The platform does not retry a failed or missed run. It skips a run in these cases:
- another run of the schedule is in progress;
- the account is suspended;
- the environment has no eligible deploy;
- the application is over its plan's monthly limit for backend actions or data transfer (Plan and usage). The serving router then records the run
skippedwithusage_over_quota, before any handler runs. This ends at the start of the next UTC month, or at once when a larger plan or quota covers the usage.
While an environment is halted, its schedules do not run, and each next due time stays where it was. A resume restarts every schedule from the moment of the resume, and no missed run is recorded for the halt. A deploy to a halted development environment ends its halt and restarts its schedules from the deploy the same way. run_schedule on a halted environment is refused 409 target_environment_halted.
A run that is too late to start is recorded as missed and not run later. A run with no recorded end after its window is recorded abandoned, and its outcome is unknown.
Expected result
submit_manifest returns outcome: "recorded" with the application identifier. Other members depend on what the manifest declares:
| Member | Returned when | What it contains |
|---|---|---|
detail |
Every submission through the MCP tool. Through the HTTP API, where the submission has something to report. | What happened, one fact per line, each line a whole sentence: a line for each service the submission set up, and one naming each credential not stored yet. For the push kind with no provider named, the line says to store each provider's credential and then call configure_push. Through the HTTP API, a line for each credential value the answer returns says where to put it, since it is returned once. The last line points to the section What a submission provisions on the page in page. A detail that would pass 600 characters leaves lines out, counting each list of your own names at its first name. Lines whose facts a member or a read gives leave first, in a fixed order, and the pointer last. Lines on the database's connection limit, push, deploys owed, ended upstream keys, names not stored yet, values returned once, and local runs always stay. |
page |
Beside detail. |
The address of the Deploy an application page, which explains each line and how to run the application on your machine. Your tool can pass it unchanged to read_documentation. |
development_database |
The manifest declares the database kind and the development database exists. | host, dbName, roleName, and connection_setting, which is APP_DATABASE_URL. |
connection_limit |
Beside development_database. |
The client pool's maximum: the connections each process may hold open at once. |
credentials |
The call created a development credential. | credentials: "withheld" through the MCP tool, and over the HTTP API where the submission names local_run. Otherwise credentials: "answered" with the values over the HTTP API. |
realm |
The manifest declares the accounts kind. | The production realm, and whether it was created or confirmed. |
issue_tracking |
The manifest declares the issue_tracking kind. | A list with one entry per space the application's calls reach. space is the space's identifier. scope is application for the application's own space, account for a space of your account, or an environment's name for an older space of one environment. environments lists the environments that reach it. outcome is created, confirmed, or bound. level is the grant level the gateway allows there. note appears only where a space of your account already holds its 50 release lines (the named lines a space records builds on), and says why no release line was declared for the application. No setting and never a token: the backend reaches its space through the egress gateway. |
schedules |
The manifest declares the schedule kind, or a resubmission without the kind ends its schedules. | One row per schedule in each environment, with starts_with, next_due, and window_seconds, the seconds a run has before the platform ends it. Its starts_with gives the next deploy or promote the row waits for, with next_due null, or is already running, with next_due set, once the environment has it. A halted environment that has it shows the environment's resume, with next_due null. starts_with and next_due are null for an ended row. detail then lists each environment that needs a deploy. Every row of read_schedules shows up to its next three run times in cron_preview, before that deploy as well as after it, though a row starts no run before its deploy. |
settings |
The manifest binds a setting in settings. |
One row per setting and environment: the setting, the secret, the environment, and stored, whether the name is stored at that environment's scope. The rows cover only the environments the application has. detail lists each name not stored yet, for the environments the application has. |
After the deploy, read_schedules shows each row of the environment it reached with deployed: true, its next_due, and reason: null. On an application with two environments, the production rows show the same after the promote. run_schedule returns a run whose outcome is running, or with wait_seconds the run's own outcome where it ended inside the wait. read_schedules then shows that run under last_run with its outcome, status, and duration_ms.
Refusals
A refusal gives its cause in detail. A submission refused before the manifest is recorded changes nothing.
Six refusals leave the manifest recorded: plan_quantity_unset for a database, database_provisioning_failed, server_unavailable, cell_not_configured, and, for the issue_tracking kind, not_yet_provisioned and issue_service_unreachable. Resubmit once the cause clears; the submission then provisions what is missing. Any realms or databases the same submission created stay in place, because the issue_tracking kind is provisioned after every other kind.
Three more refusals leave the manifest recorded, each for an upstreams entry: binding_refused, upstream_key_is_bound, and store_unavailable. The entries are declared after the manifest is recorded, one at a time in name order, and the declarations stop at the first entry that does not complete. The database, realm, and push steps then wait for the next submission. The detail lists the upstreams declared, those not, and any the platform wrote and could not take back, which the next submission checks again. Submit again before the next deploy or promote, because an entry not yet declared sets nothing in the new copy.
Five more leave the manifest recorded, for the realm member or a push entry's providers: name_bound_to_setting, custody_entry_missing, custody_entry_unmovable, realm_not_declared, and store_unavailable. After the upstreams, each environment's realm is set up, then each environment's push configuration, and the steps stop at the first environment that does not complete. The detail lists the realms or push configurations done and those not. A store failure in these steps takes nothing back, and the next submission checks the current state again. Make the change the detail asks for and submit again.
The table covers submit_manifest, read_schedules, and run_schedule. It also includes manifest_missing from deploy, and one refusal each from the serving router when it skips a run and from set_plan. A deploy or promote applies the same rules to each binding, and refuses one whose name is not stored at its environment's scope with setting_secret_missing. The refusals page lists every refusal.
| Refusal | Status | Cause | Remedy |
|---|---|---|---|
manifest_invalid |
400 | The manifest does not match the schema; violations lists the failing paths. An upstreams entry's line also names the refusal declare_upstream would give it. A realm member's or push entry's line names the refusal of configure_realm or configure_push. |
Correct each path and resubmit the whole manifest. |
environment_unsupported |
400 | The manifest has an environments member. |
Remove it; create_environment and delete_environment set an application's environments. |
region_unavailable |
400 | region is not usa. |
Set region to usa. |
invalid_request |
400 | A member is malformed or missing, and detail names it. For read_schedules, the environment is not development or production. For the issue_tracking entry, a level with no space, or a space given by an application that kept a space for each environment or has its own. Nothing is recorded. |
Correct the named member. |
level_invalid |
400 | The issue_tracking entry names a space with a level other than report or contribute. Nothing is recorded. |
Name report or contribute, or leave level out for report. |
space_not_owned |
403 | The issue_tracking entry gives a space your account cannot bind: an application's own space, which binds only through its own application's manifest, or a space your account does not have. Nothing is recorded. |
Name a space of kind account that list_issue_spaces returns, or leave space out and the application gets a space of its own. |
no_such_application |
404 | application names no application of your account. |
Use the identifier list_applications returns. |
no_such_schedule |
404 | The application has no schedule of that name in that environment. | Name a declared schedule, and name the environment when it is not production. |
realm_not_declared |
404 | An end-user realm was deleted while this manifest was being recorded, so it was not set up. The manifest stays recorded. | Submit the manifest again. |
manifest_missing |
409 | deploy found no manifest for the application. |
Submit the manifest first. |
declared_tenant_contradicts_route |
409 | The workforce audience gives a different tenant from the work-account route of one of the application's realms, and the refusal's environment names which. |
Declare that route's tenant, or call configure_realm for the named environment with the audience's tenant (Manage end users). |
declared_audience_contradicts_route |
409 | The invited audience is declared while a realm's sign-in methods include entra, which an invitation-only realm does not allow. |
Remove entra from the realm's sign_in_methods with configure_realm (Manage end users) and submit again, or keep the current audience. |
setting_scope_refused |
409 | A binding in settings names a secret stored at account scope or at another application's scope, which no deploy of this application reads. |
Store the value under a new name at this application's scope, in each environment, and bind that name. |
platform_minted_name |
409 | A binding names a credential the platform created for one of your applications. | Bind a name of your own that you stored with store_secret. |
setting_is_upstream_key |
409 | A binding names the key an upstream of the application declares; the gateway never gives that key to the application. | Call the upstream through the gateway, or bind a value stored under another name. |
name_bound_to_realm |
409 | A binding names a realm sign-in method's credential, the work-account client secret or the Apple signing key. | Bind a value stored under another name; a sign-in method's credential stays the realm's. |
name_bound_to_push |
409 | A binding names a push provider's credential of the application's push configuration. | Bind a value stored under another name; a provider's credential stays the push service's. |
binding_refused |
409 | Another application of your account declared an upstreams entry's name while this manifest was being recorded, and application names it. The manifest stays recorded. |
Rename the entry and submit the manifest again before the next deploy or promote. |
upstream_key_is_bound |
409 | A binding of an upstreams entry's key was recorded while this manifest was being recorded, so the platform undid this submission's change to that entry where it could. The detail names an entry it could not undo. The manifest stays recorded. |
Store the key under another name and change the entry, or remove the binding, then submit the manifest again before the next deploy or promote. |
name_bound_to_setting |
409 | A binding of a sign-in or push credential the manifest names was recorded while this manifest was being recorded, so the platform took that credential back where this submission set it. The manifest stays recorded. | Store the credential under another name and change the member, or remove the binding, then submit the manifest again. |
custody_entry_missing |
409 | A credential the realm member names was deleted while this manifest was being recorded, so that environment's realm was not set up. The manifest stays recorded. |
Store the value again, or under a new name the member then names, and submit the manifest again. |
custody_entry_unmovable |
409 | The platform's store for sign-in credentials already contains a credential the realm member names, at another version, so that environment's realm was not set up. The manifest stays recorded. |
Store the value under a new name, change the member to it, and submit the manifest again. |
setting_secret_missing |
409 | A deploy or promote found a bound name not stored at its environment's scope; detail names the setting and the name. |
Store the value there with store_secret, as Store a secret describes, naming the application and the environment, then deploy or promote again. |
schedule_interval_below_plan |
409 | A schedule runs more often than schedule-minimum-interval. path names it, quantity gives the minimum in minutes, and gap the shortest gap found. |
Run it less often, or move to a plan with a shorter minimum. |
schedule_count_over_plan |
409 | The manifest declares more schedules than schedule-count-limit. path names the first one over, and quantity gives the limit. |
Declare fewer schedules, or move to a larger plan with set_plan. |
plan_schedule_conflict |
409 | A plan change targets a plan whose limits the current schedules exceed. schedules lists each one with its value and the limit. |
Resubmit the manifest within the new plan's limits first, or choose another plan. |
plan_quantity_unset |
409 | The plan has no value set for a limit the request needs; plan and measure name them. The manifest stays recorded for a database, and nothing is recorded for a schedule. |
Choose a plan whose limits are set, or wait for platform staff to set it. |
deletion_in_progress |
409 | The application or one of its environments is being deleted. | Wait for the deletion to finish; read_pending_action shows whether it completed. If it failed, request the same deletion again to complete it. |
schedule_not_deployed |
409 | run_schedule named an environment with no deploy or promote since the schedule was declared. |
Deploy to that environment, or promote to production on an application with two environments, then run again. |
environment_not_created |
409 | run_schedule named development on an application with one environment. |
Run it in production, or turn development on with create_environment and deploy there first. |
run_in_flight |
409 | A run of the schedule is still in progress. A due time during it is skipped, not queued. | Wait until read_schedules shows the run ended, then run again. |
target_environment_halted |
409 | run_schedule targeted a halted environment. |
Resume it with resume_environment, then run again. |
usage_over_quota |
429 | The application is over its plan's monthly limit, so the serving router skipped the run and recorded it skipped. |
Wait for the next UTC month, or move to a larger plan or raise the quota. |
not_yet_provisioned |
501 | The manifest declares issue_tracking and the platform's issue service is not configured; the manifest stays recorded and the other kinds are provisioned. |
Remove the kind until the feedback actions are listed, or resubmit later. |
database_provisioning_failed |
502 | Creating the database role or password failed; the manifest stays recorded. | Read the response, then resubmit to finish the setup. |
issue_service_unreachable |
503 | The manifest declares issue_tracking and the platform's provisioning credential is not set, or the issue service rejected the space-creation call; detail contains the cause word. The manifest stays recorded. |
Resubmit after a short wait; nothing in the manifest causes it. |
server_unavailable |
503 | No database server has room for the database now. Or an earlier, unfinished setup recorded a server that no longer takes databases, and the setup cannot move to another server while that record exists. The manifest stays recorded. | Where detail does not name a server recorded by an earlier run, resubmit later; changing the manifest does not help. Where it names one, resubmit once, since that run may still be finishing. If it repeats, delete_environment naming development removes the setup, with development's data and secrets, on one environment or two. A resubmission then sets it up again; on two environments, call create_environment before it. If that deletion fails, report the refusal with its detail, and resubmit once platform staff have removed the earlier setup or opened the server again. |
cell_not_configured |
503 | The platform cannot place the database now; the manifest stays recorded. | Nothing on your side corrects this: report the refusal with its detail, and resubmit once platform staff have configured the cell. |
store_unavailable |
503 | The platform's store did not respond while the upstreams entries were being declared, or while the realms or push configurations were being set up. The manifest stays recorded, and detail lists what was done and what was not. |
Submit the same manifest again, before the next deploy or promote. |
Related
- The manifest explains each member and what submitting provisions.
- Deploy an application covers the deploy that follows a submission. Running on your machine describes the line that writes the environment file for a local run.
- Add a database covers the database kind: the connection setting, the migrations, and the credentials.
- Call an external API with an API key declares an upstream for a host called with a stored key, which the egress list does not include.
- Manage end users covers the accounts kind and the audience member.
- Schedule describes what a run delivers to the handler, the run headers among them, and what the handler must do.
- Egress firewall and request limits explains the observe and enforce modes for the egress list.
- Read logs and counters reads the egress records and the run outcomes.
- submit_manifest, read_schedules, and run_schedule in the generated reference give each action's arguments and result.
- Glossary defines the terms used on this page.