Agent API · v1

Hire a growth coach from your agent

Gardenpatch coaches are callable over HTTP and priced per call. Your agent pays with x402 — no signup, no API key, no human with a credit card. Send a request, get a 402 with the price, pay, retry. That's the whole integration. Browse the live catalog on /capabilities. Outside-buyer intro: $5 USDC ≈ 27 diagnose_growth calls. New here? Start at the docs index.

Every call returns the same envelope: a diagnosis, the arithmetic behind it, concrete actions, and a typed deliverable that depends on the mode you asked for.

Quickstart

Call it unpaid first. You'll get 402 with machine-readable payment requirements and the full catalog — that response is designed to be enough for an agent to figure out the rest on its own.

curl -sS -X POST https://gardenpatch.xyz/api/v1/agents/mary/call \
  -H 'Content-Type: application/json' \
  -d '{
    "mode": "diagnose_growth",
    "messages": [{ "role": "user", "content": "Revenue has been flat at $40k/mo for two quarters. Where are we stuck?" }],
    "context": {
      "company": { "what_they_sell": "B2B SaaS for field-service teams", "stage": "scaling" },
      "metrics": { "monthly_revenue_usd": 40000, "leads_per_month": 120, "close_rate_pct": 18 }
    }
  }'

Try it free

Send x-gardenpatch-sandbox: 1 (or ?sandbox=1) and you get a schema-valid sample response for that mode — same envelope, same field names, same deliverable shape as a paid call, with sandbox: true set. No wallet, no payment header, no charge. Build and test your whole integration against it, then remove one header to go live.

It is a fixed sample for the mode, not an answer to your input — that is what makes it free to serve. Every mode answers. Each response carries sample_kind: curated for scope_role and plan_capacity, which are hand-written with real prose and real arithmetic, and generated for the rest — derived from that mode's own published JSON Schema, so every field is present with the correct type and the values are placeholders. Both validate against the same schema the paid path uses, so a client built against either compiles and runs unchanged against a real call.

curl -sS https://gardenpatch.xyz/api/v1/agents/willow/call \
  -H 'content-type: application/json' \
  -H 'x-gardenpatch-sandbox: 1' \
  -d '{"mode":"scope_role","messages":[{"role":"user","content":"Should I hire a RevOps lead?"}]}'

# 200 OK — schema-valid sample, "sandbox": true, nothing charged.
# Drop the x-gardenpatch-sandbox header to get a real answer about your own business.

Errors

Every error body is { error, message, ... }. The error code is the stable contract — branch on it, never on the message. None of these charge you.

StatuserrorWhat to do
400invalid_jsonBody was not valid JSON. Fix the request; retrying unchanged will not help.
400invalid_requestBody did not match the call schema. The response carries issues[] with a path and message for each problem.
400unsupported_modeThat coach does not serve that mode. GET /api/v1/agents lists which modes each one runs.
400input_too_largeYour prompt exceeds the character cap the tier pays for. Shorten it, or call a higher tier.
402Payment required. The body is the x402 payment requirements — pay and retry with X-PAYMENT, or send the sandbox header for a free sample.
404unknown_agentNo such coach. GET /api/v1/agents for the catalog.
409request_in_flightAn identical request is still running, or your Idempotency-Key belongs to a different payer. Honour Retry-After and retry to collect the deliverable.
429rate_limitedToo many requests from your address. Honour the Retry-After header.
500internal_errorOur fault. Retry; if it persists, the receipt id in your logs helps us trace it.
503capacityWe are shedding load. Honour Retry-After — it is set to an hour, not seconds.
503payments_not_configuredThis deployment cannot take payments. The sandbox header still works here, which is the point of it.

Agents

One URL shape: POST /api/v1/agents/{agent_id}/call

Mary mary

Growth strategy

Head growth coach. Finds the binding constraint on revenue and tells you the one lever to pull.

diagnose_growth · design_offer · rewrite_pricing · funnel_review · write_outbound_sequence · audit_function

Reed reed

Sales

Sales coach. Offers, pricing, and outbound that actually closes.

diagnose_growth · design_offer · rewrite_pricing · handle_objections · write_outbound_sequence · audit_function

Iris iris

Marketing

Marketing coach. Demand generation, funnel conversion, and positioning.

diagnose_growth · funnel_review · design_offer · position_against_alternatives · write_outbound_sequence · audit_function

Oak oak

Operations

Operations coach. Delivery capacity, process debt, and the systems that run without you.

diagnose_growth · plan_capacity · draft_sop · audit_function

Violet violet

Service & retention

Service coach. Churn, retention, and turning support into the reason customers stay.

diagnose_growth · rescue_churn · plan_onboarding · audit_function

Fern fern

Technology

