isError: true. content[0].text reads <code>: <detail> <recovery>. structuredContent.error carries
the problem body the REST API returns for the same failure. It also carries a recovery sentence and any facts that
save a round trip. For example, godmode_deals_summary at a point in New York:
recovery sentence itself, word for word. So what a client shows
a reader is what the code carries:
Two details about
errors[]. The tool encodes the arguments to the /v1 querystring before the route’s validator
runs. So a geometry failure names the wire field, not the tool’s input field. A lone latitude comes back as
geometry_conflict with param: "lng" and the detail “lat and lng must be given together”.
The tool’s schema does not refuse an argument it does not define. The tool encodes it, and the route refuses it as
unknown_parameter, which is how a bracketed list reads. godmode_coverage is the exception: it builds no wire query
and validates none. So it drops an argument it does not define without a word. Three real bodies, trimmed to the
parts that differ:
resolved: false and candidates[].
Rate limits and credits. The gateway enforces the plan’s requests a minute and a day. So a burst answers 429
with the problem body (code: "rate_limited", request_id) and a Retry-After header, and the gateway never reads
the JSON-RPC message. A key whose plan ended answers 403 subscription_required, and a key whose card failed more than
seven days ago answers 403 payment_overdue, the same way.
A tools/call costs what its route costs (Plans and limits). On a client that negotiates
protocol 2026-07-28, that cost is the ledger’s figure. The figure is one credit per deal, investor or parcel new to the
account, plus the tool’s base, and nothing for a record already seen. Every result reports it in
meta.credits.charged, beside used, line and period_end. Those three are the period’s count after the call, the
figure it stops at, and when it resets, so a client can predict the stop. See Keys and headers.
A spent allowance, or on Growth and Scale the ceiling, then arrives as a quota_exceeded tool result. The tool sends
no page and charges nothing. Every charged tool answers so until the period resets or the plan changes, a re-read
included. The handshake and the uncharged tools keep answering.
The API writes a legacy client’s single-frame stream before the tool runs. So it charges such a client per call,
refused or served, at the largest possible cost of its page. That cost is the tool’s base plus the asked page, clamped
to 50 and the plan’s largest page.
On a legacy client, the API refuses a call whose weight passes the line as a real 403 quota_exceeded. The refusal
comes before the tool runs and before the API counts anything. It reads the envelope only for the tool’s name and the
asked page, which set the weight. A call the
tool then refuses, for example with a plan_limit or a validation error, still costs that weight. So on a legacy
client, ask for the plan’s page or less. initialize, tools/list, resources/read and prompts/get cost nothing.
The SDK’s own refusals never reach a tool and carry no problem body. A schema failure is an isError result with
one line of text and no structuredContent, for example
Input validation error: Invalid arguments for tool godmode_deal_get: data/deal_id must match pattern "^deal_[0-9a-f]{32}$".
A typed id field carries its pattern in the schema, so the SDK catches a wrong prefix here, not the plan. That is why
invalid_id is not in the table above. An unknown tool name is a JSON-RPC error instead,
{"error": {"code": -32602, "message": "Tool godmode_nope not found"}}. Three more refusals occur at the transport: a
batched array is 400, any Origin header is 403, and GET and DELETE are 405.
Partners and staff
On the internal host, rate limiting has two parts. Staff and contracted partners reach that host on the company network with agm_ key. The API enforces the key budget (rpm, 120 by default) and the 240 per minute per
(key, X-On-Behalf-Of) bucket before the SDK runs. So they answer 429 with the problem body, a Retry-After header
and the x-ratelimit-* trio, and the API never reads the JSON-RPC message. The API charges the contact bucket, 30 per
minute per (key, X-On-Behalf-Of), inside the tool wrapper each time the contact gate is open. So it arrives as an
isError result with code: "rate_limited", retry_after and bucket on the body.
The API spends the unit even when the record holds nothing. So a loop that opens one profile after another stops after
30 in a minute for that person. The header refusals of a contact-scope key, on_behalf_of_required and the
prefixed-form validation_error, are on Keys and headers.