Build on the back office.
Every service exposes a typed REST API and an MCP server. OpenAPI for humans. MCP descriptors for agents. Signed event streams for everyone else. Six SDKs share one Rust core — your TypeScript signature matches your Rust signature, byte for byte.
API Explorer
POST /v1/payments · live
curl -X POST https://api.gardens.ml/v1/payments \
-H "Authorization: Bearer sk_live_····················" \
-H "Idempotency-Key: pi_2c4f1a" \
-d @- <<'JSON'
{
"amount": 1482000,
"currency": "USD",
"from_entity": "did:oas:org:acme",
"to_entity": "did:oas:org:northwood",
"corridor": "USA->EU",
"policy": "lowest_fee_under_60s"
}
JSONHTTP/1.1 200 OK
content-type: application/json
vine-event-id: evt_91a8c4f
chard-rail: wise-sepa-instant
bean-journal: J-2026-04-29-04417
{
"id": "pi_2c4f1a",
"status": "routed",
"rail": "wise-sepa-instant",
"fee": "$3.20",
"estimated_eta": "8s",
"settlement": {
"expected": "2026-04-29T17:42:16Z",
"currency": "EUR",
"amount": "13,710.42"
}
}Three languages. One canonical flow.
Identity → capability → action → lineage. The same four-step pattern in every Garden SDK. Whichever language you use, the wire format is identical and the response headers carry the same lineage proof.
import { Garden } from "@l1fe/garden";
const garden = new Garden({
did: "did:oas:org:acme:cfo",
authToken: process.env.GARDEN_TOKEN!,
});
// 1. Mint a scoped capability (≤ $50k, 1-hour TTL)
const cap = await garden.arsenal.mint({
scope: "cabbage.invoices.issue",
ttlSec: 3600,
maxAmount: 50_000_00,
});
// 2. Issue an invoice — idempotency-keyed
const inv = await garden.cabbage.invoices.issue({
customer: "acme:1042-A",
lines: [{ sku: "S-ORC", qty: 1 }],
capability: cap.id,
}, { idempotencyKey: "inv_iss_2941" });
// 3. Lineage headers come back on every response
console.log(inv.lineage);
// → { bean: "J-2026-04-29-04417",
// vine: "evt_91a8c4f",
// chard: "intent_queued" }from l1fe_garden import Garden
garden = Garden(
did="did:oas:org:acme:cfo",
auth_token=os.environ["GARDEN_TOKEN"],
)
# 1. Mint a scoped capability
cap = garden.arsenal.mint(
scope="cabbage.invoices.issue",
ttl_sec=3600,
max_amount=5_000_000,
)
# 2. Issue an invoice — idempotency-keyed
inv = garden.cabbage.invoices.issue(
customer="acme:1042-A",
lines=[{"sku": "S-ORC", "qty": 1}],
capability=cap.id,
idempotency_key="inv_iss_2941",
)
# 3. Lineage headers on every response
print(inv.lineage)
# → {"bean": "J-2026-04-29-04417",
# "vine": "evt_91a8c4f",
# "chard": "intent_queued"}use garden_sdk::{Garden, MintScope};
let garden = Garden::builder()
.did("did:oas:org:acme:cfo")
.auth_token(std::env::var("GARDEN_TOKEN")?)
.build()?;
// 1. Mint a scoped capability
let cap = garden.arsenal().mint(
MintScope::CabbageInvoicesIssue {
max_amount_cents: 5_000_000,
},
Duration::from_secs(3600),
).await?;
// 2. Issue an invoice — idempotency-keyed
let inv = garden.cabbage().invoices().issue(
IssueInvoice {
customer: "acme:1042-A".into(),
lines: vec![Line::sku("S-ORC", 1)],
capability: cap.id,
},
).idempotency_key("inv_iss_2941").send().await?;
// 3. Lineage headers on every response
println!("{:?}", inv.lineage);
// → Lineage { bean: "J-2026-04-29-04417",
// vine: "evt_91a8c4f",
// chard: "intent_queued" }Six languages. One Rust core.
cargo add garden-sdk
bun add @l1fe/garden
go get go.l1fe.ai/garden
pip install l1fe-garden
swift package add
implementation
The whole surface, service by service.
Each product page shows one exchange. This page shows everything — every tool, endpoint, and header, in one place.
MCP tools
- tool bean.journal.draft Draft a balanced journal entry
- tool bean.journal.post Post a drafted entry (idempotent)
- tool bean.period.close Close period with seal (signed)
- tool bean.trial.balance Trial balance for entity + period
- tool bean.reverse Reverse a posted entry
- resource coa://acme/us-parent Chart of accounts (live)
REST
- POST /bean/v1/journals — Draft + post (idempotency-key required)
- POST /bean/v1/journals/:id/reverse — Generate a reversing entry
- POST /bean/v1/periods/close — Close period and seal
- GET /bean/v1/trial-balance — Trial balance per entity & period
- GET /bean/v1/audit/stream — Hash-chained event stream
Headers
- Authorization: Bearer arsenal:act_7d…
- X-Garden-Principal: did:oas:org:acme:cfo
- X-Garden-Agent: did:agent:tomato/0x4f1
- X-Garden-Capability: bean.journal.post · scope=us-parent
- X-Garden-Idempotency: J-2026-04-28-04417
MCP tools
- tool chard.payments.create Authorize a payment (idempotent)
- tool chard.refunds.create Refund full or partial
- tool chard.transfers.create Move funds (rail-agnostic)
- tool chard.disputes.respond Submit dispute evidence
- resource routes://usd-eur Live corridor scores
REST
- POST /chard/v1/payments — Authorize + capture in one call
- POST /chard/v1/refunds — Refund partial or full
- POST /chard/v1/transfers — Multi-rail transfer
- POST /chard/v1/disputes — Submit dispute response
- GET /chard/v1/routes/:corridor — Live rail rankings
Headers
- Authorization: Bearer arsenal:act_d2…
- X-Garden-Capability: chard.payments.create · max=14820 · corridor=usd-eur
- X-Garden-Idempotency: pi_2c4f1a
- X-Garden-Policy: lowest_fee_under_60s
MCP tools
- tool cabbage.invoices.issue Issue an invoice for a customer
- tool cabbage.subscriptions.upgrade Apply proration + ramp
- tool cabbage.pricing.set Update a pricing plan (signed)
- tool cabbage.dunning.advance Move account to next dunning step
- resource contract://acme/sub_2941 Live subscription contract
REST
- POST /cabbage/v1/invoices — Issue invoice (idempotent)
- POST /cabbage/v1/subscriptions — Create subscription
- PATCH /cabbage/v1/subscriptions/:id — Upgrade / downgrade / pause
- POST /cabbage/v1/pricing/plans — Create or version a plan
- POST /cabbage/v1/dunning/runs — Advance dunning campaign
Headers
- Authorization: Bearer arsenal:act_ce…
- X-Garden-Capability: cabbage.invoices.issue · max_amount=5000
- X-Garden-Approval-Policy: invoice > 5000 USD requires human
- X-Garden-Idempotency: inv_iss_pi_2c4f1a
MCP tools
- tool kale.sweep.execute Move cash per sweep rule (signed)
- tool kale.fx.hedge.open Open FX forward up to policy cap
- tool kale.allocation.update Update yield allocation
- tool kale.forecast.run Re-run cash forecast
- resource positions://kale Live position snapshot
REST
- GET /kale/v1/positions — Cash positions by account
- POST /kale/v1/sweeps — Trigger sweep rule
- POST /kale/v1/fx/hedges — Open / close hedge
- POST /kale/v1/forecasts — Run forecast against pipeline
- GET /kale/v1/exposure — FX exposure by pair
Headers
- Authorization: Bearer arsenal:act_kl…
- X-Garden-Capability: kale.sweep.execute · max=80000
- X-Garden-Policy: target_balance=$300k · counterparty<$2.5M
- X-Garden-Audit: sealed into Bean as fx_pnl + sweep entries
MCP tools
- tool greenhouse.transfers.draft Draft a transfer (returns sign-envelope)
- tool greenhouse.transfers.sign Submit partial FROST signature
- tool greenhouse.policy.evaluate Dry-run policy against a draft
- resource signers://acme/ops Active signer ring
REST
- POST /greenhouse/v1/transfers — Draft transfer (returns sign envelope)
- POST /greenhouse/v1/transfers/:id/sign — Submit FROST partial signature
- POST /greenhouse/v1/policy/evaluate — Dry-run a policy
- GET /greenhouse/v1/signers — Active signers in the ring
- POST /greenhouse/v1/recovery/ceremony — Initiate guardian ceremony
Headers
- Authorization: Bearer arsenal:act_gh…
- X-Garden-Capability: greenhouse.transfers.draft · max=120400 USDC
- X-Garden-Threshold: 2-of-3 (CFO, CEO, ops/03)
- X-Garden-Signers-Held: 1 (ops/03 only) · 2 more required
MCP tools
- tool turnip.session.elevate Request step-up for sensitive action
- tool turnip.capability.mint Mint a scoped Arsenal token
- tool turnip.capability.revoke Revoke active capability
- tool turnip.signer.enroll Enroll a hardware device
- resource ring://turnip/acme Active signer ring
REST
- POST /turnip/v1/sessions/elevate — Step up session (WebAuthn / YubiKey)
- POST /turnip/v1/capabilities — Mint scoped capability token
- DELETE /turnip/v1/capabilities/:id — Revoke a live capability
- POST /turnip/v1/signers — Enroll a hardware signer
- GET /turnip/v1/lineage/:id — Resolve an action back to its human root
Headers
- Authorization: Bearer arsenal:act_tn…
- X-Garden-Capability: turnip.capability.mint · scope=bean.period.close
- X-Garden-TTL: 90s · principal=did:oas:org:acme:cfo
- X-Garden-Hardware-Proof: yk:5C:19284441 (touched 2026-04-29T16:41:02Z)
MCP tools
- tool sage.screen.run Run sanctions + PEP + adverse media
- tool sage.kyc.start Start a KYC session
- tool sage.case.review Submit reviewer decision (signed)
- tool sage.evidence.fetch Fetch sealed evidence packet
- resource cases://sage/open Live caseload
REST
- POST /sage/v1/screenings — Run a screen against name + DOB + country
- POST /sage/v1/kyc/sessions — Start a KYC session
- GET /sage/v1/screenings/:id — Status + hits + evidence URI
- POST /sage/v1/cases/:id/review — Submit a review decision
- GET /sage/v1/evidence/:case — Fetch sealed packet (PDF + JSON)
Headers
- Authorization: Bearer arsenal:act_sg…
- X-Garden-Capability: sage.screen.run · scope=counterparty-onboard
- X-Garden-Audit-Trail: posted to Bean on approve, refused on hit
- X-Garden-Reviewer: did:oas:org:acme:compliance@hl
MCP tools
- tool branches.balance.fetch Live balance per connection
- tool branches.transactions.list Stream transactions with cursor
- tool branches.ach.originate Originate ACH (capability-gated)
- tool branches.wire.draft Draft wire instructions
- resource connections://acme All linked accounts
REST
- GET /branches/v1/balances — Authoritative balance per connection
- GET /branches/v1/transactions — Cursor-paginated transactions
- POST /branches/v1/ach — Originate ACH (cap-gated)
- POST /branches/v1/wires — Draft wire instructions
- GET /branches/v1/connections — Connection health + sync latency
Headers
- Authorization: Bearer arsenal:act_br…
- X-Garden-Capability: branches.balance.fetch (read-only)
- X-Garden-Source-Quorum: plaid+mx+direct (2-of-3 agreed)
- X-Garden-Stale-Threshold: 5m → flag, 30m → block writes
MCP tools
- tool vine.events.subscribe Subscribe to topic(s) under scope
- tool vine.events.replay Replay from cursor
- tool vine.events.ack Acknowledge cursor advance
- resource topics://vine Live topic registry
REST
- GET /vine/v1/events — Cursor-paginated event stream
- POST /vine/v1/subscriptions — Create webhook subscription
- POST /vine/v1/replays — Replay from cursor
- GET /vine/v1/topics — List topics + retention
- GET /vine/v1/dlq — Dead-letter queue
Headers
- Authorization: Bearer arsenal:act_vn…
- X-Garden-Capability: vine.events.subscribe · topics=cabbage.* chard.*
- X-Garden-Cursor: 92010
- X-Garden-Signature: ed25519=…
MCP tools
- tool celery.recon.draft Draft an adjusting entry
- tool celery.case.assign Assign case to reviewer
- tool celery.case.resolve Resolve case (signed)
- tool celery.tolerance.set Set per-pair tolerance
- resource cases://celery/open Live caseload
REST
- GET /celery/v1/cases — Open recon cases
- POST /celery/v1/cases/:id/draft — Draft adjusting entry
- POST /celery/v1/cases/:id/resolve — Resolve (post or skip)
- GET /celery/v1/matrix — Cross-source matrix snapshot
- PATCH /celery/v1/tolerances — Update per-pair tolerance bands
Headers
- Authorization: Bearer arsenal:act_cl…
- X-Garden-Capability: celery.case.resolve · max=10 · max_amount=$50
- X-Garden-Policy: confidence>0.95 AND amount<$50 → auto-resolve
- X-Garden-Audit: every adjustment posts to Bean with explanation
MCP tools
- tool figs.workspace.switch Switch entity / workspace
- tool figs.command.run Invoke any command in the palette
- tool figs.window.present Surface a particular view to user
- resource workspaces://figs Active workspaces
REST
- GET /figs/v1/workspaces — List + switch workspaces
- POST /figs/v1/commands — Execute a palette command
- GET /figs/v1/state — Local sync + cache state
- POST /figs/v1/updates/check — Check for a signed update
- GET /figs/v1/mcp/manifest — MCP descriptor served locally
Headers
- Authorization: Bearer arsenal:act_fg…
- X-Garden-Capability: figs.command.run · workspace=us-parent
- X-Garden-Local: paired to YubiKey 5C · serial 19284441
- X-Garden-Update-Chain: signed manifest blake3:dbb0a7be…
MCP tools
- tool basil.document.render Render an invoice / receipt / 1099
- tool basil.dispatch.send Send via channel(s) — email / push / sms / post
- tool basil.template.publish Publish a template version (signed)
- tool basil.receipt.fetch Fetch sealed proof-of-delivery
- resource templates://basil Live template catalogue
REST
- POST /basil/v1/documents/render — Render a signed financial document
- POST /basil/v1/dispatch — Multi-channel dispatch
- GET /basil/v1/dispatch/:id — Status + receipt
- POST /basil/v1/templates — Publish a template version
- GET /basil/v1/dlq — Dead-letter view
Headers
- Authorization: Bearer arsenal:act_bs…
- X-Garden-Capability: basil.dispatch.send · channels=email,post
- X-Garden-Document-Seal: blake3:7a91be… (sealed before send)
- X-Garden-Audit: proof-of-delivery posts to Bean on receipt
Speak to Garden as an agent.
Every Garden service ships an MCP server. Connect your model with one line; let it browse the schema, ask for capabilities, and act under an Arsenal token.
# .mcp.json
{
"mcpServers": {
"garden": {
"command": "garden",
"args": ["mcp", "--scope", "default"],
"env": {
"GARDEN_DID": "did:oas:org:acme",
"GARDEN_ACT": "arsenal:act_7d…"
}
}
}
}
# Try it
garden mcp call cabbage.invoices.issue \
--customer "did:oas:org:acme:1042-A" \
--item '{"sku":"S-ORC","qty":1}' \
--policy 'approval > 5000'At-least-once delivery, signed envelopes, 90-day replay.
POST https://acme.example/hooks/garden
X-Garden-Event: inv.issued
X-Garden-Cursor: 92010
X-Garden-Sig: ed25519=…
X-Garden-Tenant: acme
{
"event": "inv.issued",
"data": {
"invoice": "INV-2941",
"customer": "did:oas:org:acme:1042-A",
"total": 1482000,
"currency": "USD",
"bound": {
"bean": "J-2026-04-28-04417",
"chard": "pi_2c4f…"
}
},
"ts": "2026-04-28T14:02:11Z"
}import { verifyEvent } from "@l1fe/garden/webhooks";
app.post("/hooks/garden", express.raw({ type: "*/*" }),
async (req, res) => {
const ok = await verifyEvent({
body: req.body, // raw bytes
signature: req.header("X-Garden-Sig")!,
tenantPubKey: process.env.GARDEN_TENANT_PUBKEY!,
});
if (!ok) return res.status(400).send("bad sig");
const evt = JSON.parse(req.body);
// Cursor lets you resume from any point.
const cursor = Number(req.header("X-Garden-Cursor"));
await consume(evt, cursor);
res.status(200).end();
});from l1fe_garden.webhooks import verify_event
from flask import Flask, request, abort
app = Flask(__name__)
@app.post("/hooks/garden")
def hook():
ok = verify_event(
body=request.get_data(),
signature=request.headers["X-Garden-Sig"],
tenant_pub_key=os.environ["GARDEN_TENANT_PUBKEY"],
)
if not ok:
abort(400, "bad sig")
evt = request.get_json()
cursor = int(request.headers["X-Garden-Cursor"])
consume(evt, cursor)
return ("", 200)use axum::{extract::Json, http::HeaderMap, body::Bytes};
use garden_sdk::webhooks::verify_event;
async fn hook(
headers: HeaderMap,
body: Bytes,
) -> Result<(), AppError> {
let sig = headers.get("X-Garden-Sig")
.ok_or(AppError::MissingSig)?.to_str()?;
verify_event(
&body,
sig,
&std::env::var("GARDEN_TENANT_PUBKEY")?,
)?;
let evt: Event = serde_json::from_slice(&body)?;
let cursor: u64 = headers["X-Garden-Cursor"]
.to_str()?.parse()?;
consume(evt, cursor).await?;
Ok(())
}


