ThunderPhone 2.0 is live.Self-serve, from 2¢/min.Read the announcement

Agents

Prompt Revisions

Every change to an agent

A prompt revision is one version of an agent's prompt text. ThunderPhone records one every time the text changes, from every place that can change it: the dashboard builder, the copilot, the REST API, the MCP server, a version restore, an investigation fix, a deploy, a discarded draft, a duplicate or an import. Version history records deployed configurations only; prompt revisions also cover every draft edit in between, so a test run, a test conversation or a call can name the exact text it ran.

Revisions form lines. The agent's main line is its draft and deployed prompt. Each A/B variant with its own prompt has its own line, and a prompt that a replay run tested without it ever being the draft is recorded as a candidate, off the main line. A text that comes back later (a rule removed, then added back) is a new revision linked to the one it replaced.

The agent iteration toolkit

These endpoints are part of the agent iteration toolkit: prompt revisions, test conversations, replay runs, prompt review and refactor, and talk links. The toolkit is enabled per organization while it is in beta. Without it, every toolkit endpoint answers 403 with code: "feature_not_enabled" and a next_step; ask support to enable it.

Three rules run through the toolkit:

  • The test split is hidden from API keys. Each test conversation belongs to a dev split, for tuning, or a test split, for measuring a change once. API keys and OAuth tokens (and so MCP clients) see a test-split item as its id, split, source, status and counts only. People signed in to the dashboard see everything. Prompt revisions are the agent's own configuration, not call content, so this rule does not apply on this page. In an organization with the toolkit, a call whose caller is in an agent's test split is held out: the calls API and the MCP call tools return it to API keys and OAuth tokens as its id, start time and held_out: true only (see test conversations). Leave those calls out when tuning.
  • dry_run quotes before anything is spent. Every endpoint that spends model or replay credits takes dry_run: true and returns an estimate without running anything. Conversation jobs (generate, import, refresh) and replay runs also return a quote_id, and a real run must send it back: it is good for 15 minutes, for the same arguments only, and for one run. It is used up once, durably, as the run starts; a start that is refused or fails before then leaves it usable, and a run that fails after it is repeated with a new dry run for a new quote_id.
  • max_cost_cents is a hard cap. A real run needs a spend cap in US cents. Before each request the run sets aside the most that request can cost and stops instead of sending one that does not fit, keeping what it already did. So a conversation job or replay run can stop with part of its cap unspent: leave about twice the estimate's largest_reservation_cents of headroom above the expected cost. A run is charged what its requests really cost, not what it set aside. Prompt review and refactor need a cap for any call not answered from the cache, one that covers the dry run's worst_case_cost_cents.

With OAuth, the toolkit's endpoints need these scopes: prompt review and refactor agents:write (their dry runs too) and prompt revisions agents:read; test conversations, conversation jobs and replay runs testing:read to read and testing:write to start; talk links calls:write. Without the scope the response is 403 with error: "insufficient_scope".

Reading prompt revisions spends nothing, so these endpoints take neither dry_run nor max_cost_cents.

Endpoints

MethodPathDescription
GET/v1/agents/{agent_id}/prompt-revisionsList an agent's prompt revisions, newest first
GET/v1/agents/{agent_id}/prompt-revisions/{revision_id}Retrieve one revision with its full text

Prompt revision object

{
  "id": 4182,
  "agent_id": 123,
  "sha256": "9c1f0b6e2a…",
  "source": "draft",
  "actor": "mcp",
  "author_name": null,
  "author_staff": false,
  "parent_id": 4177,
  "variant_id": null,
  "config_version_revision": null,
  "created_at": "2026-10-19T17:02:11.204Z",
  "text_length": 2214,
  "rule_inventory": [],
  "diff": "--- revision 4177\n+++ revision 4182\n@@ -3 +3 @@\n-Ask for their name and their phone number.\n+Ask for their name. Once they answer, ask for their phone number.\n"
}
FieldTypeDescription
idintegerRevision id
sha256stringSHA-256 of the text. Review a prompt returns the same value as prompt_sha256, and the MCP update_agent tool takes it as expected_prompt_sha256
sourcestringdraft (a draft edit), deploy, variant (an A/B variant's prompt), import, backfill (recorded from older version history), or candidate (a prompt a replay run tested)
actorstringWhere the write came from: dashboard, zeus, api (API keys and OAuth), mcp, copilot, replay, or empty for writers with no surface
author_namestring | nullThe person who made the change, when a person did
author_staffbooleantrue when a ThunderPhone staff member made the change
parent_idinteger | nullThe revision this one replaced in the same line
variant_idinteger | nullThe variant whose line this is, for source: "variant"
config_version_revisioninteger | nullThe deployed version that carried this text, when one did
text_lengthintegerCharacters in the text
rule_inventoryobject[]The behaviors the prompt asks for, with stable ids, once a review has read this text; empty before that
diffstringUnified diff against the parent revision (against an empty text for the first)
textstringThe full text. Only on the single-revision endpoint

Agents created before prompt revisions existed get their history recorded from their deployed versions (source: "backfill") the first time their prompt changes.

List prompt revisions

cURL
curl 'https://api.thunderphone.com/v1/agents/123/prompt-revisions?source=draft&limit=20' \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"
Python
page = requests.get(
    "https://api.thunderphone.com/v1/agents/123/prompt-revisions",
    headers={"Authorization": "Bearer sk_live_YOUR_API_KEY"},
    params={"limit": 20},
).json()
for revision in page["results"]:
    print(revision["id"], revision["source"], revision["actor"])

Query parameters

ParamTypeDescription
sourcestringOnly revisions from this source
limitinteger1 to 100, default 20. Values outside the range are clamped
offsetintegerDefault 0

Returns a page, newest first. The revisions in it have no text; fetch one to read it.

{
  "count": 37,
  "limit": 20,
  "offset": 0,
  "next_offset": 20,
  "results": [{ "id": 4182, "source": "draft", "diff": "…" }]
}

next_offset is null on the last page.

Retrieve a prompt revision

cURL
curl https://api.thunderphone.com/v1/agents/123/prompt-revisions/4182 \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"

Returns the revision object with text.

Errors

StatusCondition
400An unknown source, or a limit or offset that is not an integer
403feature_not_enabled: the organization does not have the agent iteration toolkit
404The agent, or the revision, is not in this organization

MCP

The MCP server exposes the list as list_prompt_revisions.