Meridian documentation
Meridian lets engineering teams express treasury policy as code, deploy it to TON and USD₮ rails under a non-custodial multisig, and reconcile it against an on-chain audit trail. This is the primary developer reference. If you have not yet been granted workspace access, apply for access first.
Quickstart
Get from a fresh workspace to a deployed policy in roughly five minutes.
1. Install the SDK
npm install @meridian/sdk
npx meridian login
npx meridian init treasury
2. Author a policy
// treasury/policies/vendor-payouts.ts
import { meridian, USD } from "@meridian/sdk";
export const policy = meridian.policy({
name: "weekly-vendor-payouts",
authorizers: ["finance-multisig"],
schedule: "every friday at 14:00 utc",
limits: { perRun: USD(500_000) },
execute: async ({ ledger, pay }) => {
const invoices = await ledger.openInvoices({ approved: true });
for (const inv of invoices) {
await pay(inv.counterparty, inv.amount, { rail: "usdt-ton" });
}
},
});
3. Simulate against forked TON state
npx meridian simulate ./treasury/policies/vendor-payouts.ts \
--fork mainnet \
--balance "USDT:1000000"
4. Deploy to staging, then production
npx meridian deploy ./treasury/policies/vendor-payouts.ts --env staging
npx meridian promote weekly-vendor-payouts --to production
Promotion to production opens an approval in the console requiring multisig sign-off on the compiled policy hash.
Workspaces & authentication
Each Meridian client receives a workspace scoped to a single legal entity. Inside a workspace you have one or more environments (typically staging and production), each with its own multisig, counterparty registry, and policy set.
SDK and CLI calls authenticate using a workspace API key plus a signed device. API keys are scoped to a single environment and are never written to disk in plaintext — the CLI delegates signing to a paired hardware key or your OS keychain.
| Scope | Created by | Lifespan |
|---|---|---|
| Workspace key | Console admin | Rotated quarterly |
| Environment key | SDK meridian login | 30 days, rolling |
| Device key | Hardware-paired | Until revoked |
Policies
A policy is the unit of work in Meridian. It is a typed, version-controlled function that emits a sequence of payment instructions when it executes. Policies are deterministic: a policy compiled at hash H emits the same instructions every run, modulo external state.
A policy has five components:
name— the canonical identifier inside a workspace. Cannot be changed after first deployment.authorizers— the named multisigs that must co-sign every instruction this policy emits.schedule— cron expression, natural-language schedule, or webhook trigger.limits— hard caps per run, per counterparty, and per period. Enforced at compile time by the verifier.execute— the body that runs against the ledger and emits instructions.
Instructions
An instruction is a single signed payment payload. Instructions move through five states: drafted, awaiting_signatures, broadcast, settled, reconciled. The protocol guarantees that no instruction transitions from awaiting_signatures to broadcast without the full quorum present.
Authorizers & multisig
Authorizers are named multisigs registered to a workspace. Meridian supports four authorizer types:
- Safe{Wallet} on TON — multisig signed in-app or via WalletConnect.
- Fireblocks MPC — co-signer model with HSM-backed signing.
- Hardware — Ledger and Trezor signers in 2-of-3 or 3-of-5 configurations.
- Hybrid — combinations of the above (Fireblocks + Ledger is common).
The protocol's executor key is never an authorizer. It can only co-broadcast a transaction whose value-bearing payload has already been signed by the client's authorizers.
Counterparties
A counterparty is any external entity to which your treasury sends funds or from which it receives funds. Every counterparty is registered before the first instruction settles, and registration triggers KYB via Sumsub for entity-level counterparties or sanctions screening via Chainalysis KYT for individual addresses.
Reconciliation
Reconciliation is automatic. The reconciliation indexer consumes the TON ledger and matches every settled instruction back to its originating policy and authorizer set. Signed reconciliation reports are published per policy on a fixed cadence (default: weekly) and via webhook on every settlement.
SDK · install
npm install @meridian/sdk # or
pnpm add @meridian/sdk # or
bun add @meridian/sdk
Runtime support: Node.js 20+, Bun 1.1+, Deno 1.40+. The SDK is reproducible-build certified and signed with Sigstore.
meridian.policy()
The policy() factory is the primary entrypoint. It returns a CompiledPolicy which can be deployed to a workspace.
type PolicyArgs = {
name: string;
authorizers: string[];
schedule: string | CronExpression | WebhookTrigger;
limits?: PolicyLimits;
execute: (ctx: PolicyContext) => Promise<void>;
};
pay() & ledger
Inside the execute function, the policy context exposes ledger (read-only) and pay (instruction-emitting). All pay calls are queued and atomically authorized at end-of-run.
await pay(counterpartyId, amount, {
rail: "usdt-ton" | "ton",
memo?: string,
priority?: "normal" | "high",
});
Simulate & deploy
Simulation forks TON state at a chosen block, applies any --balance overrides, and runs the policy without touching production keys. Output is a structured trace including every instruction, the required signers, and the projected fee.
HTTP API · authentication
The HTTP API lives at https://api.meridian.fi/v1. All requests require a Bearer token issued from the console.
Authorization: Bearer mrn_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Test-mode keys are prefixed mrn_test_ and resolve to the staging environment. Test-mode instructions never settle on mainnet.
Instructions endpoints
/v1/instructionsSubmit a payment instruction for multisig review. Body must include policy, amount, currency, rail, counterparty.id, and an idempotency_key.
/v1/instructions/:idRead instruction status, signatures, and on-chain proof.
/v1/instructions?status=awaiting_signaturesList instructions, filterable by status, policy, counterparty, created_after.
Policies endpoints
/v1/policies/v1/policies/v1/policies/:nameBalances
/v1/balancesReturns current balances grouped by asset and policy. All values are reconciled against on-chain state at request time; the response includes a reconciled_at timestamp.
Counterparties
/v1/counterparties/v1/counterparties/:idAudit log
/v1/audit/logStream the full on-chain audit log. Supports server-sent events for live tail. Every event includes the policy hash, the authorizer quorum, and the on-chain transaction reference.
Webhook events
Subscribe to webhooks per workspace. Each event is a JSON document signed with the workspace's Ed25519 key.
| Event | Description |
|---|---|
instruction.drafted | A policy run produced an instruction. |
instruction.signed | An authorizer signed. |
instruction.settled | Settled on TON. Includes tx_hash and block. |
instruction.failed | Settlement attempt failed; queued for retry or manual review. |
policy.deployed | A new policy version was promoted. |
reconciliation.report | A signed reconciliation report is available. |
Webhook signing
Every webhook includes X-Meridian-Signature (Ed25519, base64) and X-Meridian-Timestamp. Reject any payload older than 300 seconds. The signing key is rotatable per workspace via the console.
import { verify } from "@meridian/sdk/webhook";
const ok = verify({
payload: req.rawBody,
signature: req.headers["x-meridian-signature"],
timestamp: req.headers["x-meridian-timestamp"],
publicKey: process.env.MERIDIAN_WEBHOOK_KEY,
});
Retries
Failed deliveries are retried with exponential backoff up to 24 hours. After that, events land in a dead-letter queue inspectable from the console. The retry schedule: 30s, 5m, 30m, 2h, 6h, 24h.
Errors
The HTTP API uses standard status codes plus a structured error body.
| Status | Code | Meaning |
|---|---|---|
400 | invalid_request | The payload is malformed or missing required fields. |
401 | unauthorized | API key missing, expired, or revoked. |
403 | policy_violation | The instruction would breach a policy limit. |
404 | not_found | The resource does not exist within this workspace. |
409 | idempotency_conflict | Idempotency key reused with a different payload. |
422 | counterparty_unscreened | Counterparty has not completed KYB or sanctions screening. |
429 | rate_limited | Workspace exceeded its per-second burst. |
503 | executor_paused | Executor is paused due to RPC or oracle divergence. |
Rate limits
The default rate limit is 60 requests per second per workspace, with a 200-request burst. Limits are enforced per environment. Audit-log streams do not count against the rate limit. Higher limits are available on request.
Idempotency
Every POST endpoint accepts an idempotency_key in the body. Reuse the same key for safe retries — Meridian returns the original response. Keys are scoped to a workspace and retained for 7 days.
Cookbook
Patterns the design-partner cohort has converged on.
Bi-weekly payroll with cooldown
meridian.policy({
name: "payroll-bi-weekly",
schedule: meridian.cron("0 12 * * 4/2", "UTC"),
limits: {
perRun: USD(300_000),
cooldownHours: 336,
},
execute: async ({ pay }) => { /* … */ },
});
Threshold-driven FX rebalance
meridian.policy({
name: "ton-rebalance-threshold",
schedule: meridian.onBalance({ asset: "TON", above: USD(5_000_000) }),
execute: async ({ ledger, swap }) => {
const excess = await ledger.excessOver("TON", USD(3_000_000));
await swap("TON", "USDT", excess, { venue: "licensed-fx-desk" });
},
});
Webhook-driven receivable
meridian.policy({
name: "recurring-receivable",
schedule: meridian.onWebhook("invoice.issued"),
execute: async ({ event, expect }) => {
await expect(event.amount).from(event.payerId).within("7d");
},
});
Changelog
| Version | Date | Notes |
|---|---|---|
v0.4.2 | 2026-05-08 | Webhook signing key rotation via console. |
v0.4.1 | 2026-04-22 | Idempotency window extended to 7 days. |
v0.4.0 | 2026-04-01 | onBalance trigger; swap() primitive (private preview). |
v0.3.5 | 2026-02-18 | SDK reproducible builds. Sigstore verification enforced. |
v0.3.0 | 2025-11-12 | Counterparty KYB pipeline (Sumsub). |
Need something not covered here? Email se@meridian.fi. The SDK is open source at github.com/meridian-fi/sdk (private during preview — request access for the link).