Each secret has a key that your functions read as env.<KEY>. A secret can hold an API token or signing key, for example. You can write or replace a value, but you cannot read it back. List operations return names only.
Operations
| Operation | Route | MCP tool | What it does |
|---|---|---|---|
secret.write |
PUT /v1/_secrets (or POST) |
substrio_secret_write |
Sets key to value (both required, not empty), replacing a value already there |
secret.list |
GET /v1/_secrets |
substrio_secret_list |
Returns secrets (each {key, value: "***"}) and count |
secret.delete |
DELETE /v1/_secrets/{key} |
substrio_secret_delete |
Removes one secret and returns {success, deleted, key} |
Each write and delete is recorded in the app's audit log. Both are irreversible because the previous value cannot be read back, and no undo token is provided. An agent's call to either requires approval, as with other changes of authority.
The CLI commands are substrio secrets set KEY, substrio secrets list, and substrio secrets delete KEY. For set, provide the value on stdin, with --from-file, or with --from-env; do not pass it as an argument. The delete command asks for confirmation unless you pass --yes.
Refusals
| Code | Status | When | Field |
|---|---|---|---|
SECRET_NOT_FOUND |
404 | secret.delete of a key the app does not hold |
key |
INVALID_ARGUMENT |
400 | secret.write without a key or a value |
Worked example: an agent sets and removes a secret
Every call is a plain MCP tools/call with the account key on the account's MCP server; app names the app.
[
{"name": "substrio_secret_write", "arguments": {"app": "shop", "key": "API_TOKEN", "value": "..."}},
{"name": "substrio_secret_list", "arguments": {"app": "shop"}},
{"name": "substrio_secret_delete", "arguments": {"app": "shop", "key": "API_TOKEN"}}
]
The delete returns {"success": true, "deleted": true, "key": "API_TOKEN"}. Repeating it returns SECRET_NOT_FOUND with field: "key". Use secret.list to check the name before trying again.
Worked example: a site and a function that reads the secret, shipped with no hash
The function reads the secret from the global env.API_TOKEN. Commit the function and page to the app's history as text without computing a hash or uploading a file. Then promote the version returned by the commit, version 3 in this example:
[
{"name": "substrio_version_commit", "arguments": {"app": "shop", "branch": "main", "message": "with a token",
"units": [
{"path": "functions/status.js", "text": "export default async () => new Response(env.API_TOKEN ? 'ready' : 'no token');\n"},
{"path": "functions/status.json", "text": "{\"routes\":[\"GET /status\"]}"},
{"path": "site/index.html", "text": "<!doctype html><h1>Shop</h1>\n"}]}},
{"name": "substrio_live_set", "arguments": {"app": "shop", "version": 3}}
]
See versioning ("Developing over MCP": tree, read, edit, search, commit, promote) and static sites (large files uploaded with no hash).