write_route #

View this page as Markdown

On this page

Generated automatically from the published contract sources.

A script calls this action over HTTPS at POST https://tzcloud.turnzero.ai/v1/write_route, with a bearer credential and the action's payload as the JSON body. An MCP connection lists this action as the tool tzcloud_write_route.

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.

Contract description #

Send one HTTP POST, PUT, PATCH, or DELETE request to a route of your own deployed application and read its status, headers, and body. Use it for a route that changes data, where your shell cannot make the request. The request reaches your running code and can change its data, production's included. To send GET, call request_route. Pass test_end_user to sign a test end user in for it, and routes, up to five requests, each with its own method, to send several under one sign-in. The platform builds the address from the environment's hostname and takes no host from you. headers lists the headers you cannot set.

More about this action #

The platform builds the address from the environment's own hostname under the platform's serving suffix. The path cannot change the host, and a path that opens with // is refused invalid_request. The platform resolves the hostname and connects only to a public address it checked. A hostname that resolves to an address outside public unicast is refused route_unreachable before any connection.

The call follows a redirect only to the same hostname over https on port 443. It follows at most five redirects, and checks the address again at each one. A redirect to another host, port, or scheme comes back as the result, with its status and location header intact. A 303 redirect after any method but GET, and a 301 or 302 after a POST, is followed with a GET and no body.

The request sends your headers and body, and the HTTP client's defaults where you set none. It sends no header or credential of the platform's. The accept-encoding header is always identity, and a body the application encodes anyway comes back decoded. You may set at most 16 headers. Setting accept-encoding, a hop header, or a header in one of the platform's own header families is refused invalid_request, and the detail gives the header. The one name of those families you may set is x-turnzero-cloud-client, the app's client-version header: it reaches your application as sent, so a route check meets a native client's version gate as the client does.

The body comes back as text where it is UTF-8, and in base64 otherwise; body_encoding says which. The body returned is at most 64 KiB, counted after any decoding. Past that, the connection ends and truncated is true. Pass max_body_bytes to lower that bound, down to 0 for the status and headers alone, so a large page fits your tool's output limit. The call ends within 20 seconds, or it is refused route_unreachable with the reason timeout. One account makes at most 60 calls a minute, request_route's counted with them; past that, the call is refused rate_capped.

Where the platform's router relayed the response, its headers include x-request-id, the router's identifier for the request. Your application also receives it, as the request header x-request-id. Where the status is 400 or more, call read_logs as the answer's detail gives it: field: "request_id", that identifier as value, since, wait_seconds, and no source. One read returns a 5xx's router record and your code's entries that carry it.

A router replica writes at most twenty such records a minute for an environment. A whole router record reaches a read within a few seconds, which the detail's ten-second wait covers. The lines your code writes include the identifier only where your code writes it. To find your own log entry by a field such as request_id, read the app source with that field and value. To find a console line, read the container source with contains.

With test_end_user, the platform signs that test end user in on the environment under sign_in_test_end_user's rules, sends the session cookie with this request and its same-host redirects, and ends the session when the call ends. The platform replaces the session's value wherever it appears unencoded in the answer's url, headers, and body, a set-cookie for the session among them. Each call is one sign-in against the address's code_sends_per_hour, five an hour by default; past it the call is refused signin_code_rate_limited. It also counts against the realm's signin_starts_per_hour for your source address, thirty an hour by default; past it the call is refused signin_rate_limited. A cookie header beside it is refused invalid_request.

Pass routes, a list of one to five requests, each with its own method and path, and optionally headers, body, and expect, to make them in order in one call; with test_end_user, one sign-in serves them all. The answer has results, one per request, and failed, counted as the next paragraph says. A request the network refuses has its own refused, and the next still runs. Each request keeps its 20 seconds, the list ends within 60, each body comes back up to 12 KiB, and each request counts toward the 60 calls a minute.

With routes, failed counts results refused with route_unreachable and those returning a status other than the one the request expects. That is a status differing from expect, or 400 or more when the request gives none. A request may carry expect, an integer status from 100 to 599. Set it to 404 or 401 to check that status without counting a matching result as failed. Its result then carries expected: true when the status matches and false otherwise, including a refused result. invalid_request refuses the whole call.

The call does not stop a request that changes production's data. The release skill's check requests a route that writes only where the account's owner judges the request safe for production's data. An environment that serves no version is refused never_deployed. The platform's record of the call keeps the method and the path without its query, and never the headers or the body.

