Quickstart
Prepare a tenant, publish pricing, deliver events and download evidence. All writes run from your own server.
Run the no-account synthetic examples or register, verify your email and create a workspace in the console. Failed provisioning can be retried safely; each account can create up to 3 workspaces during the beta. This documentation page does not receive credentials.
01 Prepare service URL and credentials
Create a workspace in the console and wait for ready before opening tenant management. To use the terminal commands below, the owner can claim the initial admin credential once. You also need the service origin and a Bash terminal with curl and jq. ledger.example.com is a placeholder. Keep credentials on your server or in shell environment variables, never in this website.
# Use your assigned service and tenant. Keep tokens in your shell only.
export OT_API_ROOT="https://ledger.example.com"
export OT_TENANT="your-tenant"
read -rs -p "Admin credential: " OT_ADMIN_TOKEN; echo
export OT_ADMIN_TOKEN02 Publish pricing and create an ingest credential
Replace the illustrative expression with your agreed price list. p and c are billable token variables. Example coefficients demonstrate syntax, not real model prices. Amounts use micro USD and authoritative calculation runs on the server. These prices are separate from the Open Tally free plan.
PRICING_HASH=$(curl --fail-with-body -sS "$OT_API_ROOT/tenant/pricing" \
-H "Authorization: Bearer $OT_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
--data '{"expr":"tier(\"standard\", p * 2 + c * 8)","label":"Your agreed price list"}' \
| jq -er '.pricing.hash')
export PRICING_HASH
curl --fail-with-body -sS "$OT_API_ROOT/tenant/credentials" \
-H "Authorization: Bearer $OT_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
--data '{"label":"billing-exporter"}'
# Securely save the returned token; it is shown only once.
read -rs -p "Ingest credential: " OT_INGEST_TOKEN; echo
export OT_INGEST_TOKEN03 Deliver an event from a real billing record
Map a real outbox event to real-event.json and submit it with one of the clients below. Keep source_event_id stable across retries. This page only provides documentation; it never connects to your ledger.
View the field template and replacement rules
This annotated template cannot be submitted as-is. Replace all placeholders with real data, using JSON integers for counts and timestamps. Do not invent production usage; synthetic traffic is rejected by the signer. Use the public fixtures to explore.
{
"stream": "your-stream",
"source_event_id": "<stable ID from your billing outbox>",
"generation": 6,
"occurred_at_ms": "<actual event time in milliseconds; JSON integer>",
"input": {
"RequestID": "<actual request ID>",
"StartedAtMs": "<actual request start; JSON integer>",
"Model": "<your catalog model identifier>",
"UnitType": "token",
"PromptTokens": "<actual input tokens; JSON integer>",
"CompletionTokens": "<actual output tokens; JSON integer>",
"Billable": true,
"Currency": "USD",
"UsageSemantic": "<openai or anthropic, matching actual upstream accounting>",
"UpstreamProvider": "<provider identifier>",
"UpstreamModelName": "<upstream declared model name>",
"Pricing": {
"ExprHash": "<published pricing hash>",
"P": "<billable p; JSON integer>",
"C": "<billable c; JSON integer>",
"Len": "<context len; JSON integer>"
}
}
}// Node.js 22+. real-event.json comes from your billing outbox.
import { readFile } from 'node:fs/promises';
const event = JSON.parse(await readFile('real-event.json', 'utf8'));
const response = await fetch(
process.env.OT_API_ROOT + '/t/' + process.env.OT_TENANT + '/ingest/events',
{
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.OT_INGEST_TOKEN,
'Content-Type': 'application/json',
},
body: JSON.stringify([event]),
redirect: 'error',
},
);
const result = await response.json();
if (!response.ok || result.success !== true) {
throw new Error(JSON.stringify(result));
}
console.log(result); // enqueued means accepted, not yet signed or sealed# Python 3. Use a real event from your billing outbox.
import json, os, urllib.request
class NoRedirect(urllib.request.HTTPRedirectHandler):
def redirect_request(self, *args, **kwargs):
return None
with open("real-event.json", encoding="utf-8") as source:
event = json.load(source)
url = os.environ["OT_API_ROOT"] + "/t/" + os.environ["OT_TENANT"] + "/ingest/events"
request = urllib.request.Request(url,
data=json.dumps([event]).encode(),
headers={"Authorization": "Bearer " + os.environ["OT_INGEST_TOKEN"],
"Content-Type": "application/json"}, method="POST")
with urllib.request.build_opener(NoRedirect).open(request, timeout=30) as response:
result = json.load(response)
if result.get("success") is not True:
raise RuntimeError(result)
print(result) # enqueued is not a verification result# jq is required. real-event.json must contain one real event object.
jq -s '.' real-event.json > batch.json
curl --fail-with-body -sS \
"$OT_API_ROOT/t/$OT_TENANT/ingest/events" \
-H "Authorization: Bearer $OT_INGEST_TOKEN" \
-H "Content-Type: application/json" \
--data-binary @batch.jsonHTTP success and enqueued mean accepted, not signed, timestamped or anchored. Retain original event IDs on batch retries and inspect event status for failure reasons.
04 Check readiness download and verify
Find an issued stream and sequence in the console; wait for the commitment and timestamp. Obtain the verifier through the public distribution information and check its provenance. Do not use the demo TSA root for a real ledger.
curl --fail-with-body -sS "$OT_API_ROOT/tenant/status" \
-H "Authorization: Bearer $OT_ADMIN_TOKEN"
# Choose an actual issued stream and sequence from the console.
export OT_STREAM="your-stream"
export OT_SEQ="1"
curl --fail-with-body -sS -G \
"$OT_API_ROOT/t/$OT_TENANT/api/receipt/bundle" \
--data-urlencode "stream=$OT_STREAM" --data-urlencode "seq=$OT_SEQ" \
-o bundle.json
opentally-verify -bundle bundle.json -json
# Anonymous disclosure may SKIP amount checks. Inspect every check.This download is anonymous and only returns the hash layer. Use an audit grant or customer token for supplier-cost or customer-itemization evidence. Never interpret SKIPPED as PASS.
Read about verification and disclosure