Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.truthlocks.com/llms.txt

Use this file to discover all available pages before exploring further.

Production-ready code patterns for integrating Truthlocks into your applications. Examples are shown in TypeScript, Go, and Python.

JavaScript SDK

JavaScript SDK →

Go SDK

Go SDK →

Python SDK

Python SDK →

End-to-End: Issue and Verify a Credential

Complete workflow from creating an issuer to minting and verifying an attestation.
import { TruthlockClient, Algorithm, Verdict } from '@truthlock/sdk';

const client = new TruthlockClient({
baseUrl: 'https://api.truthlocks.com',
auth: { type: 'apiKey', apiKey: 'tl*live*...', tenantId: 'your-tenant-id' },
});

// Step 1: Create issuer + register key (one-time setup)
const issuer = await client.issuers.create({
name: 'Acme University',
legal_name: 'Acme University Inc.',
display_name: 'Acme U',
});
await client.issuers.trust(issuer.id);

await client.keys.register(issuer.id, {
kid: 'ed-key-2026',
alg: Algorithm.Ed25519,
public_key_b64url: 'MCowBQYDK2VwAyEA...', // Your Ed25519 public key
});

// Step 2: Mint a degree credential (sends email to recipient)
const attestation = await client.attestations.mint({
issuer_id: issuer.id,
kid: 'ed-key-2026',
alg: Algorithm.Ed25519,
schema: 'degree',
claims: {
student_name: 'Jane Doe',
institution: 'Acme University',
degree_type: 'Bachelor of Science',
field_of_study: 'Computer Science',
graduation_date: '2026-05-15',
honors: 'Magna Cum Laude',
},
recipient_email: 'jane.doe@example.com',
});

console.log('Minted:', attestation.id, 'Log:', attestation.log_index);

// Step 3: Verify the attestation
const result = await client.verify.verifyOnline({
attestation_id: attestation.id,
});

switch (result.verdict) {
case Verdict.Valid:
console.log('Valid! Signed by:', result.issuer_name);
break;
case Verdict.Revoked:
console.log('Revoked at:', result.revoked_at);
break;
case Verdict.Invalid:
console.log('Signature invalid or tampered');
break;
}

Revoke and supersede an attestation

Revoke a credential when it’s no longer valid, or supersede it with an updated version to preserve the audit trail.
import { TruthlockClient, Algorithm, Verdict } from '@truthlock/sdk';

const client = new TruthlockClient({
  baseUrl: 'https://api.truthlocks.com',
  auth: { type: 'apiKey', apiKey: 'tl_live_...', tenantId: 'your-tenant-id' },
});

const attestationId = '660e8400-e29b-41d4-a716-446655440001';

// --- Revoke: permanently invalidate a credential ---
const revoked = await client.attestations.revoke(attestationId, {
  reason: 'Certificate holder no longer employed',
});
console.log('Status:', revoked.status);      // "REVOKED"
console.log('Revoked at:', revoked.revoked_at);

// Verify returns REVOKED after revocation
const check = await client.verify.verifyOnline({ attestation_id: attestationId });
console.log('Verdict:', check.verdict);      // "REVOKED"

// --- Supersede: replace with an updated version ---
const otherAttestationId = '770e8400-e29b-41d4-a716-446655440002';

const updatedPayload = Buffer.from(JSON.stringify({
  student_name: 'Jane Doe',
  degree_type: 'Master of Science', // Updated from Bachelor
  graduation_date: '2026-05-15',
})).toString('base64url');

const result = await client.attestations.supersede(otherAttestationId, {
  new_payload_b64url: updatedPayload,
});

console.log('Old status:', result.old.status);           // "SUPERSEDED"
console.log('New attestation:', result.new.attestation_id);
console.log('New status:', result.new.status);           // "VALID"
Revocation is permanent and cannot be undone. If you need to issue an updated credential, use supersede instead — it preserves the original in the transparency log while linking to the replacement.

Batch minting with rate limiting

Mint credentials for multiple recipients in a single workflow with configurable concurrency and per-recipient success/failure reporting. Each recipient is processed independently — a failure for one does not block the others.
import { TruthlockClient, Algorithm, TruthlockError } from "@truthlock/sdk";

interface Recipient {
  name: string;
  email: string;
  department: string;
}

async function batchMint(
  client: TruthlockClient,
  issuerId: string,
  recipients: Recipient[],
  concurrency = 10,
) {
  const results: { email: string; id?: string; error?: string }[] = [];

  for (let i = 0; i < recipients.length; i += concurrency) {
    const batch = recipients.slice(i, i + concurrency);

    const settled = await Promise.allSettled(
      batch.map((r) =>
        client.attestations.mint({
          issuer_id: issuerId,
          kid: "ed-key-2026",
          alg: Algorithm.Ed25519,
          schema: "employment-verification",
          claims: {
            employee_name: r.name,
            employer: "Acme Corp",
            department: r.department,
            employment_type: "Full-time",
            start_date: new Date().toISOString().split("T")[0],
          },
          recipient_email: r.email,
        }),
      ),
    );

    for (let j = 0; j < settled.length; j++) {
      const s = settled[j];
      results.push({
        email: batch[j].email,
        id: s.status === "fulfilled" ? s.value.id : undefined,
        error: s.status === "rejected" ? s.reason?.message : undefined,
      });
    }
  }

  const ok = results.filter((r) => r.id).length;
  console.log(`Minted ${ok}/${recipients.length} attestations`);
  return results;
}
Set concurrency to match your plan’s rate limit. Start with 10 for Starter plans and increase to 50 or higher on Business and Enterprise tiers. Each failed recipient includes a descriptive error so you can retry selectively.

Query audit logs

