declare_upstream

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/declare_upstream, with a bearer credential and the action's payload as the JSON body.

Contract description

Declare an external API that the application calls with a stored key, or revise its declaration. Store the key first with `store_secret` and name it here; the application then calls the API through the platform's proxy at `/egress/v0/<upstream>/<path>`, and the proxy adds the key at its own edge. The proxy never gives the key to the application, though an upstream that echoes the key returns it. A key is declared here where you may edit the client to read its base URL and key from settings, and bound in the manifest's `settings` only where its host is fixed in code you must not change.

With `settings`, a provider's SDK reaches the API from a deployed container with no code change. Each deploy, promote, and `restart_application` sets `base_url`'s setting to the proxy's address for this upstream, and `key`'s setting to an egress key: a credential the platform mints for this upstream and container, which the proxy swaps for the stored key. The OpenAI SDKs read `OPENAI_BASE_URL` and `OPENAI_API_KEY`; the Anthropic SDKs read `ANTHROPIC_BASE_URL` and their bearer setting, `ANTHROPIC_AUTH_TOKEN`.

Nothing at the declaration, the manifest's submission, or the deploy checks that the application's client calls through the proxy, since that depends on its code, which the platform does not read. After the first call, `read_logs` with `filter: "undeclared"` and `source: "egress"`, then with `source: "harness"`, shows a call the client made to the provider's host itself. Such a call skipped `/egress/v0/`, and a failed attempt shows too. `read_status`'s `application.egress` lists the undeclared hosts the serving version reached where its `read` is `logs`, failed attempts left out. A host the manifest's `egress` lists shows in neither. The rest is on the page /cloud/reference/actions/declare-upstream/, which `read_documentation` reads as `page` and the platform's origin serves.

More about this action

Declaring the same name again revises the entry, except its application binding, which is never unbound or re-bound (`binding_refused`). An upstream the application's manifest names in `upstreams` is the manifest's, and a change to it here is refused `manifest_owned_field`. A stored name a setting binds, in the manifest's `settings`, in either environment's running copy, or in an act in flight, is refused `upstream_key_is_bound`.

Access and action metadata

{
  "name": "declare_upstream",
  "resource": "upstream",
  "tier": "reversible",
  "summary": "Declare or revise an upstream: base URL, custody credential name, auth form, and the optional `settings`; the tier noted for its credential-binding weight. The route for every external API an application calls on a stored key: the value goes into custody under the name first (`store_secret`), and the gateway applies it at its own edge. The gateway never gives the key to the application, though an upstream that echoes the key returns it. With `settings`, each deploy, promote, and restart sets the named base-URL setting to the gateway's route and the key setting to an egress key the gateway swaps for the stored key, so an unchanged SDK reaches the API. A stored name a setting binds, in the manifest's `settings`, in either environment's running copy, or in an act in flight, is refused `upstream_key_is_bound`. An upstream the bound application's manifest names in `upstreams` is the manifest's, and a change to it is refused `manifest_owned_field`. The declaration is recorded in the project's own requirements, the platform's example applications showing one form that works, reached through the served library-first guide.",
  "annotations": {
    "readOnlyHint": false,
    "destructiveHint": true,
    "openWorldHint": false
  }
}

MCP catalog entry