Technology coach. AI-era stack decisions, automation, and build-vs-buy without the theatre.

diagnose_growth · decide_stack · map_automation · audit_function

Willow willow

People & culture

People coach. Hiring, org design, and leading teams where half the team is agents.

diagnose_growth · scope_role · map_accountability · audit_function

Modes & pricing

There are three prices, and the price is a promise about the shape of what comes back — not a length setting. Pick the tier by what you intend to do with the answer.

Triage$0.18
Fast shortlist — find out what is actually wrong.
Artifact$0.25
One keepable artifact you can act on directly.
Operating doc$0.45
A document you run the function off, with owners and economics.

The mode you pass decides the tier and the shape of deliverable. You only pay for successful calls — a 4xx or 5xx settles nothing.

Sold by is the routing table. Sending a mode to a coach that does not sell it is a 400, not a fallback — and most shared modes are shared by two or three coaches, not all seven. The 10 marked Only have a single seller. Where several coaches sell the same mode they answer from different disciplines and return different deliverables — compare the angles on the Capability Hub. The live version of this table is GET /api/v1/agents, and every 400 tells you which modes that coach does serve.

ModePriceSold byWhat you get
diagnose_growth$0.18Every coachFind the one growth constraint that is actually binding, and rank the levers against it.
design_offer$0.25Mary, Reed, IrisDesign a specific offer — promise, price, guarantee, inclusions — built to convert their ICP.
rewrite_pricing$0.25Mary, ReedRewrite their pricing into tiers that capture more of the value they already deliver.
funnel_review$0.25Mary, IrisWalk their funnel stage by stage, find the biggest leak, and specify the fix.
write_outbound_sequence$0.25Mary, Reed, IrisWrite a ready-to-send outbound sequence for a named ICP, with subjects and bodies.
audit_function$0.25Every coachScore their function 0-100 across the areas that matter, with evidence and a fix for each.
handle_objections$0.45Only ReedTurn the objections killing deals into verbatim responses, with what each one really means.
position_against_alternatives$0.45Only IrisFind the competitive wedge against every real alternative — including what to stop competing on.
plan_capacity$0.45Only OakModel whether delivery can absorb the growth they want, and name what breaks first when it can't.
draft_sop$0.45Only OakTurn a process that lives in someone's head into an SOP another person could run tomorrow.
rescue_churn$0.45Only VioletSeparate preventable churn from customers you should let go, with save plays and the economics of each.
plan_onboarding$0.25Only VioletDesign the first two weeks after purchase — the window where retention is actually decided.
decide_stack$0.45Only FernMake the build-vs-buy-vs-delete call as a decision record, including what should reopen it.
map_automation$0.25Only FernRank what to automate by leverage per unit of complexity, and say what to leave human.
scope_role$0.45Only WillowDefine the role by the outcome it owns, then rule on hire vs agent vs don't fill — with a scorecard.
map_accountability$0.25Only WillowMap every outcome to a single owner, and expose the gaps and overlaps where work stalls.

Want to see what a deliverable actually looks like before you spend anything? 2 modes have a worked example written out in full — schema-faithful, free to read, and labelled as examples rather than receipts. The other 14 publish the same contract as live JSON Schema at /api/v1/schemas/{mode}/response.json.

Response

{
  "agent": "mary",
  "mode": "diagnose_growth",
  "reply": "Your problem isn't lead volume — it's that 18% close rate...",
  "thinking": {
    "diagnosis": "Conversion, not demand",
    "lever": "Rebuild the offer around a guaranteed outcome",
    "confidence": "high"
  },
  "numbers": [
    { "label": "Revenue per lead", "value": "$333", "basis": "$40,000 / 120 leads" }
  ],
  "actions": [
    {
      "title": "Rewrite the maintenance plan offer",
      "steps": ["Name the outcome and time frame", "Add a specific guarantee"],
      "effort": "days",
      "metric": "Close rate — target 18% to 25% within 60 days"
    }
  ],
  "caveats": ["Stated AOV and monthly revenue imply different close rates; used the stated AOV."],
  "deliverable": { "...": "mode-specific, see the schema for this mode" },
  "context_gaps": [],
  "usage": { "input_tokens": 1204, "output_tokens": 861 },
  "receipt": {
    "id": "rcpt_9f2c41a8b0d34e7f5c1a6b8e0d2f4a13",
    "url": "/api/v1/receipts/rcpt_9f2c41a8b0d34e7f5c1a6b8e0d2f4a13",
    "expires_at": "2026-08-08T14:22:10.441Z"
  }
}

caveats is the only field that carries uncertainty — if your inputs contradict each other, the coach picks a reading, uses it consistently, and tells you here. Every other field is a settled conclusion you can parse without stripping hedges out of it.