Retrieve and filter audit events for security monitoring and compliance reporting.
import { TruthlockClient } from "@truthlock/sdk";

const client = new TruthlockClient({
  baseUrl: "https://api.truthlocks.com",
  auth: { type: "apiKey", apiKey: "tl_live_...", tenantId: "your-tenant-id" },
});

// Find all attestation operations for the past month
const events = await client.audit.query({
  resource_type: "attestation",
  from: "2026-01-01T00:00:00Z",
  to: "2026-01-31T23:59:59Z",
  limit: 200,
});

for (const event of events) {
  console.log(
    `[${event.timestamp}] ${event.action} on ${event.resource_id} by ${event.actor_id}`,
  );
}

// Export a compliance report as CSV
const exportJob = await client.audit.export({
  start_date: "2026-01-01",
  end_date: "2026-01-31",
  format: "csv",
});
console.log(`Export job: ${exportJob.id} (${exportJob.status})`);
Use resource_type and action filters to scope queries. For long-term archival, schedule periodic exports and store them in your own infrastructure. See audit logs for retention tiers and SIEM integration options.

Governance workflow

Create a multi-party approval workflow to manage issuer lifecycle changes such as suspend, revoke, and reinstate.
import { TruthlockClient } from "@truthlock/sdk";

const client = new TruthlockClient({
  baseUrl: "https://api.truthlocks.com",
  auth: { type: "apiKey", apiKey: "tl_live_...", tenantId: "your-tenant-id" },
});

// Step 1: Create a request to suspend an issuer
const req = await client.governance.createRequest({
  action: "suspend",
  issuer_id: "issuer-uuid",
  reason: "Annual compliance review",
});
console.log(`Request ${req.id}: ${req.status}`);

// Step 2: Approve (requires governance:approve permission)
await client.governance.approveRequest(req.id);

// Step 3: Execute the approved request
const result = await client.governance.executeRequest(req.id);
console.log(`Issuer is now: ${result.issuer_status}`);

// List all pending requests
const pending = await client.governance.listRequests();
for (const r of pending) {
  console.log(`[${r.status}] ${r.action} on ${r.issuer_id}`);
}
Governance requests require different permissions at each step. Creating requests needs governance:create, approving needs governance:approve, and executing needs governance:execute. See RBAC for role configuration.

Webhook Signature Verification

Securely verify incoming webhooks from Truthlocks to ensure they haven’t been tampered with.
webhook-handler.ts
import { verifyWebhookSignature } from "@truthlock/sdk";
import express from "express";

const app = express();
app.use(express.raw({ type: "application/json" }));

app.post("/webhooks/truthlock", (req, res) => {
  const signature = req.headers["x-truthlocks-signature"] as string;
  const isValid = verifyWebhookSignature(
    req.body,
    signature,
    process.env.WEBHOOK_SECRET!,
  );

  if (!isValid) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  const event = JSON.parse(req.body.toString());

  switch (event.type) {
    case "attestation.created":
      console.log("New attestation:", event.data.id);
      // Update your database, notify users, etc.
      break;

    case "attestation.revoked":
      console.log("Revoked:", event.data.id, "Reason:", event.data.reason);
      // Invalidate cached verification results
      break;

    case "issuer.created":
      console.log("Issuer created:", event.data.issuer_id);
      break;
  }

  res.json({ received: true });
});

Caching Verification Results

Cache verification results to reduce API calls. Only cache VALID results — revocations should always be checked fresh.
cached-verify.ts
import { TruthlockClient, Verdict } from "@truthlock/sdk";

// Simple in-memory cache with TTL
const cache = new Map<string, { result: any; expires: number }>();
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes

async function verifyWithCache(client: TruthlockClient, attestationId: string) {
  // Check cache first
  const cached = cache.get(attestationId);
  if (cached && cached.expires > Date.now()) {
    return cached.result;
  }

  // Fetch fresh result
  const result = await client.verify.verifyOnline({
    attestation_id: attestationId,
  });

  // Only cache VALID results (revocations must be checked each time)
  if (result.verdict === Verdict.Valid) {
    cache.set(attestationId, {
      result,
      expires: Date.now() + CACHE_TTL,
    });
  }

  return result;
}

Document Attestation with SHA-256

Attest a PDF document by computing its SHA-256 hash and including it in the claims for integrity verification.
import { createHash } from 'crypto';
import { readFile } from 'fs/promises';
import { TruthlockClient, Algorithm } from '@truthlock/sdk';

async function attestDocument(
client: TruthlockClient,
issuerId: string,
filePath: string,
) {
const fileBuffer = await readFile(filePath);
const sha256 = createHash('sha256').update(fileBuffer).digest('hex');
const fileName = filePath.split('/').pop() || 'document';

const attestation = await client.attestations.mint({
issuer_id: issuerId,
kid: 'ed-key-2026',
alg: Algorithm.Ed25519,
schema: 'custom',
content_type: 'application/pdf',
claims: {
document: {
sha256,
name: fileName,
size: fileBuffer.length,
content_type: 'application/pdf',
},
subject: 'Employment Contract',
signer: 'Jane Doe',
signed_date: new Date().toISOString().split('T')[0],
},
});

console.log('Document attested:', attestation.id);
console.log('SHA-256:', sha256);
return attestation;
}

Next Steps

Mint API reference

Full API docs with 35 credential schemas and interactive playground.

JavaScript SDK

Installation, authentication, and full method reference.

Go SDK

Idiomatic Go with context propagation and typed structs.

Python SDK

Python SDK with async support and type hints.

Rate limits

Understand quotas and retry strategies for production.

Revoke API reference

Permanently invalidate an attestation.

Supersede API reference

Replace an attestation with an updated version.