Observed
Stratl received these events. The application could, in principle, have bypassed its own instrumentation.
SDKs · OpenTelemetry · pulled traces
Each stage is built on a published standard, and none of them sits in the path of your agents. This page describes what runs today. What is still missing is on the early access page.
From an SDK, OpenTelemetry, Langfuse or the Anthropic Compliance API.
model_call · tool_call orders.read · approval priya@example.comOne decision record per business subject.
DEC-01J8ZK3Q… · order/48213 · 7 events · observedHashed, signed in AWS KMS, chained, sealed, timestamped.
sha256:4a030b51… · ES256 · chain #1 · checkpoint #1Bundles, notices, coverage and period packs.
DEC-01J8ZK3Q….stratl.zip · Record integrity: VALIDStratl collects from capture points you already have. There is no gateway to deploy and nothing to put in front of your models.
import stratl
with stratl.decision(
agent="support-bot",
subject=("order", "48213"),
agent_version="2026.09.1",
) as d:
d.input(ticket)
d.model("openai", "gpt-5", prompt, response)
d.tool("orders.read", {"order_id": "48213"}, order)
d.policy("refunds_above_100_require_approval", version=7, result="human_required")
d.approval(required=True, approver="priya@example.com", authority="support_lead",
decision="approved", identity_provider="okta")
d.action("refunds.issue", target="order/48213", result="executed")
d.outcome(adverse=False, category="refund")Whatever you pass in is fingerprinted on your side. The record carries the fingerprints; the originals are uploaded to the custody bucket so they can be re-hashed later. If Stratl cannot be reached, the record is spooled to disk and sent on the next run.
import { Client, decision } from "@stratl/sdk";
const client = new Client(); // reads STRATL_API_URL and STRATL_API_KEY
await decision("support-bot", ["order", "48213"], async (d) => {
await d.input(ticket);
await d.model("openai", "gpt-5", prompt, response);
await d.tool("orders.read", { order_id: "48213" }, order);
await d.policy("refunds_above_100_require_approval", { version: 7, result: "human_required" });
await d.approval({ required: true, approver: "priya@example.com", authority: "support_lead",
decision: "approved", identityProvider: "okta" });
await d.action("refunds.issue", { target: "order/48213", result: "executed" });
await d.outcome({ adverse: false, category: "refund" });
}, client, { agentVersion: "2026.09.1" });Same rules as Python: content is fingerprinted on your side, the record carries the fingerprints, and the originals are uploaded to the custody bucket. The two SDKs produce the same fingerprint for the same content. Under Node, an unreachable API means the record is spooled to disk and sent later.
exporters:
otlphttp/stratl:
traces_endpoint: https://api.stratl.ai/v1/ingest/otlp/v1/traces
encoding: json
headers:
Authorization: Bearer ${env:STRATL_API_KEY}
service:
pipelines:
traces:
exporters: [otlphttp/stratl]Point a collector at Stratl and one trace becomes one decision record. Set stratl.subject.type and stratl.subject.id on any span so the record is filed under the business subject rather than the trace id.
curl -X POST https://api.stratl.ai/v1/ingest/srf \
-H "Authorization: Bearer $STRATL_API_KEY" \
-H "Content-Type: application/json" \
-d @decision.jsonAny language can post a Stratl Record Format document. Stratl computes the hash, signs it and links it into the workspace chain. Posting the same decision_id twice is safe.
Context manager. Spools to disk offline.
Async function wrapper.
GenAI traces, OTLP with JSON encoding.
Pulled on a schedule.
Pulled on a schedule.
Approving a notice, a reconsideration.
The Stratl Record Format is small on purpose. It holds what a reviewer needs to reconstruct a decision, in the order it happened.
inputThe request that started the decision, kept as a fingerprint. The original goes to the custody bucket if you send it.
{
"seq": 1,
"type": "input",
"at": "2026-09-17T14:31:02.000Z",
"hash": "sha256:5e884898da28047151d0e56f8dc62927…"
}Lets a reviewer confirm which request led to this decision without Stratl indexing its content.
retrievalWhich source was queried and which documents came back, each as a fingerprint.
{
"seq": 2,
"type": "retrieval",
"at": "2026-09-17T14:31:03.000Z",
"source": "refund-policy",
"doc_refs": [
"sha256:9f86d081884c7d659a2feaa0c55ad015…"
]
}Shows the policy or knowledge the agent relied on at that moment, not the version that exists today.
model_callProvider, model and served version, with fingerprints of the prompt and the response.
{
"seq": 3,
"type": "model_call",
"at": "2026-09-17T14:31:04.000Z",
"provider": "openai",
"model": "gpt-5",
"version": "0611",
"input_hash": "sha256:5e884898da28…",
"output_hash": "sha256:2c26b46b68ff…"
}Pins the exact model version. Prompts and outputs never enter the search index.
tool_callEach tool the agent called, with fingerprints of its arguments and its result, and whether it succeeded.
{
"seq": 4,
"type": "tool_call",
"at": "2026-09-17T14:31:06.000Z",
"name": "orders.read",
"args_hash": "sha256:fcde2b2edba5…",
"result_hash": "sha256:9f86d081884c…",
"status": "ok"
}After an incident, this is the ordered account of what the agent actually touched.
policy_evalThe policy that was evaluated, its version, and what it returned.
{
"seq": 5,
"type": "policy_eval",
"at": "2026-09-17T14:31:11.000Z",
"policy_id": "refunds_above_100_require_approval",
"version": "7",
"result": "human_required"
}Proves the control ran on this decision, and which version of it, rather than asserting that a policy exists.
approvalThe person, their authority, how their identity was established, and how long they took.
{
"seq": 6,
"type": "approval",
"at": "2026-09-17T14:32:00.000Z",
"required": true,
"requested_at": "2026-09-17T14:31:42.000Z",
"approver": "priya@example.com",
"identity_provider": "okta",
"authority": "support_lead",
"decision": "approved"
}Human oversight becomes a fact with a name and a timestamp, not a claim in a policy document.
actionThe action, its target, a fingerprint of its parameters, and whether it executed.
{
"seq": 7,
"type": "action",
"at": "2026-09-17T14:32:02.000Z",
"name": "refunds.issue",
"target": "order/48213",
"params_hash": "sha256:a665a4592042…",
"executed_at": "2026-09-17T14:32:02.000Z",
"result": "executed"
}Separates what the agent proposed from what really happened to the customer or the system.
outcomeThe result, and whether it was adverse for the person or thing it was about.
{
"seq": 8,
"type": "outcome",
"at": "2026-09-17T14:32:03.000Z",
"status": "completed",
"adverse": false,
"category": "refund"
}Adverse outcomes are what trigger notices, reconsideration and most framework obligations.
Change one character and the whole fingerprint changes. The text cannot be recovered from it.
Stratl received these events. The application could, in principle, have bypassed its own instrumentation.
SDKs · OpenTelemetry · pulled traces
The source signed or sealed its own log before Stratl received it.
signed or sealed source logs
The action could not have happened without passing the control point that recorded it.
Stratl's notice and reconsideration workflow
Stratl never raises a level. A record captured as observed stays observed, however it is stored afterwards. Being exact about this is the point.
Each step uses a published standard, so a third party can check the result with their own tools and does not have to take our word for any of it.
Serialised one exact way, so the same record gives the same bytes in any language.
RFC 8785Those bytes are fingerprinted. Python, TypeScript and Go are tested to agree.
SHA-256AWS KMS signs with your workspace's key. Every signature is checked before anything is written.
ES256 · RFC 7638Each record names the hash of the one before it, so a gap or reorder shows.
per-workspace chainEvery hour, or every thousand records, one signed Merkle root commits them all.
RFC 6962An authority that is neither you nor Stratl stamps the checkpoint. Failures are retried.
RFC 3161You choose a custody region when you create a workspace. Your signing key is created there and your records are stored there, and neither one moves afterwards.
One per workspace, created inside KMS and not exportable. Every signature is logged by AWS CloudTrail.
One prefix per workspace. Every version of every object is kept. A retention lock can make versions undeletable for a set period.
Prompts, responses and payloads you choose to send, stored under their own fingerprint so they can be re-hashed against the record.
Fingerprints and metadata only: subjects, agent names, outcomes, timestamps. It is shared infrastructure and is not in your custody region.
Or take custody: your own bucket and your own key, on AWS, Google Cloud or Azure, from the Custody page. Stratl proves it can sign, write and not delete before anything moves, and the hand-over is a statement signed by both keys.
The reason to keep records is to give them to someone. Everything Stratl serves is built from fields in the record and says where each statement came from.
Your request was declined.outcome.category
An automated system assessed it, and a member of our support team reviewed the result before it was final.events[5].approver
The main reason was that the item was returned after our 14-day period for a change of mind.events[4].rule
You can ask for a person to look at this again, and you can ask what information was used.pack: colorado_admt
The signed record, the key that signed it, the checkpoint with its timestamp, the originals and a manifest, in one archive that verifies offline.
When a person reviews a decision again, the review, its reviewer and its result become their own record, linked to the original.
Which fields the Colorado AI Act, the EU AI Act, ISO/IEC 42001, NIST AI RMF, the NAIC model bulletin and ECOA Regulation B ask for, and which are missing. Coverage, not certification.
Everything for a date range in one archive with a signed manifest, for an audit or a regulator's request.
Nothing. Stratl is never in the request path. The SDK submits after the decision is made, and if it cannot reach Stratl it writes the record to a local spool and sends it on a later run.
No. The record only needs their fingerprints, which the SDK computes on your side. Sending the originals is optional. If you do, they are stored in the custody bucket under their fingerprint so they can be re-hashed later, and they never enter the search index.
It means the events came from instrumentation inside your application, which could in principle have been bypassed. That is an honest description of most SDK and tracing data. The record says so, the verifier reports it, and Stratl never raises the level afterwards.
By the business subject it was about: the order, claim, applicant or service. Every record carries a subject, and everything about that subject is listed together, including notices and reconsiderations.
Yes. The verifier is also compiled to WebAssembly and runs inside your browser tab on the Verify page. It performs the same checks as the command-line verifier, and neither one calls Stratl.
Each record and checkpoint carries the id of the key that signed it, and Stratl keeps every public key that has ever signed for a workspace. A record is always checked against its own key, so earlier records keep verifying.
The fastest way to judge this is to record one real decision and verify it yourself. It takes a few lines of code and no sales call.