Skip to content
offprompt

collect_secret

Gets the values a task needs written where the project reads them, typed by a human or generated. The call opens the page and waits there while the human types, then returns the key names and the file, never the values.

{
  "secrets": [
    { "name": "RESEND_API_KEY", "provider": "resend" },
    { "name": "AUTH_SECRET", "generate": {} }
  ],
  "reason": "Sending the sign-in emails needs a **Resend** key, and sessions need a signing secret.",
  "sink": { "kind": "dotenv", "path": ".env.local" }
}

Input

FieldTypeWhat it is
secretsarray, 1 to 12Every key the task needs, in one request. See the entries
reasonstring, 1 to 2,000 charactersWhy the values are needed, shown on the page as the agent's claim. Markdown: bold, italics, code, lists, quotes. Links show as text
sinkobjectWhere the values are written. See Sinks
sandboxboolean, optionaltrue only when the agent's environment tells it that it runs in a cloud sandbox, a container or a remote VM. See Cloud sandboxes

secrets

Each entry is one key. It takes at most one of generate, provider and format; Asking for values shows what each looks like on the page.

FieldTypeWhat it is
namestringThe variable the value is stored under, and the field's label. SCREAMING_SNAKE_CASE, matching [A-Z][A-Z0-9_]*, up to 128 characters
generateobject, optionalA random value, made on the page. bytes: 16 to 64, default 32. encoding: base64url, base64 or hex, default base64url. A key the file already holds is kept
providerstring, optionalA key the human gets from a provider: a provider, such as stripe, or one of its keys, such as stripe/webhook_secret. Every accepted value is in Providers
formatstring, optionalWhat the value is, when no one provider issues it: one of the formats, such as postgres_url
secretboolean, optionalfalse shows the value in the clear, for one that is not secret
captionstring, optional, 1 to 90 charactersOne short line under the field, such as where to find the value

A key with none of generate, provider and format is plain text, unless its name is one a provider's key conventionally goes by.

Output

FieldTypeWhat it is
tell_userstring, optionalFirst, once written: the line the agent writes to the human straight away, such as Fingerprint ๐Ÿ‹ ๐Ÿฅ ๐ŸŒฎ ๐Ÿง€
request_idstringThe request, for await_secret and cancel_secret
status"awaiting", "written" or "expired"Where the request stands
expires_innumberSeconds the request has left
namesstring[]The keys asked for
keptstring[], optionalGenerated keys the file already held, left as they were
emojistring, optionalThe fingerprint of the values as the file holds them, such as ๐Ÿ‹ ๐Ÿฅ ๐ŸŒฎ ๐Ÿง€
file_keysstring[], optionalEvery key the file holds after the write, names only
sink{ kind, path }The file, relative to the project
notestringWhat to do next, written for the agent
urlstring, optionalA link the human has to open, when offprompt could not open the page itself

The result travels both as structured content and as the same JSON in a text block, so an agent that reads either sees the same thing. A written result's text opens with one more line, for hosts that show the result's text rather than its structure, such as Wrote 1 value to .env ยท fingerprint ๐Ÿ‹ ๐Ÿฅ ๐ŸŒฎ ๐Ÿง€.

What the call does

Checks the request

offprompt finds the project, resolves the sink inside it and resolves each key. The first problem refuses the whole request, with a message saying what to fix. See Refusals.

Writes at once, when nothing needs typing

When every key is generated, no page opens: offprompt makes the values, writes them and returns written.

Opens the page and waits

Otherwise, on the human's machine, it offers the page in the host's own URL dialog, where the host takes one, then opens it in the human's browser if the dialog is refused or there is none. It waits, for up to 300 seconds, until the values are written or the request expires. There is nothing to poll and nothing to do meanwhile.

From a cloud sandbox it always returns at once with status: "awaiting", and with a url unless the host is showing the tunnel's link in its own dialog. On the human's machine it returns at once with a url only when neither the dialog nor the browser worked. The agent shows a url to the human on its own line and calls await_secret.

A value that fails its checks leaves the request open for another try, however many times. One successful write closes it.

What the agent is told

The tool's description tells the agent to:

  • reference the values by name from then on, such as process.env.RESEND_API_KEY;
  • never open, cat, grep or print the file: reading it puts the values into the transcript, and file_keys already says what it holds;
  • write "Fingerprint" and the four emoji to the human as soon as the call returns, before its next tool call, so they can check them against the page while it is still open;
  • show a url, when the result carries one, in its reply on its own line, since nothing happens until the human opens it.

Each result's note repeats what applies to it: a written result's carries the first three, and a result with a url the last.

Refusals

A refused request is an error result that begins offprompt refused that request:, and no page opens. The causes:

CauseExample
No project to write tooffprompt was started outside a project and the host named no workspace
A sink path that leaves the project../.env, or a symlink out of it
A sink path that is the project folder, or a folder.
Several keys for a file sinkA file sink holds exactly one value
A key asked for twiceRESEND_API_KEY twice
More than one way for one keygenerate and provider together
A provider's key the name does not settleprovider: "stripe" for PAYMENTS_KEY, where Stripe issues several
Generated keys alone, for a git-tracked fileOnly the human can allow that write, on the page

The tool's input schema refuses a name that is not SCREAMING_SNAKE_CASE, and a provider or format that is not in the registry, before offprompt sees the call.

Timeouts

A request lives 300 seconds, or 30 minutes when made from a cloud sandbox, where the human may not be watching when the link appears. offprompt's config for Claude Code, Codex and Pi gives the call 330 seconds, so the agent's own tool timeout does not end it while the human is typing. Cursor's sets none, and Cursor applies its own default.

If the human interrupts the waiting call, the page stays live for the rest of the request's time, and typing the values still writes them. await_secret picks the request up again.