Skip to content
Open the portal
Developer docs

TypeScript SDK reference

Engineers10 min read

@stratl/sdk (sdk/typescript) mirrors the Python SDK method for method, with the same field names in the record and the same fingerprint rules for content. It ships as ESM and CommonJS with types, and its record format module also runs in the browser, where the portal uses it.

Terminal window
npm install @stratl/sdk

decision(agent, subject, fn, client?, opts?)

Section titled “decision(agent, subject, fn, client?, opts?)”

Runs fn with a builder, then builds the record and, if a client was given, submits it with its artifacts. Submits even when fn throws, then rethrows.

const { result, record, artifacts, submitted } = await decision(
"claims-triage", ["claim", "8843"],
async (d) => { /* events */ return value; },
client,
{ agentName: "Claims Triage", agentVersion: "2026.09.1", subjectDisplay: "Claim 8843" },
);

submitted is whatever the client returned (for Client, a SubmitResult).

All content methods are async because hashing uses WebCrypto.

Method Event
d.input(content) input
d.retrieval(source, docs[]) retrieval
d.model(provider, model, prompt, response, { version?, config? }) model_call (a plain string fifth argument is accepted as the version)
d.tool(name, args, result, status = "ok") tool_call
d.policy(policyId, { version, result, rule? }) policy_eval
d.approval({ required, approver?, authority?, decision?, identityProvider?, comment? }) approval, and the approver becomes a human actor
d.actor(kind, id, { role?, identityProvider? }) adds a participant
d.action(name, { target, result, params? }) action
d.outcome({ adverse, category?, status?, effectOnSubject? }) outcome
d.build({ assurance?, custody? }) the Decision, without submitting

d.artifacts holds { "sha256:…": { data: Uint8Array, contentType } } for every fingerprint in the record.

Returns ["sha256:<hex>", artifact]. Strings hash as UTF-8, Uint8Array as is, everything else as RFC 8785 canonical JSON. Both SDKs assert the same reference hashes in their test suites, so the same content gets the same fingerprint in Python and TypeScript.

new Client({ baseUrl?, apiKey?, spoolDir?, timeoutMs?, storeArtifacts? })
new Client(baseUrl, apiKey) // the older positional form still works

Reads STRATL_API_URL, STRATL_API_KEY and, under Node, STRATL_SPOOL_DIR (default .stratl-spool). spoolDir: false disables spooling.

Method Does
client.submit(decision, { artifacts? }) → SubmitResult Resolves, never rejects. { status: "recorded", decision_id, record_hash } when accepted; { status: "spooled", … } when the API was unreachable or answered 5xx or 429 and the record was written to the spool; { status: "failed", error } when the API refused the record (4xx) or no spool is available (the browser)
client.spooled() Records waiting in the spool
client.flushSpool() → { sent, remaining, error? } Resends spooled records in order, with artifacts; stops at the first failure
client.custodyLabels() The workspace’s custody labels, fetched once; applied by submit to records with the default labels

The spool is Node only (it uses node:fs through a guarded dynamic import, so the browser build is unaffected) and writes the same file format as the Python SDK.

canonicalizeJson(value), canonicalSha256(value) and unsignedPayload(record) (strips integrity and every null or undefined member) implement the same rules as stratl.srf in Python and internal/srf in Go. The conformance test hashes every golden fixture and must agree with the other two on every change. The portal’s Verify page uses this module together with the Go verifier compiled to WebAssembly.