YOUR FIRST INTEGRATION

Quickstart

Prepare a tenant, publish pricing, deliver events and download evidence. All writes run from your own server.

No tenant yet?

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.

bash
# 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_TOKEN

02 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.

bash
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_TOKEN

03 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.

JSON template
{
  "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>"
    }
  }
}
JavaScript
// 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

HTTP 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.

bash
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

Search developer documentation

Enter a search term to find documentation.

Esc to close · No credentials are sent