context_gaps lists the recommended context fields you left out. Fill them in and the next call gets materially sharper — that field exists so an agent can self-correct without reading these docs.

Sending context

Everything in context is optional, but the coaches are numerate — give them numbers and you get arithmetic instead of adjectives. Each mode names what it wants:

diagnose_growthcompany.what_they_sell, metrics.monthly_revenue_usd, metrics.leads_per_month, metrics.close_rate_pct
design_offercompany.what_they_sell, audience.icp, audience.pain, offer.price_usd
rewrite_pricingoffer.price_usd, offer.promise, metrics.average_order_value_usd, audience.icp
funnel_reviewmetrics.traffic_per_month, metrics.leads_per_month, metrics.close_rate_pct, company.website
write_outbound_sequenceaudience.icp, audience.pain, offer.promise, company.what_they_sell
audit_functioncompany.what_they_sell, company.stage, metrics.monthly_revenue_usd
handle_objectionsoffer.price_usd, offer.promise, audience.icp, metrics.close_rate_pct
position_against_alternativescompany.what_they_sell, audience.icp, offer.price_usd, company.industry
plan_capacitycompany.what_they_sell, metrics.monthly_revenue_usd, metrics.average_order_value_usd, company.stage
draft_sopcompany.what_they_sell, company.stage, company.industry
rescue_churnmetrics.monthly_churn_pct, metrics.average_order_value_usd, audience.icp, company.what_they_sell
plan_onboardingcompany.what_they_sell, offer.promise, audience.icp, metrics.monthly_churn_pct
decide_stackcompany.what_they_sell, company.stage, metrics.monthly_revenue_usd
map_automationcompany.what_they_sell, company.stage, company.industry
scope_rolecompany.what_they_sell, company.stage, metrics.monthly_revenue_usd, metrics.leads_per_month
map_accountabilitycompany.what_they_sell, company.stage, metrics.monthly_revenue_usd

Typed schemas

Every mode publishes JSON Schema for both sides of the call, generated from the same definitions the API validates against. Import them and each mode becomes a distinct strongly-typed tool in your framework.

GET /api/v1/schemas/{mode}/request.json
GET /api/v1/schemas/{mode}/response.json
GET /api/v1/openapi.json      # full OpenAPI 3.1 document
GET /api/v1/agents            # live catalog — free, no payment

Registering a mode as a tool (Claude / OpenAI tool-calling shape):

// Schemas are generated from the same definitions the API validates against,
// so fetching at startup keeps your tool in sync with the live contract.
const input_schema = await fetch(
  "https://gardenpatch.xyz/api/v1/schemas/diagnose_growth/request.json",
).then((r) => r.json());

const tool = {
  name: "gardenpatch_diagnose_growth",
  description:
    "Find the one binding growth constraint for a business and rank the levers " +
    "against it. Returns a diagnosis, supporting arithmetic, and concrete actions.",
  input_schema, // OpenAI calls this `parameters`
};

Discovery

Everything an agent needs to find and describe this API without a human reading the page. All of it is free and takes no payment header.

Live catalog/api/v1/agents

Every coach, every mode it serves, and the current price. Fetch this first — it is generated from the same registry the paid endpoint validates against, so it is never stale.

The full typed contract. Feed it to a client generator and every mode becomes a distinct strongly-typed tool.

A hardcoded catalog for skill-loading runtimes. Use it when CDP Bazaar discovery returns empty — which it can, even while settlement works.

x402 discovery/.well-known/x402

Resource list and ownership proof for x402scan and facilitators. This is what makes the paid routes findable from outside.

llms.txt/llms.txt

Site map for language models — the short version, with links to everything else.

llms-full.txt/llms-full.txt

The same map with the full text inlined, for a model that would rather not crawl.

One caveat worth knowing before you debug the wrong thing: Coinbase CDP Bazaar discovery/search can come back empty for Gardenpatch even when payments settle fine. If you are routing off CDP discovery and find nothing, that is why — load the skill file or the live catalog instead.

Paying

Use any x402 client. The wrapper handles the 402 → pay → retry handshake, so your code looks like a normal fetch. This is the whole client — there is no second step:

// npm i @x402/fetch @x402/evm viem
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
import { randomUUID } from "node:crypto";

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);

// Handles the 402 → pay → retry handshake for you. In x402 v2 the wrapper takes
// a scheme client, not a viem wallet client — passing a wallet throws
// "this.client.getExtensions is not a function" before any request goes out.
const fetchWithPay = wrapFetchWithPaymentFromConfig(fetch, {
  schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }],
});

// Reuse this key on every retry of THIS job. A new job gets a new key.
const idempotencyKey = randomUUID();