{
  "name": "declare_upstream",
  "tier": "reversible",
  "scenario": "EGW-L0-09",
  "summary": "Declare an external API that the application calls with a stored key, or revise its declaration. Store the key first with `store_secret` and name it here; the application then calls the API through the platform's proxy at `/egress/v0/<upstream>/<path>`, and the proxy adds the key at its own edge. The proxy never gives the key to the application, though an upstream that echoes the key returns it. A key is declared here where you may edit the client to read its base URL and key from settings, and bound in the manifest's `settings` only where its host is fixed in code you must not change.\n\nWith `settings`, a provider's SDK reaches the API from a deployed container with no code change. Each deploy, promote, and `restart_application` sets `base_url`'s setting to the proxy's address for this upstream, and `key`'s setting to an egress key: a credential the platform mints for this upstream and container, which the proxy swaps for the stored key. The OpenAI SDKs read `OPENAI_BASE_URL` and `OPENAI_API_KEY`; the Anthropic SDKs read `ANTHROPIC_BASE_URL` and their bearer setting, `ANTHROPIC_AUTH_TOKEN`.\n\nNothing at the declaration, the manifest's submission, or the deploy checks that the application's client calls through the proxy, since that depends on its code, which the platform does not read. After the first call, `read_logs` with `filter: \"undeclared\"` and `source: \"egress\"`, then with `source: \"harness\"`, shows a call the client made to the provider's host itself. Such a call skipped `/egress/v0/`, and a failed attempt shows too. `read_status`'s `application.egress` lists the undeclared hosts the serving version reached where its `read` is `logs`, failed attempts left out. A host the manifest's `egress` lists shows in neither. The rest is on the page /cloud/reference/actions/declare-upstream/, which `read_documentation` reads as `page` and the platform's origin serves.",
  "owners": [
    "EGW-L0-03",
    "EGW-L0-01",
    "EGW-L0-07",
    "EGW-L0-08",
    "SEC-L0-18",
    "MAN-14",
    "EGW-L0-17"
  ]
}

request

JSON pointer Description and constraints
"" (root) Type: object
Required fields: ["name","base_url","credential_name","auth_header","application"]
/properties/name A name for this upstream, unique within the account; it is the `<upstream>` segment of the proxy path. Declaring it again updates the entry; its application binding cannot be unbound or rebound. An upstream the application's manifest names in `upstreams` is the manifest's, and a change to it is refused `manifest_owned_field`.

Type: string
Pattern: ^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$
/properties/base_url The upstream's base URL, `https://…`; calls under it are what the key is applied to.

Type: string
Minimum length: 9
Maximum length: 2000
/properties/credential_name The name the key was stored under with `store_secret`.

Type: string
Pattern: ^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$
/properties/auth_header The HTTP header the key is sent in — for example `Authorization` or `x-api-key`.

Type: string
Pattern: ^[a-zA-Z0-9-]{1,64}$
/properties/auth_format How the header value is formed around the key, with `{value}` standing for the key — for example `Bearer {value}`. `{value}` alone where none is given.

Type: string
Minimum length: 7
Maximum length: 200
/properties/token_shape How token counts are read from this upstream's answers: `gemini` or `anthropic`. Omit for an upstream that reports none.

Type: string
Pattern: ^(gemini|anthropic)$
/properties/settings Optional: the settings an unchanged client of this upstream reads in a deployed container, such as a provider's SDK. These are not the manifest's `settings`, which bind a stored value into the container; these name the settings the platform fills so the stored key stays at the gateway. Each deploy, promote, and `restart_application` of the bound application sets `base_url`'s setting to the gateway's address for this upstream, and `key`'s setting to an egress key the platform mints for this upstream and environment. The gateway swaps the egress key for the stored key at its edge, and the egress key works on this upstream alone. The OpenAI SDKs read `OPENAI_BASE_URL` and `OPENAI_API_KEY`; the Anthropic SDKs read `ANTHROPIC_BASE_URL` and their bearer setting, `ANTHROPIC_AUTH_TOKEN`. A revision that omits `settings` keeps the standing ones. `null` removes them, and `settings` without `key` drops the key setting; either ends the upstream's egress keys in both environments, which the answer's `egress_keys_ended` counts.

Type: ["object","null"]
Additional properties: false
Required fields: ["base_url"]
/properties/settings/properties/base_url Required where `settings` is given: the setting that receives the gateway's address for this upstream, the gateway origin the deploy injects, then `/egress/v0/<name>`, then `base_path`. An upper-case letter first, then upper-case letters, digits, or underscores, ending in a letter or a digit. A name the platform sets, or one a manifest binding or another upstream of the application already uses, is refused `invalid_request`.