Pass the application, the method, POST, PUT, PATCH, or DELETE, and the path, such as /orders; without environment, the request goes to production. A call with no method, and a routes entry with none, is refused invalid_request. A method of GET is refused invalid_request too, and the detail points to request_route, so a list of reads and writes is two calls.

The path argument: for example /health or /orders?limit=5, at most 2,048 characters with its query. Percent-encode a space.

Access and action metadata #

{
  "name": "write_route",
  "resource": "environment",
  "tier": "reversible",
  "surface": "tzcloud",
  "product": "cloud",
  "summary": "Send one HTTP `POST`, `PUT`, `PATCH`, or `DELETE` request to a route of your own deployed application and read what it returns: the status, the headers, and the body. The request reaches your running code and can change its data, production's included. To send `GET`, call `request_route`. Pass the application, the method, and the path, opening with one `/`, and a body where the route reads one. `environment` is `production` where you give none, so give `development` to write to the development copy. The platform builds the address from the environment's own hostname and takes no host from you. A redirect is followed only on the same hostname over https, at most five; any other redirect comes back as the result. The body comes back up to 64 KiB, or the lower `max_body_bytes` you pass, `truncated` true past it, and the call ends within 20 seconds. One account makes at most 60 calls a minute, `request_route`'s counted with them. `routes` takes up to five requests in one call, each with its own method, one `test_end_user` sign-in serving them all.",
  "annotations": {
    "readOnlyHint": false,
    "destructiveHint": true,
    "openWorldHint": true
  }
}

MCP catalog entry #

{
  "name": "write_route",
  "title": "Write to a route",
  "tier": "reversible",
  "scenario": "API-L0-26",
  "summary": "Send one HTTP `POST`, `PUT`, `PATCH`, or `DELETE` request to a route of your own deployed application and read its status, headers, and body. Use it for a route that changes data, where your shell cannot make the request. The request reaches your running code and can change its data, production's included. To send `GET`, call `request_route`. Pass `test_end_user` to sign a test end user in for it, and `routes`, up to five requests, each with its own method, to send several under one sign-in. The platform builds the address from the environment's hostname and takes no host from you. `headers` lists the headers you cannot set.",
  "owners": [
    "API-L0-26",
    "MAPI-28",
    "SVC-L0-07",
    "SVC-L0-24",
    "EGW-L0-01",
    "PLD-L0-67",
    "PLD-L0-59",
    "ACS-L0-21"
  ]
}

request #

JSON pointer Description and constraints
"" (root) Type: object
Required fields: ["application"]
/properties/application The application id, from list_applications: an application of your own account.

Type: string
/properties/environment The environment whose route you send to: production where you give none, whatever the deploy target. One the application does not have is refused environment_not_created, and one serving no version never_deployed.

Type: string
Allowed values: ["development","production"]
/properties/method Required unless you give routes. GET is refused: call request_route to read. The request reaches your running code and can change its data.

Type: string
Allowed values: ["POST","PUT","PATCH","DELETE"]
/properties/path The route on the application, opening with one / and holding its query; a path opening with //, or holding a space or a control character, is refused invalid_request.

Type: string
Maximum length: 2048
/properties/headers At most 16 strings, no line breaks. Refused invalid_request: accept-encoding, host, content-length, the other hop headers, and headers under x-forwarded-, x-azure-, x-fd-, x-envoy-, x-k8se-, x-ms-containerapp-, x-turnzero-cloud- (not -client), or x-app-cloud-.

Type: object
maxProperties: 16
/properties/headers/additionalProperties Type: string
/properties/body The request body as text, at most 64 KiB as UTF-8; set its content-type in headers.

Type: string
/properties/follow_redirects true where you give none. false returns a same-host redirect as the result, with its status and location.

Type: boolean
/properties/max_body_bytes The most bytes of each response body returned, counted after any decoding, from 0 to 65,536: it lowers the 64 KiB bound, or each entry's 12 KiB with routes, and never raises it. Give 0 to read the status and headers of a large page alone, so the result fits your tool's output limit.

Type: integer
Minimum: 0
Maximum: 65536
/properties/routes Up to five requests, in order, in place of those five: each its own method, path, and optional headers, body, follow_redirects, and expect. One test_end_user sign-in serves all. Each counts toward 60 calls a minute, each body returns up to 12 KiB, and the list ends within 60 seconds.

