Tenzro
Canton on Tenzro

Canton agentic.

Tenzro Network exposes Canton DAML as a native destination for autonomous agents. Mandate-bound writes, scoped party reads, and operator analytics — built on top of the existing Canton surface (tenzro_submitDamlCommand, tenzro_listDamlContracts, tenzro_canton_*), composed with the agent delegation fields on tenzro_createApiKey.
STATUS
Testnet
STABILITY
Stable
AUTHORIZATION
Key + Mandate + Delegation
REFERENCE
canton-agentic
01

Three RPCs

  • tenzro_canton_submitWithMandate — submit a DAML command bound to an AP2 mandate pair. Validates the cart against AP2 invariants + TDIP DelegationScope + runtime SpendingPolicy + optional on-chain escrow + optional Stripe SPT ceiling.
  • tenzro_canton_watchParty— active-contracts snapshot for a single party. Gated against the API key's can_read_as_parties allow-list.
  • tenzro_canton_aggregateAnalytics — operator admin-read of rolled-up per-key Canton call counters, grouped by subject or key_id.
02

Access and network selection

Canton is an operator-brokered resource — the ledger sits outside Tenzro and the node reaches it with credentials the operator supplies. Every RPC on this page needs an API key carrying the canton scope, sent as X-Tenzro-Api-Key. Keys do not gate the marketplace: publishing an agent, skill, workflow, or MCP server is permissionless, priced by the provider in TNZO or offered free, and needs no operator approval.

A node serves each Canton network independently, and a key is authorized for a subset of them. Name the target with the canton_network JSON-RPC param, the X-Canton-Network header on the Canton MCP server (its gate sits above the MCP envelope and cannot read tool arguments), or --canton-networkon the CLI. Resolution order is the explicit selector, then the key’s sole authorized network, then the node’s configured default. A key authorizing exactly one network needs no selector; a key authorizing more and given none returns -32004 naming the authorized set.

Each key carries a tier bounding its budget over a sliding 60-second window: free at 60 requests/min with writes refused, standard at 600, priority at 6,000. A mandate-bound write needs standard or above. Over-budget returns -32005 with retry_after_ms, requests_per_minute, and tier.

A tenant key acts as its bound primaryParty and is bounded by canton_networks. The operator admin token skips the key gate entirely, so it is bounded by no key and reaches party allocation, DAR upload, rights grants, and identity-provider management — but it must still name a network explicitly, because there is no key from which to infer one.

03

Mandate-bound write

The autonomous agent presents two things on every Canton write: a scoped API key with can_act_as_parties populated, and an AP2 cart mandate pair (a checkout VDC signed by the principal + a payment VDC signed by the agent).

{
  "jsonrpc": "2.0",
  "method": "tenzro_canton_submitWithMandate",
  "params": {
    "mandate": {
      "checkout": { /* AP2 checkout VDC */ },
      "payment":  { /* AP2 payment VDC */ }
    },
    "command_type": "create",
    "template_id": "#splice-amulet:Splice.AmuletRules:Transfer",
    "create_arguments": {
      "to": "Counterparty::abc123...",
      "amount": "1000000",
      "memo": "settlement-2026-06"
    }
  },
  "id": 1
}

The handler executes fail-closed at every step: parse → AP2 validate with delegation enforcement → forward the stripped command to the existing DAML submit handler → return both receipts.

04

Scoped read

tenzro_canton_watchParty is the agent-facing read surface. Authorization: the presenting API key must satisfy can_read_as(party) (which can_act_as_parties implies).

{
  "jsonrpc": "2.0",
  "method": "tenzro_canton_watchParty",
  "params": {
    "party": "TenzroLabs::abc123...",
    "template_ids": ["#splice-amulet:Splice.AmuletRules:Holding"]
  },
  "id": 1
}
05

Operator analytics

tenzro_canton_aggregateAnalytics rolls up the per-key counters CantonAnalyticsManager tracks. Admin-token-gated; tenants can't read across each other.

{
  "buckets": [
    { "key": "did:tenzro:machine:abc...", "total_calls": 12450, "last_called_at": 1717977600 },
    { "key": "did:tenzro:machine:def...", "total_calls": 3187,  "last_called_at": 1717976000 }
  ],
  "row_count": 2,
  "group_by": "subject"
}
06

Workflow templates

The Canton-settlement reference workflow template wraps submitWithMandate as a single-step workflow. Tenants instantiate it via tenzro_useResource; the workflow executor handles the dispatch.

See Resources for the full workflow runtime model.

07

SDK access

Rust SDK — the canton() accessor on TenzroClient:

let client = TenzroClient::connect(SdkConfig::testnet()).await?;
let canton = client.canton();
let receipt = canton
    .create_contract_with_mandate(&mandate, template_id, args, None)
    .await?;

TypeScript SDK — the CantonClient class:

import { TenzroClient } from '@tenzro/sdk';
const client = new TenzroClient({ rpcUrl: 'https://rpc.tenzro.xyz' });
const receipt = await client.canton.submitWithMandate(mandate, {
  command_type: 'create',
  template_id: templateId,
  create_arguments: args,
});

Mandate-bound writes, scoped party reads and the analytics rollup all live on the same client as the operator reads and writes, so onNetwork() pinning and the API-key tier apply uniformly.

← All docs