Developers

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.

Garden Financial
Search...⌘K
3
AACME Holdings
NRNina

API Explorer

GARDEN API · /v1/payments

POST /v1/payments · live

200 · 242 ms
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"
}
JSON
responseapplication/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
GARDEN DESKTOPThe 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
Reference

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
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(())
}