Type: array
Minimum items: 1
Maximum items: 5
/properties/routes/items Type: object
Required fields: ["method","path"]
Additional properties: false
/properties/routes/items/properties/method Required. GET is refused: call request_route to read. The request reaches your running code and can change its data.

Type: string
Allowed values: ["POST","PUT","PATCH","DELETE"]
/properties/routes/items/properties/path The route on the application, opening with one / and holding its query; a path opening with //, or holding a space or a control character, is refused invalid_request.

Type: string
Maximum length: 2048
/properties/routes/items/properties/headers At most 16 strings, no line breaks. Refused invalid_request: accept-encoding, host, content-length, the other hop headers, and headers under x-forwarded-, x-azure-, x-fd-, x-envoy-, x-k8se-, x-ms-containerapp-, x-turnzero-cloud- (not -client), or x-app-cloud-.

Type: object
maxProperties: 16
/properties/routes/items/properties/headers/additionalProperties Type: string
/properties/routes/items/properties/body The request body as text, at most 64 KiB as UTF-8; set its content-type in headers.

Type: string
/properties/routes/items/properties/follow_redirects true where you give none. false returns a same-host redirect as the result, with its status and location.

Type: boolean
/properties/routes/items/properties/expect The status you expect this request to return, from 100 to 599, such as 404 for a route you mean to check is absent or 401 for one that needs a sign-in. With it, failed counts the request only where its status differs, and its result carries expected.

Type: integer
Minimum: 100
Maximum: 599
/properties/test_end_user A test end user's id or label, signed in for this request and its same-host redirects under sign_in_test_end_user's rules; give environment too, and no cookie header.

Type: string

response #

JSON pointer Description and constraints
"" (root) Type: object
Required fields: ["contract_version","application","environment"]
/anyOf/0 Required fields: ["method","url","status","headers","body","body_encoding","body_bytes","truncated","redirects","elapsed_ms"]
/anyOf/1 Required fields: ["results","failed"]
/properties/contract_version Required value: 1
/properties/reference The short reference the platform recorded your call under: ten lowercase hexadecimal characters, the value in the call’s record row. Quote it when you report the call.

Type: string
Pattern: ^[0-9a-f]{10}$
/properties/application The application id.

Type: string
/properties/environment The environment requested: production where the call gave none.

Type: string
Allowed values: ["development","production"]
/properties/method The call's own method, kept where a redirect was followed with GET.

Type: string
Allowed values: ["GET","POST","PUT","PATCH","DELETE"]
/properties/url The last address requested: the environment's hostname and the path, or the same-host address the last redirect followed gave.

Type: string
/properties/status The HTTP status of the last response, a redirect the call did not follow included.

Type: integer
/properties/headers The last response's headers, each name in lower case with one string value, repeated set-cookie values joined by a line break. With test_end_user, the session's value is replaced wherever it appears.

Type: object
/properties/headers/additionalProperties Type: string
/properties/body The response body, up to 64 KiB or max_body_bytes, counted after any decoding: the text where the bytes are UTF-8, else base64.

Type: string
/properties/body_encoding utf-8 where body is the text itself, base64 where the bytes are not UTF-8.

Type: string
Allowed values: ["utf-8","base64"]
/properties/body_bytes The size of body in bytes: of its UTF-8 text, or of the bytes the base64 carries.

Type: integer
Minimum: 0
/properties/truncated True where the body passed its bound, 64 KiB or max_body_bytes, counted after any decoding, so the connection was ended and body holds the bytes up to that bound, a cut last character left out of UTF-8 text.

Type: boolean
/properties/redirects How many same-host redirects the call followed, at most five.

Type: integer
Minimum: 0
Maximum: 5
/properties/elapsed_ms The call's time from its first address check to the end of the body's read, in milliseconds.

Type: integer
Minimum: 0
/properties/detail Present where status is 400 or more and headers carry x-request-id: the reads that find that request's records, the value filled in. The first is one held read_logs by the field request_id, the request's start as since, wait_seconds 10, and no source; then container by contains.

Type: string
/properties/results With routes, in place of the members from method to detail: one result per request, in order. Each holds those members, its body cut at 12 KiB, or the lower max_body_bytes, with truncated true past it, or, for a request refused route_unreachable, method, url, and refused with the refusal's error, reason, and detail. A result of a request that gave expect also carries expected.

