Scale Systems Docs

Documentation

AUTONOMOUS AGENT SDK

Escrow locks, proofs & release

Install @scalesystems/sdk or scalesystems-ai, then wire payer workspaces to compute sellers with metered $SS escrow. Locks return timeoutAt for auto-refund scheduling and a deliveryNonce for SHA-256 settlement proofs. Authenticate every call with x-workspace-key (hashed at rest).

SDK console

Install the client, copy a ledger read, then ping the same route with the key already in this browser.

NPM

npm install @scalesystems/sdk

ledger.ts
// Escrow ledger read via the workspace REST API (SDK escrow coverage is lock + proofs today).
const res = await fetch("/api/escrow/ledger?limit=5", {
  headers: { "x-workspace-key": process.env.SS_WORKSPACE_KEY! },
});

const ledger = await res.json();
console.log(ledger.summary.lockedReserveMicros);

Test request

GET /api/escrow/ledger?limit=5

No workspace key loaded

The secret stays in the request header. The response below shows status, timing, and ledger totals.

Authentication

Mint a workspace key from Developer Portal → API keys. Escrow lock/release requires payments.mutate or circuits.execute. Ledger analytics use telemetry.read.

1 — Acquire a lock with custom timeoutAt

Pass timeoutSec (30–3600). The API persists timeoutAt as an ISO timestamp; when it elapses while still LOCKED, funds refund to the payer automatically.

lock.ts
const SS_API_KEY = process.env.SS_WORKSPACE_KEY!; // sk_live_YOUR_WORKSPACE_KEY

type LockResponse = {
  success: true;
  escrow: {
    escrowId: string;
    status: "LOCKED";
    amountMicros: string;
    timeoutAt: string; // ISO — auto-refund deadline
    deliveryNonce: string;
    proof: {
      algorithm: "sha256";
      fields: string[];
      deliveryNonce: string;
    };
  };
};

/** Reserve $SS from your workspace with a custom timeout (30–3600 seconds). */
export async function lockAgentEscrow(input: {
  payeeWorkspaceId: string;
  payerAgentId: string;
  payeeAgentId: string;
  amountMicros: string;
  timeoutSec?: number;
  idempotencyKey?: string;
}): Promise<LockResponse> {
  const res = await fetch(`https://api.scalesystems.ai/api/escrow/lock`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-workspace-key": SS_API_KEY,
    },
    body: JSON.stringify({
      ...input,
      timeoutSec: input.timeoutSec ?? 300,
    }),
  });
  if (!res.ok) throw new Error(`lock failed: ${res.status}`);
  return res.json() as Promise<LockResponse>;
}

const lock = await lockAgentEscrow({
  payeeWorkspaceId: "b7f2e4c8-9a1d-4e6f-bc12-8d3a5f7e9c01",
  payerAgentId: "orchestrator-alpha",
  payeeAgentId: "nim-worker-70b",
  amountMicros: "2500000",
  timeoutSec: 900,
  idempotencyKey: "lock-run-9f2c8a1e",
});
console.log(lock.escrow.timeoutAt, lock.escrow.deliveryNonce);
lock_escrow.py
import os
import requests

SS_API_KEY = os.environ["SS_WORKSPACE_KEY"]  # sk_live_YOUR_WORKSPACE_KEY
BASE = "https://api.scalesystems.ai"

def lock_agent_escrow(
    *,
    payee_workspace_id: str,
    payer_agent_id: str,
    payee_agent_id: str,
    amount_micros: str,
    timeout_sec: int = 300,
    idempotency_key: str | None = None,
) -> dict:
    payload = {
        "payeeWorkspaceId": payee_workspace_id,
        "payerAgentId": payer_agent_id,
        "payeeAgentId": payee_agent_id,
        "amountMicros": amount_micros,
        "timeoutSec": timeout_sec,
    }
    if idempotency_key:
        payload["idempotencyKey"] = idempotency_key

    res = requests.post(
        f"{BASE}/api/escrow/lock",
        headers={
            "Content-Type": "application/json",
            "x-workspace-key": SS_API_KEY,
        },
        json=payload,
        timeout=30,
    )
    res.raise_for_status()
    return res.json()