Type: string
Pattern: ^[A-Z](?:[A-Z0-9_]{0,62}[A-Z0-9])?$
/properties/settings/properties/key The setting that receives the egress key, a credential the platform mints for this upstream and environment at each deploy, promote, and restart. It reaches this upstream's route alone, and the gateway swaps it for the stored key, so the stored key never enters the container. Absent, the container receives no key setting for this upstream. The name rules are `base_url`'s.

Type: string
Pattern: ^[A-Z](?:[A-Z0-9_]{0,62}[A-Z0-9])?$
/properties/settings/properties/base_path The path `base_url`'s value ends with, such as `/v1` for a client that appends `/chat/completions` to its base URL. Absent, the value ends at the upstream's name.

Type: string
Pattern: ^(/[A-Za-z0-9._~%-]+)+$
Maximum length: 200
/properties/application Required: the id of the one application of the acting account the upstream binds to. Every credential that reaches this action is the account’s own, so the member is always required here; refused 400 `application_required` without it. An upstream is neither unbound nor re-bound (`binding_refused`).

Type: string

response

JSON pointer Description and constraints
"" (root) Type: object
Required fields: ["contract_version","upstream","outcome"]
/properties/contract_version Required value: 1
/properties/reference 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}$
/properties/upstream Type: object
/properties/outcome Type: string
Pattern: ^(created|revised)$
/properties/settings Present where the declaration names `settings`: what a deployed container of the bound application reads for this upstream. The settings arrive at each environment's next deploy or promote, and a running container keeps its settings until then.

Type: object
Required fields: ["base_url","path"]
/properties/settings/properties/base_url The setting that receives the gateway's address for this upstream.

Type: string
/properties/settings/properties/path The path that address ends with, `/egress/v0/<name>` followed by `base_path`, after the gateway origin the deploy injects.

Type: string
/properties/settings/properties/key The setting that receives the egress key for this upstream; absent where the declaration names none.

Type: string
/properties/egress_keys_ended Present where the revision gave `settings` as null or without `key`: the count of the upstream's egress keys it ended, in both environments, zero where no deployed copy held one. A copy that held one has no working key until the environment's next deploy, promote, or `restart_application` after a declaration that names `key` again, and the detail names those environments.

Type: integer
Minimum: 0
/properties/detail Type: string
/properties/page $ref: #/shapes/page

Complete payload contract

