Developers
Build on the back office.
Every Garden service exposes a typed REST API and an MCP server. We publish OpenAPI for humans, MCP descriptors for agents, and signed event streams for everyone else. SDKs in six languages share a single Rust core via WASM — your TypeScript signature byte-for-byte matches your Rust signature.
Garden Financial · v1.3.2 — DesktopLIVE
Search…⌘K
AACME Holdings
API Explorer
Updated Apr 29 at 14:02
GARDEN API · /v1/payments
POST /v1/payments · live
requestcurl
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"
}
JSONresponseapplication/json
HTTP/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"
}
} every response carries lineage headers · chard rail · bean entry · vine eventv1.4 · 99.99% uptime · 9 regions
FIG. 0.A — GARDEN DESKTOP / DASHBOARDThe actual UI. No marketing simulator.
Quickstart
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.
TypeScriptissue-invoice.ts
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" }Pythonissue-invoice.py
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"}Rustissue-invoice.rs
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" }SDKs
Six languages. One Rust core.
Rust
garden-sdk
cargo add garden-sdk
TypeScript
@l1fe/garden
bun add @l1fe/garden
Go
go.l1fe.ai/garden
go get go.l1fe.ai/garden
Python
l1fe-garden
pip install l1fe-garden
Swift
l1fe-garden-swift
swift package add
Kotlin
ai.l1fe:garden
implementation
API
A representative selection.
POST /bean/v1/journalsPost a balanced journal entry
POST /bean/v1/periods/closeClose a period and seal it
GET /bean/v1/trial-balanceTrial balance for an entity & period
POST /cabbage/v1/invoicesIssue an invoice
POST /cabbage/v1/subscriptionsCreate a subscription
POST /chard/v1/paymentsAuthorize and capture a payment
POST /chard/v1/refundsRefund a payment, partial or full
POST /greenhouse/v1/transfersInitiate a transfer (returns sign envelope)
POST /greenhouse/v1/transfers/signSubmit FROST partial signatures
GET /branches/v1/balancesAuthoritative balance per connection
GET /sage/v1/screenings/{id}Sanctions and KYC screening status
POST /turnip/v1/sessions/elevateStep up a session for sensitive action
GET /vine/v1/eventsStream events with cursor pagination
POST /celery/v1/recon/draftDraft adjusting entries
POST /basil/v1/documents/renderRender a receipt, invoice, or 1099
MCP
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'Webhooks · Vine
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"
}Verify · TypeScripted25519
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();
});Verify · Pythoned25519
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)Verify · Rusted25519
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(())
}

