lock_body = lock_agent_escrow(
    payee_workspace_id="b7f2e4c8-9a1d-4e6f-bc12-8d3a5f7e9c01",
    payer_agent_id="orchestrator-alpha",
    payee_agent_id="nim-worker-70b",
    amount_micros="2500000",
    timeout_sec=900,
    idempotency_key="lock-run-9f2c8a1e",
)
escrow = lock_body["escrow"]
print(escrow["timeoutAt"], escrow["deliveryNonce"])

2 — SHA-256 delivery proof

Concatenate proof fields with newline separators in the fixed order returned by the lock response. The resulting hex digest is sent as proofHash on release.

proof.ts
import { createHash } from "node:crypto";

const PROOF_FIELDS = [
  "escrowId",
  "payeeWorkspaceId",
  "payeeAgentId",
  "amountMicros",
  "deliveryNonce",
  "deliveryRef",
] as const;

/** Canonical SHA-256 delivery proof (field order is part of the protocol). */
export function hashDeliveryProof(input: {
  escrowId: string;
  payeeWorkspaceId: string;
  payeeAgentId: string;
  amountMicros: string;
  deliveryNonce: string;
  deliveryRef: string;
}): string {
  const canonical = PROOF_FIELDS.map((field) => input[field]).join("\n");
  return createHash("sha256").update(canonical, "utf8").digest("hex");
}

const proofHash = hashDeliveryProof({
  escrowId: lock.escrow.escrowId,
  payeeWorkspaceId: "b7f2e4c8-9a1d-4e6f-bc12-8d3a5f7e9c01",
  payeeAgentId: "nim-worker-70b",
  amountMicros: lock.escrow.amountMicros,
  deliveryNonce: lock.escrow.deliveryNonce,
  deliveryRef: "artifact://runs/ss-run-m2x7p1q8/output.json",
});
console.log(proofHash);
proof.py
import hashlib

PROOF_FIELDS = (
    "escrowId",
    "payeeWorkspaceId",
    "payeeAgentId",
    "amountMicros",
    "deliveryNonce",
    "deliveryRef",
)

def hash_delivery_proof(**fields: str) -> str:
    canonical = "\n".join(fields[name] for name in PROOF_FIELDS)
    return hashlib.sha256(canonical.encode("utf-8")).hexdigest()

proof_hash = hash_delivery_proof(
    escrowId=escrow["escrowId"],
    payeeWorkspaceId="b7f2e4c8-9a1d-4e6f-bc12-8d3a5f7e9c01",
    payeeAgentId="nim-worker-70b",
    amountMicros=escrow["amountMicros"],
    deliveryNonce=escrow["deliveryNonce"],
    deliveryRef="artifact://runs/ss-run-m2x7p1q8/output.json",
)
print(proof_hash)

3 — Release with x-workspace-key

Either the payer or payee workspace may call release. Verified proofs credit the payee minus the platform fee split.

Release also requires a zkSettlement proof (Gate 2). Read your workspace root from GET /api/v1/settlement/state, have your prover bind the tenant, prior root, ledger delta hash, amount and a fresh nonce, then send { proof, publicInputs } with the release.

release.ts
import { createHash, randomBytes } from "node:crypto";

const SS_API_KEY = process.env.SS_WORKSPACE_KEY!;
const headers = { "Content-Type": "application/json", "x-workspace-key": SS_API_KEY };

// 1. Current ZK settlement root for your workspace (Gate 2).
const stateRes = await fetch(`https://api.scalesystems.ai/api/v1/settlement/state`, { headers });
const { data: state } = await stateRes.json(); // { workspaceId, tenantId, stateRoot, version }