{
  "request": {
    "type": "object",
    "required": [
      "name",
      "base_url",
      "credential_name",
      "auth_header",
      "application"
    ],
    "properties": {
      "name": {
        "type": "string",
        "pattern": "^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$",
        "description": "A name for this upstream, unique within the account; it is the `<upstream>` segment of the proxy path. Declaring it again updates the entry; its application binding cannot be unbound or rebound. An upstream the application's manifest names in `upstreams` is the manifest's, and a change to it is refused `manifest_owned_field`."
      },
      "base_url": {
        "type": "string",
        "minLength": 9,
        "maxLength": 2000,
        "description": "The upstream's base URL, `https://…`; calls under it are what the key is applied to."
      },
      "credential_name": {
        "type": "string",
        "pattern": "^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$",
        "description": "The name the key was stored under with `store_secret`."
      },
      "auth_header": {
        "type": "string",
        "pattern": "^[a-zA-Z0-9-]{1,64}$",
        "description": "The HTTP header the key is sent in — for example `Authorization` or `x-api-key`."
      },
      "auth_format": {
        "type": "string",
        "minLength": 7,
        "maxLength": 200,
        "description": "How the header value is formed around the key, with `{value}` standing for the key — for example `Bearer {value}`. `{value}` alone where none is given."
      },
      "token_shape": {
        "type": "string",
        "pattern": "^(gemini|anthropic)$",
        "description": "How token counts are read from this upstream's answers: `gemini` or `anthropic`. Omit for an upstream that reports none."
      },
      "settings": {
        "type": [
          "object",
          "null"
        ],
        "additionalProperties": false,
        "required": [
          "base_url"
        ],
        "description": "Optional: the settings an unchanged client of this upstream reads in a deployed container, such as a provider's SDK. These are not the manifest's `settings`, which bind a stored value into the container; these name the settings the platform fills so the stored key stays at the gateway. Each deploy, promote, and `restart_application` of the bound application sets `base_url`'s setting to the gateway's address for this upstream, and `key`'s setting to an egress key the platform mints for this upstream and environment. The gateway swaps the egress key for the stored key at its edge, and the egress key works on this upstream alone. The OpenAI SDKs read `OPENAI_BASE_URL` and `OPENAI_API_KEY`; the Anthropic SDKs read `ANTHROPIC_BASE_URL` and their bearer setting, `ANTHROPIC_AUTH_TOKEN`. A revision that omits `settings` keeps the standing ones. `null` removes them, and `settings` without `key` drops the key setting; either ends the upstream's egress keys in both environments, which the answer's `egress_keys_ended` counts.",
        "properties": {
          "base_url": {
            "type": "string",
            "pattern": "^[A-Z](?:[A-Z0-9_]{0,62}[A-Z0-9])?$",
            "description": "Required where `settings` is given: the setting that receives the gateway's address for this upstream, the gateway origin the deploy injects, then `/egress/v0/<name>`, then `base_path`. An upper-case letter first, then upper-case letters, digits, or underscores, ending in a letter or a digit. A name the platform sets, or one a manifest binding or another upstream of the application already uses, is refused `invalid_request`."
          },
          "key": {
            "type": "string",
            "pattern": "^[A-Z](?:[A-Z0-9_]{0,62}[A-Z0-9])?$",
            "description": "The setting that receives the egress key, a credential the platform mints for this upstream and environment at each deploy, promote, and restart. It reaches this upstream's route alone, and the gateway swaps it for the stored key, so the stored key never enters the container. Absent, the container receives no key setting for this upstream. The name rules are `base_url`'s."
          },
          "base_path": {
            "type": "string",
            "pattern": "^(/[A-Za-z0-9._~%-]+)+$",
            "maxLength": 200,
            "description": "The path `base_url`'s value ends with, such as `/v1` for a client that appends `/chat/completions` to its base URL. Absent, the value ends at the upstream's name."
          }
        }
      },
      "application": {
        "type": "string",
        "description": "Required: the id of the one application of the acting account the upstream binds to. Every credential that reaches this action is the account’s own, so the member is always required here; refused 400 `application_required` without it. An upstream is neither unbound nor re-bound (`binding_refused`)."
      }
    }
  },
  "response": {
    "type": "object",
    "required": [
      "contract_version",
      "upstream",
      "outcome"
    ],
    "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."
      },
      "upstream": {
        "type": "object"
      },
      "outcome": {
        "type": "string",
        "pattern": "^(created|revised)$"
      },
      "settings": {
        "type": "object",
        "description": "Present where the declaration names `settings`: what a deployed container of the bound application reads for this upstream. The settings arrive at each environment's next deploy or promote, and a running container keeps its settings until then.",
        "required": [
          "base_url",
          "path"
        ],
        "properties": {
          "base_url": {
            "type": "string",
            "description": "The setting that receives the gateway's address for this upstream."
          },
          "path": {
            "type": "string",
            "description": "The path that address ends with, `/egress/v0/<name>` followed by `base_path`, after the gateway origin the deploy injects."
          },
          "key": {
            "type": "string",
            "description": "The setting that receives the egress key for this upstream; absent where the declaration names none."
          }
        }
      },
      "egress_keys_ended": {
        "type": "integer",
        "minimum": 0,
        "description": "Present where the revision gave `settings` as null or without `key`: the count of the upstream's egress keys it ended, in both environments, zero where no deployed copy held one. A copy that held one has no working key until the environment's next deploy, promote, or `restart_application` after a declaration that names `key` again, and the detail names those environments."
      },
      "detail": {
        "type": "string"
      },
      "page": {
        "$ref": "#/shapes/page"
      }
    }
  }
}

Shared contracts