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

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