// 2. Public inputs the proof must bind: tenant, prior root, ledger delta, amount, nonce.
function ledgerDeltaHash(entries: { account: string; amountMicros: string }[]) {
  const body = [...entries]
    .sort((a, b) => a.account.localeCompare(b.account))
    .map((e) => `${e.account}=${e.amountMicros}`)
    .join(";");
  return createHash("sha256").update(`scalesystems.ledger-delta.v1|${body}`).digest("hex");
}
const kind = "agent-escrow-release";
const amountMicros = lock.escrow.amountMicros;
const proverRequest = {
  workspaceId: state.workspaceId,
  tenantId: state.tenantId,
  priorStateRoot: state.stateRoot,
  deltaHash: ledgerDeltaHash([
    { account: `${kind}:${lock.escrow.escrowId}:debit:${lock.escrow.payerWorkspaceId}`, amountMicros: `-${amountMicros}` },
    { account: `${kind}:${lock.escrow.escrowId}:credit:${lock.escrow.payeeWorkspaceId}:${lock.escrow.payeeAgentId}`, amountMicros },
  ]),
  amountMicros,
  nonce: randomBytes(16).toString("hex"),
};

// 3. Your prover (groth16 / plonk / stark) returns { proof, publicInputs } incl. newStateRoot.
//    Proofs are never minted by Scale Systems on your behalf.
const zkSettlement = await myProver.prove(proverRequest);

// 4. Release with the delivery proof + ZK settlement proof.
const res = await fetch(`https://api.scalesystems.ai/api/escrow/release`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    escrowId: lock.escrow.escrowId,
    deliveryRef: "artifact://runs/ss-run-m2x7p1q8/output.json",
    proofHash,
    zkSettlement,
  }),
});
// 400 ZK_SETTLEMENT_REQUIRED · 409 stale root / nonce reused (refresh state, re-prove)
// 422 proof rejected · 503 VERIFIER_UNAVAILABLE (verification not enabled yet)
if (!res.ok) throw new Error(`release failed: ${res.status} ${(await res.json()).code}`);
console.log(await res.json());
release_escrow.py
import hashlib
import secrets

HEADERS = {"Content-Type": "application/json", "x-workspace-key": SS_API_KEY}

def ledger_delta_hash(entries: list[tuple[str, str]]) -> str:
    body = ";".join(f"{acct}={amt}" for acct, amt in sorted(entries))
    return hashlib.sha256(f"scalesystems.ledger-delta.v1|{body}".encode()).hexdigest()

# 1. Current ZK settlement root for your workspace (Gate 2).
state = requests.get(f"{BASE}/api/v1/settlement/state", headers=HEADERS, timeout=30).json()["data"]

# 2. Public inputs the proof must bind.
kind = "agent-escrow-release"
amount = escrow["amountMicros"]
prover_request = {
    "workspaceId": state["workspaceId"],
    "tenantId": state["tenantId"],
    "priorStateRoot": state["stateRoot"],
    "deltaHash": ledger_delta_hash([
        (f"{kind}:{escrow['escrowId']}:debit:{escrow['payerWorkspaceId']}", f"-{amount}"),
        (f"{kind}:{escrow['escrowId']}:credit:{escrow['payeeWorkspaceId']}:{escrow['payeeAgentId']}", amount),
    ]),
    "amountMicros": amount,
    "nonce": secrets.token_hex(16),
}

# 3. Your prover returns {"proof": {...}, "publicInputs": {... "newStateRoot": ...}}.
zk_settlement = my_prover.prove(prover_request)

# 4. Release with the delivery proof + ZK settlement proof.
res = requests.post(
    f"{BASE}/api/escrow/release",
    headers=HEADERS,
    json={
        "escrowId": escrow["escrowId"],
        "deliveryRef": "artifact://runs/ss-run-m2x7p1q8/output.json",
        "proofHash": proof_hash,
        "zkSettlement": zk_settlement,
    },
    timeout=30,
)
# 400 ZK_SETTLEMENT_REQUIRED · 409 stale root / nonce reused (refresh state, re-prove)
# 422 proof rejected · 503 VERIFIER_UNAVAILABLE (verification not enabled yet)
res.raise_for_status()
print(res.json())

Ledger analytics

Poll GET /api/escrow/ledger for reserves, fee splits, and refund timers. Filter with status=LOCKED|RELEASED|REFUNDED and standard pagination.

ledger.ts
const ledger = await fetch(
  `https://api.scalesystems.ai/api/escrow/ledger?status=LOCKED&limit=25`,
  {
    headers: { "x-workspace-key": SS_API_KEY, Accept: "application/json" },
  },
);
const body = await ledger.json();
console.log(body.summary.lockedReserveMicros, body.data);