Public package: The @truthlock/sdk package is available on the public
npm registry. No special configuration required.
Installation
bash npm npm install @truthlock/sdk bash yarn yarn add @truthlock/sdk bash pnpm pnpm add @truthlock/sdk
Quick Start
import { TruthlockClient, Algorithm, Verdict } from "@truthlock/sdk";
// 1. Create client
const client = new TruthlockClient({
baseUrl: "https://api.truthlocks.com",
auth: {
type: "apiKey",
apiKey: "tl_live_...",
tenantId: "your-tenant-id",
},
});
// 2. Create and trust an issuer
const issuer = await client.issuers.create({
name: "My Organization",
legal_name: "My Organization Inc.",
display_name: "My Org",
});
await client.issuers.trust(issuer.id);
// 3. Register a signing key
await client.keys.register(issuer.id, {
kid: "key-2026",
alg: Algorithm.Ed25519,
public_key_b64url: "your-public-key-base64url",
});
// 4. Mint an attestation
const attestation = await client.attestations.mint({
issuer_id: issuer.id,
kid: "key-2026",
alg: Algorithm.Ed25519,
schema: "degree",
claims: {
student_name: "Jane Doe",
degree_type: "Bachelor of Science",
},
});
console.log("Attestation ID:", attestation.id);
// 5. Verify
const result = await client.verify.verifyOnline({
attestation_id: attestation.id,
});
if (result.verdict === Verdict.Valid) {
console.log("Attestation verified successfully");
}
Revoke an attestation
Permanently invalidate an attestation. Once revoked, any verification check returns REVOKED. This action cannot be undone — if you need to issue an updated credential instead, use supersede.
import { TruthlockClient, Verdict } from "@truthlock/sdk";
const client = new TruthlockClient({
baseUrl: "https://api.truthlocks.com",
auth: { type: "apiKey", apiKey: "tl_live_...", tenantId: "your-tenant-id" },
});
const revoked = await client.attestations.revoke(
"660e8400-e29b-41d4-a716-446655440001",
{ reason: "Certificate holder no longer employed" }
);
console.log("Status:", revoked.status); // "REVOKED"
console.log("Revoked at:", revoked.revoked_at);
// Subsequent verifications reflect the revocation
const check = await client.verify.verifyOnline({
attestation_id: "660e8400-e29b-41d4-a716-446655440001",
});
console.log("Verdict:", check.verdict); // "REVOKED"
Revocation is permanent and recorded in the transparency log. You cannot undo it. Use supersede if you need to replace a credential with an updated version.
Supersede an attestation
Replace an existing attestation with an updated version. The original is marked as SUPERSEDED and linked to the new one, creating an auditable version chain.
import { TruthlockClient, Algorithm } from "@truthlock/sdk";
const client = new TruthlockClient({
baseUrl: "https://api.truthlocks.com",
auth: { type: "apiKey", apiKey: "tl_live_...", tenantId: "your-tenant-id" },
});
// Supersede an attestation with updated claims
const updatedPayload = Buffer.from(
JSON.stringify({
student_name: "Jane Doe",
degree_type: "Master of Science", // Updated from Bachelor to Master
})
).toString("base64url");
const result = await client.attestations.supersede(
"660e8400-e29b-41d4-a716-446655440001", // Original attestation ID
{ new_payload_b64url: updatedPayload }
);
console.log("Old attestation status:", result.old.status); // "SUPERSEDED"
console.log("New attestation ID:", result.new.attestation_id);
Both the original and new attestation remain in the transparency log. Verifiers can trace the full chain using the superseded_by_attestation_id field on the original.
Configuration
const client = new TruthlockClient({
// Required
baseUrl: 'https://api.truthlocks.com', // API base URL
auth: { ... }, // See Authentication below
// Optional
timeout: 30_000, // Request timeout in ms (default: 30s)
maxRetries: 3, // Auto-retry with exponential backoff
idempotencyPrefix: 'my-app', // Prefix for auto-generated idempotency keys
});
Idempotency keys
The SDK generates an Idempotency-Key header automatically on every write operation (mint, revoke, supersede), making retries safe by default. If a request fails and is retried, the server returns the original response instead of performing the action twice.
When multiple applications share the same tenant, set idempotencyPrefix to namespace the auto-generated keys and prevent collisions:
// Service A
const billingClient = new TruthlockClient({
baseUrl: "https://api.truthlocks.com",
auth: { type: "apiKey", apiKey: "tl_live_...", tenantId: "shared-tenant-id" },
idempotencyPrefix: "billing-svc",
});
// Service B
const onboardingClient = new TruthlockClient({
baseUrl: "https://api.truthlocks.com",
auth: { type: "apiKey", apiKey: "tl_live_...", tenantId: "shared-tenant-id" },
idempotencyPrefix: "onboarding-svc",
});
With these prefixes, a key generated by the billing service looks like billing-svc_<uuid>, while onboarding produces onboarding-svc_<uuid> — so concurrent requests from different services never conflict.
Idempotency keys expire after 24 hours. If you omit idempotencyPrefix, keys are generated without a prefix and are still unique per request.
Authentication
Three authentication methods are supported. API Key is recommended for server-side applications.
auth: {
type: 'apiKey',
apiKey: 'tl_live_...', // From Console > Settings > API Keys
tenantId: 'your-tenant-id', // From Console > Settings > General
}
Bearer Token (session-based)
auth: {
type: 'bearer',
token: 'eyJhbGciOi...', // JWT from login flow
}
Service Key (machine-to-machine)
auth: {
type: 'service',
apiKey: 'tl_svc_...',
tenantId: 'your-tenant-id',
}
Retry behavior
When maxRetries is set (default: 3), the SDK automatically retries failed requests using exponential backoff with jitter.
What gets retried
The SDK retries a request when all of the following are true:
- The HTTP status code is retryable:
408, 429, 500, 502, 503, or 504
- The retry count has not exceeded
maxRetries
- The request has not been aborted via an
AbortController
Responses with 400, 401, 403, 404, or 409 are not retried.
Backoff schedule
| Attempt | Base delay | With jitter (approx.) |
|---|
| 1st retry | 100 ms | 80–120 ms |
| 2nd retry | 200 ms | 160–240 ms |
| 3rd retry | 400 ms | 320–480 ms |
The delay doubles on each attempt, capped at 2 seconds. Jitter adds ±20% randomization.
When the API returns a 429 with a Retry-After header, the SDK waits the server-specified duration instead.
Disabling retries
const client = new TruthlockClient({
baseUrl: "https://api.truthlocks.com",
auth: { type: "apiKey", apiKey: "tl_live_...", tenantId: "your-tenant-id" },
maxRetries: 0, // No automatic retries
});
The SDK generates idempotency keys automatically for write operations, so retries for mint and revoke calls are safe. You can set a custom prefix with the idempotencyPrefix option.
Error handling
import { TruthlockClient, TruthlockError } from "@truthlock/sdk";
try {
const attestation = await client.attestations.mint(data);
} catch (error) {
if (error instanceof TruthlockError) {
console.log("Code:", error.code); // e.g. "ISSUER_NOT_TRUSTED"
console.log("Status:", error.status); // HTTP status code
console.log("Message:", error.message); // Human-readable
if (error.code === "RATE_LIMITED") {
// Already retried maxRetries times — back off at the application level
}
}
throw error;
}
API methods
All methods return typed Promises.
Issuers
client.issuers.create(data)
client.issuers.get(id)
client.issuers.list()
client.issuers.trust(id)
client.issuers.suspend(id)
client.issuers.revoke(id, reason)
Keys
client.keys.register(issuerId, data)
client.keys.list(issuerId)
client.keys.rotate(kid, data)
client.keys.reportCompromise(kid)
Attestations
client.attestations.mint(data)
client.attestations.get(id)
client.attestations.list()
client.attestations.revoke(id, data)
client.attestations.supersede(id, data) — see supersede API
client.attestations.getProofBundle(id)
Receipts
client.receipts.mint(data)
client.receipts.get(id)
client.receipts.list(filter?)
client.receipts.revoke(id, data?)
client.receipts.listTypes()
client.receipts.getType(name)
client.receipts.createType(data)
client.receipts.getProofBundle(id) — see proof bundle API
client.receipts.verify(receiptId) — see verify API
client.receipts.search(query) — see search API
client.receipts.export(req) — see export API
client.receipts.getExport(exportId)
client.receipts.listExports()
client.receipts.redact(id) — see redact API
Verification
client.verify.verifyOnline(data)
API Keys
client.apiKeys.list()
client.apiKeys.create(data)
client.apiKeys.revoke(id)
Audit
client.audit.query(params)
client.audit.export(data)
Governance
client.governance.listRequests()
client.governance.createRequest(data)
client.governance.approveRequest(id)
client.governance.executeRequest(id)
Governance workflows
Manage formal issuer actions — suspend, revoke, reinstate, and change trust tier — through a multi-party approval workflow. Create a request, collect approvals from authorized reviewers, then execute.
import { TruthlockClient } from "@truthlock/sdk";
const client = new TruthlockClient({
baseUrl: "https://api.truthlocks.com",
auth: { type: "apiKey", apiKey: "tl_live_...", tenantId: "your-tenant-id" },
});
// Create a governance request to suspend an issuer
const req = await client.governance.createRequest({
action: "suspend",
issuer_id: "issuer-uuid",
reason: "Compliance review pending",
});
console.log(`Request created: ${req.id} (status: ${req.status})`);
// Another authorized user approves the request
await client.governance.approveRequest(req.id);
// Execute the approved request
const result = await client.governance.executeRequest(req.id);
console.log(`Executed: ${result.id} — issuer is now ${result.issuer_status}`);
// List all governance requests
const requests = await client.governance.listRequests();
for (const r of requests) {
console.log(`[${r.status}] ${r.action} ${r.issuer_id} — ${r.reason}`);
}
See the governance API reference for the full request and response schemas.
Audit queries and exports
Query audit events with filters and export logs for compliance reporting. Use client.audit.query() to search events and client.audit.export() to start an asynchronous export job.
import { TruthlockClient } from "@truthlock/sdk";
const client = new TruthlockClient({
baseUrl: "https://api.truthlocks.com",
auth: { type: "apiKey", apiKey: "tl_live_...", tenantId: "your-tenant-id" },
});
// Query recent attestation events
const events = await client.audit.query({
action: "attestation.mint",
from: "2026-06-01T00:00:00Z",
to: "2026-06-30T23:59:59Z",
limit: 100,
});
for (const evt of events.data) {
console.log(`[${evt.timestamp}] ${evt.action} by ${evt.actor_id}`);
}
// Start an async export for compliance
const exportJob = await client.audit.export({
from: "2026-01-01T00:00:00Z",
to: "2026-06-30T23:59:59Z",
report_type: "soc2", // "soc2", "gdpr", "hipaa", or omit for raw
format: "json",
});
console.log(`Export started: ${exportJob.id} (status: ${exportJob.status})`);
See audit logs for the full event structure, filter parameters, and retention policies.
Receipt operations
Mint, verify, search, export, and redact cryptographically signed receipts. See the receipts guide for an overview of receipt types and the full lifecycle.
import { TruthlockClient } from "@truthlock/sdk";
const client = new TruthlockClient({
baseUrl: "https://api.truthlocks.com",
auth: { type: "apiKey", apiKey: "tl_live_...", tenantId: "your-tenant-id" },
});
// Mint a payment receipt
const receipt = await client.receipts.mint({
issuer_id: "your-issuer-id",
kid: "ed-key-2026",
alg: "Ed25519",
receipt_type: "payment_receipt",
subject: "customer@example.com",
payload: {
amount: 5000,
currency: "USD",
provider: "stripe",
provider_reference: "ch_3Px...",
},
});
// Verify a receipt
const result = await client.receipts.verify(receipt.receipt_id);
console.log("Verdict:", result.verdict); // "VALID"
// Download a proof bundle for offline verification
const bundle = await client.receipts.getProofBundle(receipt.receipt_id);
// Search receipts
const results = await client.receipts.search({
q: "stripe",
receipt_type: "payment_receipt",
from_date: "2026-01-01T00:00:00Z",
});
// Export receipts as JSON
const exportJob = await client.receipts.export({
format: "json",
filters: { receipt_type: "payment_receipt" },
});
// Poll for completion
const completed = await client.receipts.getExport(exportJob.id);
// Redact PII from a receipt (permanent, for GDPR erasure requests)
await client.receipts.redact(receipt.receipt_id);
See the receipts API reference for the full request and response schemas.