Type: array
/properties/results/items Type: object
/properties/results/items/properties/expected Present where the request gave expect: true where its status equals it, false where it differs or the request was refused route_unreachable.

Type: boolean
/properties/failed With routes: how many results were refused, returned a status other than their request's expect, or, for a request with no expect, returned a status of 400 or more.

Type: integer
Minimum: 0

Complete payload contract #

{
  "request": {
    "type": "object",
    "required": [
      "application"
    ],
    "properties": {
      "application": {
        "type": "string",
        "description": "The application id, from `list_applications`: an application of your own account."
      },
      "environment": {
        "type": "string",
        "enum": [
          "development",
          "production"
        ],
        "description": "The environment whose route you send to: `production` where you give none, whatever the deploy target. One the application does not have is refused `environment_not_created`, and one serving no version `never_deployed`."
      },
      "method": {
        "type": "string",
        "enum": [
          "POST",
          "PUT",
          "PATCH",
          "DELETE"
        ],
        "description": "Required unless you give `routes`. `GET` is refused: call `request_route` to read. The request reaches your running code and can change its data."
      },
      "path": {
        "type": "string",
        "maxLength": 2048,
        "description": "The route on the application, opening with one `/` and holding its query; a path opening with `//`, or holding a space or a control character, is refused `invalid_request`."
      },
      "headers": {
        "type": "object",
        "additionalProperties": {
          "type": "string"
        },
        "maxProperties": 16,
        "description": "At most 16 strings, no line breaks. Refused `invalid_request`: `accept-encoding`, `host`, `content-length`, the other hop headers, and headers under `x-forwarded-`, `x-azure-`, `x-fd-`, `x-envoy-`, `x-k8se-`, `x-ms-containerapp-`, `x-turnzero-cloud-` (not `-client`), or `x-app-cloud-`."
      },
      "body": {
        "type": "string",
        "description": "The request body as text, at most 64 KiB as UTF-8; set its `content-type` in `headers`."
      },
      "follow_redirects": {
        "type": "boolean",
        "description": "`true` where you give none. `false` returns a same-host redirect as the result, with its status and `location`."
      },
      "max_body_bytes": {
        "type": "integer",
        "minimum": 0,
        "maximum": 65536,
        "description": "The most bytes of each response body returned, counted after any decoding, from 0 to 65,536: it lowers the 64 KiB bound, or each entry's 12 KiB with `routes`, and never raises it. Give 0 to read the status and headers of a large page alone, so the result fits your tool's output limit."
      },
      "routes": {
        "type": "array",
        "minItems": 1,
        "maxItems": 5,
        "items": {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "enum": [
                "POST",
                "PUT",
                "PATCH",
                "DELETE"
              ],
              "description": "Required. `GET` is refused: call `request_route` to read. The request reaches your running code and can change its data."
            },
            "path": {
              "type": "string",
              "maxLength": 2048,
              "description": "The route on the application, opening with one `/` and holding its query; a path opening with `//`, or holding a space or a control character, is refused `invalid_request`."
            },
            "headers": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              },
              "maxProperties": 16,
              "description": "At most 16 strings, no line breaks. Refused `invalid_request`: `accept-encoding`, `host`, `content-length`, the other hop headers, and headers under `x-forwarded-`, `x-azure-`, `x-fd-`, `x-envoy-`, `x-k8se-`, `x-ms-containerapp-`, `x-turnzero-cloud-` (not `-client`), or `x-app-cloud-`."
            },
            "body": {
              "type": "string",
              "description": "The request body as text, at most 64 KiB as UTF-8; set its `content-type` in `headers`."
            },
            "follow_redirects": {
              "type": "boolean",
              "description": "`true` where you give none. `false` returns a same-host redirect as the result, with its status and `location`."
            },
            "expect": {
              "type": "integer",
              "minimum": 100,
              "maximum": 599,
              "description": "The status you expect this request to return, from 100 to 599, such as `404` for a route you mean to check is absent or `401` for one that needs a sign-in. With it, `failed` counts the request only where its status differs, and its result carries `expected`."
            }
          },
          "required": [
            "method",
            "path"
          ],
          "additionalProperties": false
        },
        "description": "Up to five requests, in order, in place of those five: each its own `method`, `path`, and optional `headers`, `body`, `follow_redirects`, and `expect`. One `test_end_user` sign-in serves all. Each counts toward 60 calls a minute, each body returns up to 12 KiB, and the list ends within 60 seconds."
      },
      "test_end_user": {
        "type": "string",
        "description": "A test end user's id or label, signed in for this request and its same-host redirects under `sign_in_test_end_user`'s rules; give `environment` too, and no `cookie` header."
      }
    }
  },
  "response": {
    "type": "object",
    "required": [
      "contract_version",
      "application",
      "environment"
    ],
    "anyOf": [
      {
        "required": [
          "method",
          "url",
          "status",
          "headers",
          "body",
          "body_encoding",
          "body_bytes",
          "truncated",
          "redirects",
          "elapsed_ms"
        ]
      },
      {
        "required": [
          "results",
          "failed"
        ]
      }
    ],
    "properties": {
      "contract_version": {
        "const": 1
      },
      "reference": {
        "type": "string",
        "pattern": "^[0-9a-f]{10}$",
        "description": "The short reference the platform recorded your call under: ten lowercase hexadecimal characters, the value in the call’s record row. Quote it when you report the call."
      },
      "application": {
        "type": "string",
        "description": "The application id."
      },
      "environment": {
        "type": "string",
        "enum": [
          "development",
          "production"
        ],
        "description": "The environment requested: `production` where the call gave none."
      },
      "method": {
        "type": "string",
        "enum": [
          "GET",
          "POST",
          "PUT",
          "PATCH",
          "DELETE"
        ],
        "description": "The call's own method, kept where a redirect was followed with `GET`."
      },
      "url": {
        "type": "string",
        "description": "The last address requested: the environment's hostname and the path, or the same-host address the last redirect followed gave."
      },
      "status": {
        "type": "integer",
        "description": "The HTTP status of the last response, a redirect the call did not follow included."
      },
      "headers": {
        "type": "object",
        "additionalProperties": {
          "type": "string"
        },
        "description": "The last response's headers, each name in lower case with one string value, repeated `set-cookie` values joined by a line break. With `test_end_user`, the session's value is replaced wherever it appears."
      },
      "body": {
        "type": "string",
        "description": "The response body, up to 64 KiB or `max_body_bytes`, counted after any decoding: the text where the bytes are UTF-8, else base64."
      },
      "body_encoding": {
        "type": "string",
        "enum": [
          "utf-8",
          "base64"
        ],
        "description": "`utf-8` where `body` is the text itself, `base64` where the bytes are not UTF-8."
      },
      "body_bytes": {
        "type": "integer",
        "minimum": 0,
        "description": "The size of `body` in bytes: of its UTF-8 text, or of the bytes the base64 carries."
      },
      "truncated": {
        "type": "boolean",
        "description": "True where the body passed its bound, 64 KiB or `max_body_bytes`, counted after any decoding, so the connection was ended and `body` holds the bytes up to that bound, a cut last character left out of UTF-8 text."
      },
      "redirects": {
        "type": "integer",
        "minimum": 0,
        "maximum": 5,
        "description": "How many same-host redirects the call followed, at most five."
      },
      "elapsed_ms": {
        "type": "integer",
        "minimum": 0,
        "description": "The call's time from its first address check to the end of the body's read, in milliseconds."
      },
      "detail": {
        "type": "string",
        "description": "Present where `status` is 400 or more and `headers` carry `x-request-id`: the reads that find that request's records, the value filled in. The first is one held `read_logs` by the field `request_id`, the request's start as `since`, `wait_seconds` 10, and no source; then `container` by `contains`."
      },
      "results": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "expected": {
              "type": "boolean",
              "description": "Present where the request gave `expect`: true where its status equals it, false where it differs or the request was refused `route_unreachable`."
            }
          }
        },
        "description": "With `routes`, in place of the members from `method` to `detail`: one result per request, in order. Each holds those members, its `body` cut at 12 KiB, or the lower `max_body_bytes`, with `truncated` true past it, or, for a request refused `route_unreachable`, `method`, `url`, and `refused` with the refusal's `error`, `reason`, and `detail`. A result of a request that gave `expect` also carries `expected`."
      },
      "failed": {
        "type": "integer",
        "minimum": 0,
        "description": "With `routes`: how many results were refused, returned a status other than their request's `expect`, or, for a request with no `expect`, returned a status of 400 or more."
      }
    }
  }
}

Shared contracts #