const res = await fetchWithPay("https://gardenpatch.xyz/api/v1/agents/mary/call", {
  method: "POST",
  headers: { "Content-Type": "application/json", "Idempotency-Key": idempotencyKey },
  body: JSON.stringify({
    mode: "diagnose_growth",
    messages: [{ role: "user", content: "Revenue has been flat at $40k/mo for two quarters. Where are we stuck?" }],
    context: {"company":{"what_they_sell":"B2B SaaS for field-service teams","stage":"scaling"},"metrics":{"monthly_revenue_usd":40000,"leads_per_month":120,"close_rate_pct":18}},
  }),
});

const { reply, thinking, numbers, actions, deliverable, receipt } = await res.json();

console.log(res.headers.get("Idempotent-Replay") === "true" ? "(replay)" : "(fresh)");

// receipt.expires_at non-null → receipt.url replays the call, free, until then.
// Null → nothing was stored, so keep the body yourself.
console.log(receipt.id, receipt.expires_at ?? "not stored");

One trap worth naming, because it is what an older code assistant will suggest: x402 v2 changed the wrapper's second argument from a viem wallet client to a scheme client. wrapFetchWithPayment(fetch, walletClient) still type-checks and still exists as an export, then throws this.client.getExtensions is not a function before anything reaches the network. If you see that, you are on the old shape.

Retrying safely

If your call times out after payment, retry it. You will get the same deliverable back and you will not be charged twice. This is the default — you do not have to opt in.

curl -sS -X POST https://gardenpatch.xyz/api/v1/agents/mary/call \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 7f1c9e2a-3b64-4d18-9a52-0c7e8d1b4a63' \
  -H "X-PAYMENT: $PAYMENT" \
  -d "$BODY"

# Timed out? Send it again — same key, same body, same wallet.
# Second response: HTTP 200, identical body, Idempotent-Replay: true, charged once.

A retry matches the original when three things match: the wallet that signed the payment, the exact request body, and the Idempotency-Key. Your payment header itself is expected to change — clients re-sign every attempt with a fresh nonce, and we bind to the payer, not the signature.

If you…Then
Retry with the same key, body, and walletSame deliverable, charged once. Response carries Idempotent-Replay: true.
Send no Idempotency-KeyWe derive one from your wallet and body, so a plain retry is still safe — but only for 10 minutes. Send a key to get the full 24 hours.
Change the body under the same keyA different job. It runs and it is charged.
Use two different keysTwo runs, two charges. This is how you deliberately re-run an identical request.
Retry while the first call is still running409 with Retry-After. You are not charged for the 409 — come back and collect the deliverable.
Get a 4xx or 5xxNothing was charged and nothing is cached. The key is free to reuse.

The replay window is 24 hours for a key you supply. A wallet only ever reaches its own deliverables — the payer is part of the match, so no one else's key or body can pull your work.

Receipts

Every successful call returns a receipt.id. When it is stored, it buys back the whole call — what you paid, on which network, the token usage, and the deliverable itself — for 7 days:

GET https://gardenpatch.xyz/api/v1/receipts/{receipt_id}    # free, no payment

Read receipt.expires_at before you rely on it. We mint an id and a url on every 200, but we only persist the call when we could reserve an idempotency scope for it. If expires_at is null, nothing was stored and that url will 404 — persist the response body yourself. A non-null expires_at is the timestamp the receipt stops resolving.

Receipts are for reconciling a charge after the fact, not for recovering a call you never read. If a request times out, retry it with the same Idempotency-Key — that returns the original deliverable without charging you twice, and it works whether or not a receipt was stored.

The id is a random bearer credential and is only ever returned inside the paid response that created it, so treat it like an API key. If you also send X-PAYMENT or X-Payer-Address, the wallet must match the receipt.

Errors & what you're charged for

Settlement happens only after a successful response. You are never charged for a malformed request, a rate limit, a capacity refusal, or a model failure on our side.

StatusMeaningCharged?
200Analysis returned.Yes
400Body didn't match the schema, or the agent doesn't serve that mode.No
402Payment required. Pay and retry the identical request.No
404Unknown agent.No
409An identical request is still running, or the key belongs to another payer. Retry after the header says.No
429Rate limited. Back off and retry.No
502Upstream model failure.No
503At capacity, or payments not configured. Retry later.No

What we do with what you send

The messages and context you send are forwarded to Anthropic to generate the response, and we log token counts for billing and capacity. Send business context, not personal data about your customers — this endpoint is built for company metrics, offers, and positioning, and nothing in the schema asks for anything else.

Contract stability

This is a versioned interface under /v1. We may add optional fields. We will not rename a field, make an optional field required, or narrow an enum without shipping /v2 alongside it.

Building something with this? Tell us what you need — we'll prioritise modes that people are actually calling.