deploy
Generated automatically from the published contract sources.
Build metadata: Registered in this build. Registration describes the default dispatcher in this build. It does not guarantee that a caller has the required credential or grant, that a tool is listed for that connection, or that the required service is configured.
A script calls this action over HTTPS at POST https://turnzero.ai/api/v1/actions/deploy, with a bearer credential and the action's payload as the JSON body.
Contract description
A folder deploys by this call and the one line it answers, which zips, uploads, and deploys it; a script passes `artifact` to deploy a stored zip. A new application's deploys go to its one environment, production; once `create_environment` adds development, they go there, and `promote` moves a version to production.
Call `deploy` with the application, naming no environment and neither `artifact` nor `upload`. Name `local_path` where the line will run from another folder. It answers at once with the state `awaiting_command`, `command`, one line, with `command_windows`, its Windows form, and `next`, a `read_status` call. Run that line once, as given, from the application's folder, on Node.js 24 or later. The line carries a one-time code that ends at `expires_at`: treat the line as a credential, run it through your shell tool, and paste it nowhere else. `previous_code` says what became of the application's last code. A job with no tool to make this call runs the command under `TURNZERO_CLOUD_MINTED_TOKEN` instead.
The line zips the folder, uploads the zip, starts its deploy, and waits for it. Allow it sixteen minutes, or set `TURNZERO_DEPLOY_NO_WAIT` in the shell to have it return after the start. Make the `next` call where the command returned early, or to read the whole record: it holds its answer for its `wait_seconds` until the deploy ends. A plan caps the deploys started in 24 hours: `read_plan_quotas` answers the cap as its `deploys-per-day` row, and a deploy past it is refused `deploys_per_day_exceeded`.
The command's start names `commit` where the folder has a Git repository; an `artifact` start may name it. An answer that is not this platform's own, such as a gateway's error page or a closed connection, says nothing about whether the deploy started. Read `read_status` first, and call again only where it shows no start. The rest is on the page /cloud/reference/actions/deploy/, which `read_documentation` reads as `page` and the platform's origin serves.
More about this action
The line pipes a one-time code to the turnzero-cloud command, which zips the folder or takes the named .zip file and then sends the code once, as the bearer of its one preparing call. Its `prepared:` line prints the answered upload's id. It uploads the zip and starts its deploy under the upload's grant, which no answer shows a tool, printing its answer or refusal. It then waits for that deploy, printing each step and the outcome, a failure's cause in the platform's words. It ends 0 for a deployed version, 1 for a failed one, 2 where it stopped before the outcome, and 3 where nothing was started.
A code is used once and ends at `expires_at`. A new call for the application ends its last code where no line has used it, and an upload a code of the application prepared that no deploy has started. The member `previous_code` says what became of the application's last code. Where it names a started upload whose id no `prepared:` line of yours printed, roll back, rotate the application's secrets and its database credential, and report it. A line refused `deploy_code_refused` is answered by calling `deploy` again and running the fresh line. No call names `zip_sha256` over this connection, and one that does is refused `local_route_required`.
While a deploy of the environment is in flight, a call naming the same `artifact` hash, or the `upload` that deploy read, answers it; any other is refused `deploy_in_flight`, an upload of identical bytes included.
Where the upload landed and no deploy read it, `read_status` names it as `pending_upload` on the environment the deploy goes to. Once the refusal's cause is cleared, make the call its `retry` carries; where `retry` is null, call `deploy` again and run the fresh line, fixing the folder first where its zip was refused. A call naming `upload` reads the zip the command wrote and deploys it as the artifact form does. An upload with no such file is refused `upload_not_found`: not uploaded yet, expired unused, replaced by a later preparing call, or already read by a deploy that has ended.
For a script, use the artifact form: upload the zip to a declared storage area under a single-use grant from `mint_upload_grant`, then name its area, file name, and SHA-256 in `artifact`. The `artifact` member's `environment` says where the file is stored, never where it deploys.
The version keeps the commit, its promote and rollback carry it, and so does the build row in the application's issue-tracking space. With `rotate_database_credential` named on the call, the line carries it as a flag, the command's preparing call records it on the upload's grant, and a start of that upload naming none applies it; a refused start's `retry` names it.
The platform then builds the artifact's image with its declared runtime dependencies, starts it, and gates its health. It runs as its own container app on production, and on development as a pod where the cell's development group has room, else a container app (a full group is refused `group_full`). A deploy to a halted production is refused `target_environment_halted`, and one to a halted development environment ends the halt after its refusals.
An answer that did not settle names the `next` call, `read_status`, whose environment's `deploy` member names the `step`. After an answer that is not this platform's own, make that read first: call again only where that member names no `deploy` with a `started_at` later than your call. An upload `pending_upload` still names was not started. The wait takes the application's one held place, so a concurrent held `read_status` answers at once, from its own read.
A deploy typically takes up to about two and a half minutes on a development pod and three and a half on its own container app. Most of it is the image build, up to about two and a quarter minutes for a first version and a minute and a half for a later one; the health gate's own bound is 180 seconds. Each row records its step `timings`, and a failed build's `outcome` carries its last lines.
An external API's stored key goes through `declare_upstream`, whose `settings` let an unchanged SDK reach it; a value the code reads itself is bound in the manifest's `settings`.
Access and action metadata
{
"name": "deploy",
"resource": "environment",
"tier": "reversible",
"summary": "Deploy a built artifact to the environment the application's deploys go to: production on an application with one environment, and development on one with two, where production receives a version through `promote`. Called with the application and none of `zip_sha256`, `artifact`, and `upload`, the line form, it answers 200 with the state `awaiting_command`, `command`, one line that carries a one-time deploy code, `command_windows`, its Windows form, `expires_at`, when the code ends, `previous_code`, what became of the application's last code, and `next`, the `read_status` call to make after, and inserts no version row. Run the line once, as given, from the application's folder, on Node.js 24 or later, and paste it nowhere: it is a credential until `expires_at`. It runs the turnzero-cloud command, which zips the application's folder or takes the named .zip file, prepares its upload under the code, uploads it, starts its deploy under that upload's grant, and waits for it. It ends 0 for a deployed version, 1 for a failed one, 2 where it stopped before the outcome, and 3 where nothing was started. With `TURNZERO_DEPLOY_NO_WAIT` set in the shell, it returns after the start. The command alone names `zip_sha256` and `withdraw`, on the HTTP action route, and over this connection a call naming either is refused `local_route_required`. A job with no tool to make the call runs the command under `TURNZERO_CLOUD_MINTED_TOKEN` instead. Called with that `upload`, by the command's start or by a retry of a start refused or never made, or with an `artifact` in a storage area bound to the application, it starts the deploy and answers 202 `deploying`, or, with `wait_seconds` up to 45, holds its answer until the deploy ends. On development the compute grain is chosen before any compute act: a pod in the hosting cell's admitting development group with headroom, a container app where the cell has none. Production runs as its own container app. A full group is refused `group_full` and a row in a group of another kind `group_kind_mismatch`; a grain that differs from the standing compute's is the grain migration, the previous compute deleted after the router's resolve interval. The hosts a manifest's `egress` member declares are reached in the tunnel mode with no key applied; a host called on a stored key is a declared upstream (`declare_upstream`), its key applied at the gateway's edge, and the container holding at most the upstream's egress key. The start may name `commit`, the commit the code was built from, which the version keeps, its promote and rollback carry, and the build row the platform writes into the application's own issue-tracking space carries. On a deploy to production, `rotate_database_credential` true also sets a new production database password, as a promote does; the line form records it on the code, and the line carries it to the upload's start. Its answer leads with a `summary`; unsettled, it names the `next` call, a `read_status` naming the `step`.\n\nAn answer that is not this platform's own, such as a gateway's error page or a closed connection, says nothing about whether the deploy started. Read `read_status` with `wait_seconds` first: the deploy started only where the environment's `deploy` member names the kind `deploy` with a `started_at` later than your call. Where `pending_upload` still names the upload, its start was not made: make the call its `retry` carries. Call again only where that read shows no start.",
"annotations": {
"readOnlyHint": false,
"destructiveHint": true,
"openWorldHint": true
}
}
MCP catalog entry
{
"name": "deploy",
"tier": "reversible",
"scenario": "CHI-L0-07",
"summary": "A folder deploys by this call and the one line it answers, which zips, uploads, and deploys it; a script passes `artifact` to deploy a stored zip. A new application's deploys go to its one environment, production; once `create_environment` adds development, they go there, and `promote` moves a version to production.\n\nCall `deploy` with the application, naming no environment and neither `artifact` nor `upload`. Name `local_path` where the line will run from another folder. It answers at once with the state `awaiting_command`, `command`, one line, with `command_windows`, its Windows form, and `next`, a `read_status` call. Run that line once, as given, from the application's folder, on Node.js 24 or later. The line carries a one-time code that ends at `expires_at`: treat the line as a credential, run it through your shell tool, and paste it nowhere else. `previous_code` says what became of the application's last code. A job with no tool to make this call runs the command under `TURNZERO_CLOUD_MINTED_TOKEN` instead.\n\nThe line zips the folder, uploads the zip, starts its deploy, and waits for it. Allow it sixteen minutes, or set `TURNZERO_DEPLOY_NO_WAIT` in the shell to have it return after the start. Make the `next` call where the command returned early, or to read the whole record: it holds its answer for its `wait_seconds` until the deploy ends. A plan caps the deploys started in 24 hours: `read_plan_quotas` answers the cap as its `deploys-per-day` row, and a deploy past it is refused `deploys_per_day_exceeded`.\n\nThe command's start names `commit` where the folder has a Git repository; an `artifact` start may name it. An answer that is not this platform's own, such as a gateway's error page or a closed connection, says nothing about whether the deploy started. Read `read_status` first, and call again only where it shows no start. The rest is on the page /cloud/reference/actions/deploy/, which `read_documentation` reads as `page` and the platform's origin serves.",
"owners": [
"PLD-L0-43",
"PLD-L0-62",
"MAN-09",
"EGW-L0-01",
"EGW-L0-11",
"PLD-L0-63",
"PLD-L0-40",
"PLD-L0-41",
"PRC-L0-16",
"OST-L0-01",
"MAPI-04",
"OST-L0-08",
"PLD-L0-86",
"PLD-L0-89",
"PLD-L0-90",
"MAN-14",
"SEC-L0-18",
"ITS-L0-02"
]
}
request
| JSON pointer | Description and constraints |
|---|---|
| "" (root) | Type: object Required fields: ["application"] |
| / |
The application id, from `list_applications`. Type: string |
| / |
Absent, the deploy goes to the application's deploy target: production with one environment, development with two. Naming `development` on one environment is refused `environment_not_created`. Naming `production` on two is refused `environment_unavailable`, because a version reaches production there through `promote`. Type: string Allowed values: ["development","production"] |
| / |
The artifact form: `area`, `name`, `hash`, and, optionally, `environment`. An absent file or a hash mismatch is refused by name, and so is the platform's deploy area, which the upload form alone reads. Name `artifact` or `upload`, never both, which is refused `invalid_request`; a call naming neither, and no `zip_sha256`, is the line form, which answers one line to run. Type: object Required fields: ["area","name","hash"] |
| / |
The storage area the artifact was put in. Type: string |
| / |
The artifact's file name within that area. Type: string |
| / |
The artifact file's SHA-256, as hex. Type: string Pattern: ^[a-f0-9]{64}$ |
| / |
The partition of the area that holds the zip, named by its environment, `development` or `production`; absent, the partition of the environment deployed. Type: string Pattern: ^(development|production)$ |
| / |
The upload's id, which the turnzero-cloud command's `prepared:` line prints and its start names. Name it yourself only to start that upload again, where its start was refused or never made, as the `retry` of `read_status`'s `pending_upload` gives it. An upload with no such file is refused `upload_not_found`. A file whose SHA-256 differs from the one the command's preparing call named is refused `upload_hash_mismatch`, and no retry starts it. Type: string Pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ |
| / |
Taken on the HTTP action route alone, from the turnzero-cloud command's preparing call: the SHA-256 of the zip the command made and will upload, 64 lower-case hexadecimal characters. The MCP tool declares no `zip_sha256`, and a call naming it there is refused `local_route_required`: from a tool, call `deploy` naming none of `zip_sha256`, `artifact`, and `upload`, the line form, and run the line it answers. Of another form, or named beside `artifact` or `upload`, it is refused `invalid_request`. Type: string HTTP action route only, never an MCP tool argument: true |
| / |
On the line form: the application's folder or a `.zip` file on your machine, absolute or relative to the folder the command runs in, which the answered line carries as its `--path`. Absent, the command zips the folder it runs in; the platform never reads the path. A path holding a control character, a double quote, `$`, a backtick, `%`, `!`, `&`, `|`, `<`, `>`, `^`, a typographic double quote, a doubled backslash, or a trailing backslash is refused `invalid_request`. So is `~` or a path opening with `~/` or `~\`, which no shell expands in double quotes. So is one named beside `artifact` or `upload`. Type: string Minimum length: 1 Maximum length: 1024 Pattern: ^(?:[^"$`%!&\|<>^\u201c-\u201e\u0000-\u001f\u007f\\]|\\[^"$`%!&\|<>^\u201c-\u201e\u0000-\u001f\u007f\\])+$ |
| / |
The seconds, 1 to 45, the answer is held until the version-history row this call starts ends, counted from the call's arrival. A value outside that range is refused `invalid_request`, its detail naming 45 as the bound. Type: integer Minimum: 1 Maximum: 45 |
| / |
On a start you make yourself (naming `upload` or `artifact`): the commit the code was built from, 7 to 64 lower-case hexadecimal characters, as `git rev-parse HEAD` prints. On the upload path name none: the turnzero-cloud command reads the folder's commit, where it has a Git repository, and names it on its start. Malformed, or named on the preparing call, it is refused `invalid_request`; absent, the version records none. The line form refuses it the same way. Type: string Pattern: ^[0-9a-f]{7,64}$ |
| / |
The withdraw form, which the turnzero-cloud command alone sends, under the deploy code a line carries, as `true` beside `application` and no other member: it ends the code unused and prepares nothing, and the answer's state is `withdrawn`. Taken on the HTTP action route alone: the MCP tool declares no `withdraw`, and a call naming it there is refused `local_route_required`. Type: boolean HTTP action route only, never an MCP tool argument: true |
| / |
On a deploy to production, `true` also sets a new password on the production database role, as a promote does. The line form records it on the line's deploy code, and the line carries it as `--rotate-database-credential`; the command's preparing call applies what the code recorded, and the upload's grant records it for the start. A preparing call or a start naming another value is refused `invalid_request`, and so is the member on a deploy to development. Type: boolean |
response
| JSON pointer | Description and constraints |
|---|---|
| "" (root) | With `artifact` or `upload`, 202 at once, before the build starts: the environment's version-history row was inserted `deploying`, and the build, the apply, the publish, and the health gate continue after the answer. With `wait_seconds`, the answer is held until the row ends: settled, it is 200 with the state `deployed` or `failed` and the row's `outcome`; unsettled, it is 202 with `next` (MAPI-04; PLD-L0-63). While a deploy of the environment is in flight, a second deploy with `artifact` answers it for the same artifact hash and is refused `deploy_in_flight` for a different one (PLD-L0-63; MAPI-04). A call naming `upload` answers it only where that deploy read the same upload, and any other upload is refused `deploy_in_flight`, identical bytes included (PLD-L0-86). With none of `zip_sha256`, `artifact`, and `upload`, the line form, 200 with the state `awaiting_command` and the line in `command`. On the HTTP action route alone, the command's preparing call names `zip_sha256` and answers 200 with the state `awaiting_upload` and `upload`, and the withdraw form answers 200 with the state `withdrawn`. Type: object Required fields: ["contract_version","application","environment","state"] |
| / |
Required value: 1 |
| / |
The short reference the platform recorded this call under, ten lowercase hexadecimal characters, the value the call’s record row carries; quote it when reporting the call. Type: string Pattern: ^[0-9a-f]{10}$ |
| / |
The answer's first member: one sentence naming the environment's state and serving version and, for the row this call started, its version, kind, step, and seconds since it started, or how it ended (PLD-L0-63). Absent with `awaiting_command`, `awaiting_upload`, and `withdrawn`. Type: string |
| / |
Type: string |
| / |
Type: string |
| / |
The version number the deploy's history row carries: answered with `deploying`, `deployed`, and `failed`, and absent with the other states. Type: integer |
| / |
`deploying` on the 202 answer, which precedes the row's end. The state is `deployed` or `failed` on the 200 answer, where the request's `wait_seconds` saw the row end. The state is `awaiting_command` on the 200 answer to the line form, a call naming none of `zip_sha256`, `artifact`, and `upload`. The state is `awaiting_upload` on the 200 answer to the turnzero-cloud command's preparing call on the HTTP action route, which names `zip_sha256`; it inserts no version row and starts nothing. The state is `withdrawn` on the 200 answer to the withdraw form. Type: string Pattern: ^(deploying|deployed|failed|awaiting_upload|awaiting_command|withdrawn)$ |
| / |
The hostname of the environment the deploy goes to: answered with `deploying`, `deployed`, and `failed`, and absent with the other states. Type: string |
| / |
The recorded manifest's health path, which the health gate that follows this act will probe, cut at 256 characters as the gate's record keeps it. It is answered where this call starts the deploy. It is absent with `awaiting_command`, `awaiting_upload`, and `withdrawn`, since none of those calls reads a manifest, and on an answer that joins a deploy already in flight (PLD-L0-63). Type: string |
| / |
Present where the request carried `wait_seconds`. True where the one read after the wait found the row this call started ended, whatever ended the wait. False means that read found the row still deploying, or could not read it, and not that it failed: `next` names the call that waits on it (MAPI-04; PLD-L0-63). Type: boolean |
| / |
Present where the request carried `wait_seconds`: the milliseconds the answer was held after the row started. Type: integer Minimum: 0 |
| / |
Present where the wait settled: the row's `outcome` as `list_versions` answers it. Its `result` is `succeeded` on a deployed row and `failed`, `interrupted`, or `superseded` on a failed one (PLD-L0-63). Type: object |
| / |
Present with the state `awaiting_upload` alone, the answer to the turnzero-cloud command's preparing call on the HTTP action route: one pending upload of the zip whose SHA-256 the call named, under a short-lived grant for that one zip, answered in `grant`. Its one write puts the zip, and its one start deploys it. After that start, the same grant serves the command's own progress reads of that deploy, until five minutes after it ends and never past fifteen minutes after the start. The same zip sent again under the grant before `expires_at`, once its write landed and while no later call replaced the upload, answers 200 with the file's name, area, size, and SHA-256 and writes nothing; other bytes under the spent grant are refused `transfer_grant_spent`. A later preparing call for the application replaces an upload no deploy has read, and a later line-form call ends an unstarted one the application's last deploy code prepared. Type: object Required fields: ["id","expires_at","max_bytes","grant","command","command_windows"] Additional properties: false |
| / |
The upload's id, which the turnzero-cloud command's `prepared:` line prints and its start names as `upload`. Type: string Pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ |
| / |
When the upload's grant stops serving, as an ISO 8601 instant. A write or a start under the grant after it is refused `transfer_grant_expired`, and a new line-form `deploy` call answers a new line. Type: string |
| / |
The most bytes the zip may carry, the configured bound. Type: integer |
| / |
The upload's grant, its value alone. The turnzero-cloud command from 0.8.0 reads it here and presents it as the bearer of the upload, the start, and the progress read. It is answered on the HTTP action route alone, to the caller that made the preparing call. Type: string |
| / |
The upload's grant, the same value as `grant` and nothing else: no words and no line to run. It is kept for a turnzero-cloud command older than 0.8.0, which reads the grant here; a reader takes `grant` instead. Type: string |
| / |
The same value as `grant` and `command`, and nothing else, kept for a turnzero-cloud command older than 0.8.0; a reader takes `grant` instead. Type: string |
| / |
With the state `awaiting_command`: one line, for macOS and Linux, that pipes a one-time deploy code to the turnzero-cloud command: `echo <code> | npx -y <origin>/packages/turnzero-cloud-<version>.tgz deploy --application <application id>`. The line adds `--origin <origin>` where the answering origin is not the command's default, `--path "<local_path>"` where the call named one, and `--rotate-database-credential` where it named `rotate_database_credential` true. Run it once, as given, from the application's folder, and paste it nowhere: it is a credential until `expires_at`. Type: string |
| / |
With the state `awaiting_command`: the same line for every Windows shell, with `npx.cmd` where its head says `npx`. On Windows, run this one in `command`'s place, once, as given. Type: string |
| / |
With the state `awaiting_command`: when the line's deploy code stops serving, as an ISO 8601 instant, the served lifetime after the call, five minutes where it is unset, and never past the expiry of a minted token that made the call. A line run after it is refused `deploy_code_refused`. Type: string |
| / |
With the state `awaiting_command`: what became of the application's last deploy code before this call. Your `prepared:` line's upload is yours; where `previous_code` names a started upload whose id no `prepared:` line of yours printed, roll back, rotate the application's secrets and its database credential, and report it. Type: object Required fields: ["state"] Additional properties: false |
| / |
`none`: the application had no code. `not_used`: this call ended it unused. `expired`: it expired unused. `replaced`: it was ended unused before this call. `withdrawn`: the command withdrew it. `used`: a preparing call spent it. Type: string Allowed values: ["none","not_used","expired","replaced","withdrawn","used"] |
| / |
With `used`: when the preparing call spent it, as an ISO 8601 instant. Type: string |
| / |
With `used`: the id of the upload that call prepared, which the command's `prepared:` line prints; absent where it prepared none. Type: string Pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ |
| / |
With `used`: whether that upload's deploy started. One that had not started is ended by this call. Type: boolean |
| / |
With `used`: the version the started upload wrote, where one stands. Type: integer |
| / |
The exact `read_status` call that reads the deploy to its end. With the state `awaiting_command` or `awaiting_upload` it is made where the command returned before the outcome, or to read the whole record; with `deploying`, where the wait did not settle. Type: object Required fields: ["action","arguments"] Additional properties: false |
| / |
Required value: read_status |
| / |
Type: object Required fields: ["application","environment","wait_seconds"] Additional properties: false |
| / |
Type: string |
| / |
Type: string Pattern: ^(development|production)$ |
| / |
The read's wait: 45, or on the answer to the line form or a preparing call that call's own `wait_seconds`, 45 where it gave none. Type: integer Minimum: 1 Maximum: 45 |
| / |
With the state `deploying`, names how the deploy's step and end are read: the `next` call, `read_status` with `wait_seconds` 45, or a read every ten seconds (MAPI-04). With `deployed` or `failed`, names how the row ended; with `deployed`, it also says the health check requested the health path alone, and to request the other routes and read `read_logs` for errors. With `awaiting_command`, says the line is a credential until `expires_at`, to run once, as given, and paste nowhere, and what it does. Where `previous_code` names a started upload of the last ten minutes, it says your `prepared:` line's upload is yours and gives that member's remedy. Otherwise, on one environment with no version serving, it says the line deploys to production and to call `create_environment` and follow its answer to try versions on development first. With `awaiting_upload`, names the upload's grant, which deploys to the answer's `environment` only the zip whose SHA-256 the call named, and names no line to run. With `withdrawn`, says the code is ended. Type: string |
| / |
present where the zip's manifest.json differs or does not parse, or a lib/ copy is unread, uncompared, or unpaired (PLD-L0-63). Type: string |
| / |
present where the build will not run an install script or rebuild a binding.gyp of the root or a workspace member, naming each and each member package.json left unread (PLD-L0-60). Type: string |
| / |
present while the application's usage state is over, absent otherwise: a router report's recomputation or the daily check found a measure at or past its plan's served quantity (ACB-L0-26). It names the over measures and what each refuses: requests on its hostnames and scheduled runs for backend actions and data transfer until the next UTC month's first instant. It names file puts on bound storage areas for stored data until the next successful daily pass, a larger plan, or a raised quota. It also names that the deploy proceeded. The deploy itself is never refused for usage. Type: string |
Complete payload contract
{
"request": {
"type": "object",
"required": [
"application"
],
"properties": {
"application": {
"type": "string",
"description": "The application id, from `list_applications`."
},
"environment": {
"type": "string",
"enum": [
"development",
"production"
],
"description": "Absent, the deploy goes to the application's deploy target: production with one environment, development with two. Naming `development` on one environment is refused `environment_not_created`. Naming `production` on two is refused `environment_unavailable`, because a version reaches production there through `promote`."
},
"artifact": {
"type": "object",
"required": [
"area",
"name",
"hash"
],
"properties": {
"area": {
"type": "string",
"description": "The storage area the artifact was put in."
},
"name": {
"type": "string",
"description": "The artifact's file name within that area."
},
"hash": {
"type": "string",
"pattern": "^[a-f0-9]{64}$",
"description": "The artifact file's SHA-256, as hex."
},
"environment": {
"type": "string",
"pattern": "^(development|production)$",
"description": "The partition of the area that holds the zip, named by its environment, `development` or `production`; absent, the partition of the environment deployed."
}
},
"description": "The artifact form: `area`, `name`, `hash`, and, optionally, `environment`. An absent file or a hash mismatch is refused by name, and so is the platform's deploy area, which the upload form alone reads. Name `artifact` or `upload`, never both, which is refused `invalid_request`; a call naming neither, and no `zip_sha256`, is the line form, which answers one line to run."
},
"upload": {
"type": "string",
"pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
"description": "The upload's id, which the turnzero-cloud command's `prepared:` line prints and its start names. Name it yourself only to start that upload again, where its start was refused or never made, as the `retry` of `read_status`'s `pending_upload` gives it. An upload with no such file is refused `upload_not_found`. A file whose SHA-256 differs from the one the command's preparing call named is refused `upload_hash_mismatch`, and no retry starts it."
},
"zip_sha256": {
"type": "string",
"x-wire-only": true,
"description": "Taken on the HTTP action route alone, from the turnzero-cloud command's preparing call: the SHA-256 of the zip the command made and will upload, 64 lower-case hexadecimal characters. The MCP tool declares no `zip_sha256`, and a call naming it there is refused `local_route_required`: from a tool, call `deploy` naming none of `zip_sha256`, `artifact`, and `upload`, the line form, and run the line it answers. Of another form, or named beside `artifact` or `upload`, it is refused `invalid_request`."
},
"local_path": {
"type": "string",
"minLength": 1,
"maxLength": 1024,
"pattern": "^(?:[^\"$`%!&\\|<>^\\u201c-\\u201e\\u0000-\\u001f\\u007f\\\\]|\\\\[^\"$`%!&\\|<>^\\u201c-\\u201e\\u0000-\\u001f\\u007f\\\\])+$",
"description": "On the line form: the application's folder or a `.zip` file on your machine, absolute or relative to the folder the command runs in, which the answered line carries as its `--path`. Absent, the command zips the folder it runs in; the platform never reads the path. A path holding a control character, a double quote, `$`, a backtick, `%`, `!`, `&`, `|`, `<`, `>`, `^`, a typographic double quote, a doubled backslash, or a trailing backslash is refused `invalid_request`. So is `~` or a path opening with `~/` or `~\\`, which no shell expands in double quotes. So is one named beside `artifact` or `upload`."
},
"wait_seconds": {
"type": "integer",
"minimum": 1,
"maximum": 45,
"description": "The seconds, 1 to 45, the answer is held until the version-history row this call starts ends, counted from the call's arrival. A value outside that range is refused `invalid_request`, its detail naming 45 as the bound."
},
"commit": {
"type": "string",
"pattern": "^[0-9a-f]{7,64}$",
"description": "On a start you make yourself (naming `upload` or `artifact`): the commit the code was built from, 7 to 64 lower-case hexadecimal characters, as `git rev-parse HEAD` prints. On the upload path name none: the turnzero-cloud command reads the folder's commit, where it has a Git repository, and names it on its start. Malformed, or named on the preparing call, it is refused `invalid_request`; absent, the version records none. The line form refuses it the same way."
},
"withdraw": {
"type": "boolean",
"x-wire-only": true,
"description": "The withdraw form, which the turnzero-cloud command alone sends, under the deploy code a line carries, as `true` beside `application` and no other member: it ends the code unused and prepares nothing, and the answer's state is `withdrawn`. Taken on the HTTP action route alone: the MCP tool declares no `withdraw`, and a call naming it there is refused `local_route_required`."
},
"rotate_database_credential": {
"type": "boolean",
"description": "On a deploy to production, `true` also sets a new password on the production database role, as a promote does. The line form records it on the line's deploy code, and the line carries it as `--rotate-database-credential`; the command's preparing call applies what the code recorded, and the upload's grant records it for the start. A preparing call or a start naming another value is refused `invalid_request`, and so is the member on a deploy to development."
}
}
},
"response": {
"type": "object",
"required": [
"contract_version",
"application",
"environment",
"state"
],
"properties": {
"contract_version": {
"const": 1
},
"reference": {
"type": "string",
"pattern": "^[0-9a-f]{10}$",
"description": "The short reference the platform recorded this call under, ten lowercase hexadecimal characters, the value the call’s record row carries; quote it when reporting the call."
},
"summary": {
"type": "string",
"description": "The answer's first member: one sentence naming the environment's state and serving version and, for the row this call started, its version, kind, step, and seconds since it started, or how it ended (PLD-L0-63). Absent with `awaiting_command`, `awaiting_upload`, and `withdrawn`."
},
"application": {
"type": "string"
},
"environment": {
"type": "string"
},
"version": {
"type": "integer",
"description": "The version number the deploy's history row carries: answered with `deploying`, `deployed`, and `failed`, and absent with the other states."
},
"state": {
"type": "string",
"pattern": "^(deploying|deployed|failed|awaiting_upload|awaiting_command|withdrawn)$",
"description": "`deploying` on the 202 answer, which precedes the row's end. The state is `deployed` or `failed` on the 200 answer, where the request's `wait_seconds` saw the row end. The state is `awaiting_command` on the 200 answer to the line form, a call naming none of `zip_sha256`, `artifact`, and `upload`. The state is `awaiting_upload` on the 200 answer to the turnzero-cloud command's preparing call on the HTTP action route, which names `zip_sha256`; it inserts no version row and starts nothing. The state is `withdrawn` on the 200 answer to the withdraw form."
},
"hostname": {
"type": "string",
"description": "The hostname of the environment the deploy goes to: answered with `deploying`, `deployed`, and `failed`, and absent with the other states."
},
"health_path": {
"type": "string",
"description": "The recorded manifest's health path, which the health gate that follows this act will probe, cut at 256 characters as the gate's record keeps it. It is answered where this call starts the deploy. It is absent with `awaiting_command`, `awaiting_upload`, and `withdrawn`, since none of those calls reads a manifest, and on an answer that joins a deploy already in flight (PLD-L0-63)."
},
"settled": {
"type": "boolean",
"description": "Present where the request carried `wait_seconds`. True where the one read after the wait found the row this call started ended, whatever ended the wait. False means that read found the row still deploying, or could not read it, and not that it failed: `next` names the call that waits on it (MAPI-04; PLD-L0-63)."
},
"waited_ms": {
"type": "integer",
"minimum": 0,
"description": "Present where the request carried `wait_seconds`: the milliseconds the answer was held after the row started."
},
"outcome": {
"type": "object",
"description": "Present where the wait settled: the row's `outcome` as `list_versions` answers it. Its `result` is `succeeded` on a deployed row and `failed`, `interrupted`, or `superseded` on a failed one (PLD-L0-63)."
},
"upload": {
"type": "object",
"required": [
"id",
"expires_at",
"max_bytes",
"grant",
"command",
"command_windows"
],
"properties": {
"id": {
"type": "string",
"pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
"description": "The upload's id, which the turnzero-cloud command's `prepared:` line prints and its start names as `upload`."
},
"expires_at": {
"type": "string",
"description": "When the upload's grant stops serving, as an ISO 8601 instant. A write or a start under the grant after it is refused `transfer_grant_expired`, and a new line-form `deploy` call answers a new line."
},
"max_bytes": {
"type": "integer",
"description": "The most bytes the zip may carry, the configured bound."
},
"grant": {
"type": "string",
"description": "The upload's grant, its value alone. The turnzero-cloud command from 0.8.0 reads it here and presents it as the bearer of the upload, the start, and the progress read. It is answered on the HTTP action route alone, to the caller that made the preparing call."
},
"command": {
"type": "string",
"description": "The upload's grant, the same value as `grant` and nothing else: no words and no line to run. It is kept for a turnzero-cloud command older than 0.8.0, which reads the grant here; a reader takes `grant` instead."
},
"command_windows": {
"type": "string",
"description": "The same value as `grant` and `command`, and nothing else, kept for a turnzero-cloud command older than 0.8.0; a reader takes `grant` instead."
}
},
"additionalProperties": false,
"description": "Present with the state `awaiting_upload` alone, the answer to the turnzero-cloud command's preparing call on the HTTP action route: one pending upload of the zip whose SHA-256 the call named, under a short-lived grant for that one zip, answered in `grant`. Its one write puts the zip, and its one start deploys it. After that start, the same grant serves the command's own progress reads of that deploy, until five minutes after it ends and never past fifteen minutes after the start. The same zip sent again under the grant before `expires_at`, once its write landed and while no later call replaced the upload, answers 200 with the file's name, area, size, and SHA-256 and writes nothing; other bytes under the spent grant are refused `transfer_grant_spent`. A later preparing call for the application replaces an upload no deploy has read, and a later line-form call ends an unstarted one the application's last deploy code prepared."
},
"command": {
"type": "string",
"description": "With the state `awaiting_command`: one line, for macOS and Linux, that pipes a one-time deploy code to the turnzero-cloud command: `echo <code> | npx -y <origin>/packages/turnzero-cloud-<version>.tgz deploy --application <application id>`. The line adds `--origin <origin>` where the answering origin is not the command's default, `--path \"<local_path>\"` where the call named one, and `--rotate-database-credential` where it named `rotate_database_credential` true. Run it once, as given, from the application's folder, and paste it nowhere: it is a credential until `expires_at`."
},
"command_windows": {
"type": "string",
"description": "With the state `awaiting_command`: the same line for every Windows shell, with `npx.cmd` where its head says `npx`. On Windows, run this one in `command`'s place, once, as given."
},
"expires_at": {
"type": "string",
"description": "With the state `awaiting_command`: when the line's deploy code stops serving, as an ISO 8601 instant, the served lifetime after the call, five minutes where it is unset, and never past the expiry of a minted token that made the call. A line run after it is refused `deploy_code_refused`."
},
"previous_code": {
"type": "object",
"required": [
"state"
],
"properties": {
"state": {
"type": "string",
"enum": [
"none",
"not_used",
"expired",
"replaced",
"withdrawn",
"used"
],
"description": "`none`: the application had no code. `not_used`: this call ended it unused. `expired`: it expired unused. `replaced`: it was ended unused before this call. `withdrawn`: the command withdrew it. `used`: a preparing call spent it."
},
"at": {
"type": "string",
"description": "With `used`: when the preparing call spent it, as an ISO 8601 instant."
},
"upload": {
"type": "string",
"pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
"description": "With `used`: the id of the upload that call prepared, which the command's `prepared:` line prints; absent where it prepared none."
},
"started": {
"type": "boolean",
"description": "With `used`: whether that upload's deploy started. One that had not started is ended by this call."
},
"version": {
"type": "integer",
"description": "With `used`: the version the started upload wrote, where one stands."
}
},
"additionalProperties": false,
"description": "With the state `awaiting_command`: what became of the application's last deploy code before this call. Your `prepared:` line's upload is yours; where `previous_code` names a started upload whose id no `prepared:` line of yours printed, roll back, rotate the application's secrets and its database credential, and report it."
},
"next": {
"type": "object",
"required": [
"action",
"arguments"
],
"properties": {
"action": {
"const": "read_status"
},
"arguments": {
"type": "object",
"required": [
"application",
"environment",
"wait_seconds"
],
"properties": {
"application": {
"type": "string"
},
"environment": {
"type": "string",
"pattern": "^(development|production)$"
},
"wait_seconds": {
"type": "integer",
"minimum": 1,
"maximum": 45,
"description": "The read's wait: 45, or on the answer to the line form or a preparing call that call's own `wait_seconds`, 45 where it gave none."
}
},
"additionalProperties": false
}
},
"additionalProperties": false,
"description": "The exact `read_status` call that reads the deploy to its end. With the state `awaiting_command` or `awaiting_upload` it is made where the command returned before the outcome, or to read the whole record; with `deploying`, where the wait did not settle."
},
"detail": {
"type": "string",
"description": "With the state `deploying`, names how the deploy's step and end are read: the `next` call, `read_status` with `wait_seconds` 45, or a read every ten seconds (MAPI-04). With `deployed` or `failed`, names how the row ended; with `deployed`, it also says the health check requested the health path alone, and to request the other routes and read `read_logs` for errors. With `awaiting_command`, says the line is a credential until `expires_at`, to run once, as given, and paste nowhere, and what it does. Where `previous_code` names a started upload of the last ten minutes, it says your `prepared:` line's upload is yours and gives that member's remedy. Otherwise, on one environment with no version serving, it says the line deploys to production and to call `create_environment` and follow its answer to try versions on development first. With `awaiting_upload`, names the upload's grant, which deploys to the answer's `environment` only the zip whose SHA-256 the call named, and names no line to run. With `withdrawn`, says the code is ended."
},
"manifest_notice": {
"type": "string",
"description": "present where the zip's manifest.json differs or does not parse, or a lib/ copy is unread, uncompared, or unpaired (PLD-L0-63)."
},
"artifact_notice": {
"type": "string",
"description": "present where the build will not run an install script or rebuild a binding.gyp of the root or a workspace member, naming each and each member package.json left unread (PLD-L0-60)."
},
"usage_notice": {
"type": "string",
"description": "present while the application's usage state is over, absent otherwise: a router report's recomputation or the daily check found a measure at or past its plan's served quantity (ACB-L0-26). It names the over measures and what each refuses: requests on its hostnames and scheduled runs for backend actions and data transfer until the next UTC month's first instant. It names file puts on bound storage areas for stored data until the next successful daily pass, a larger plan, or a raised quota. It also names that the deploy proceeded. The deploy itself is never refused for usage."
}
},
"description": "With `artifact` or `upload`, 202 at once, before the build starts: the environment's version-history row was inserted `deploying`, and the build, the apply, the publish, and the health gate continue after the answer. With `wait_seconds`, the answer is held until the row ends: settled, it is 200 with the state `deployed` or `failed` and the row's `outcome`; unsettled, it is 202 with `next` (MAPI-04; PLD-L0-63). While a deploy of the environment is in flight, a second deploy with `artifact` answers it for the same artifact hash and is refused `deploy_in_flight` for a different one (PLD-L0-63; MAPI-04). A call naming `upload` answers it only where that deploy read the same upload, and any other upload is refused `deploy_in_flight`, identical bytes included (PLD-L0-86). With none of `zip_sha256`, `artifact`, and `upload`, the line form, 200 with the state `awaiting_command` and the line in `command`. On the HTTP action route alone, the command's preparing call names `zip_sha256` and answers 200 with the state `awaiting_upload` and `upload`, and the withdraw form answers 200 with the state `withdrawn`."
}
}
Shared contracts
- Refusals: every refusal, by surface, with its cause and its remedy
- schemas/wire_error.schema.json
- schemas/wire_errors.json
- schemas/action_payloads.json (includes shared shapes)
- management_api_contract.md