SDK npm package is not published yet and API environments may be unavailable.View status
TypeScript SDK

SDK — Ananke TCode usage

Stamp PDFs, manage TDoc and placement templates, verify DataMatrix payloads, and run bulk stamping jobs with the @ananke/sdk.

TDoc templates

TDoc templates define the DataMatrix payload schema — which fields are encoded inside the barcode. Access them via client.tcode.templates.

Manage TDoc templates
// List all TDoc templates (returns flat array, not paged)
const templates = await client.tcode.templates.list();
console.log(templates[0].name);

// Get a single template
const tmpl = await client.tcode.templates.get("tdoc-uuid");

// Create a new TDoc template
const newTmpl = await client.tcode.templates.create({
  name: "Invoice Stamp",
  fields: [
    { key: "documentId", label: "Document ID", required: true },
    { key: "issuedAt", label: "Issue Date", required: true },
    { key: "amount", label: "Amount", required: false },
  ],
});

// Get CSV field metadata for bulk stamping
const csvFields = await client.tcode.templates.getCsvFields("tdoc-uuid");
MethodDescription
list(opts?)All TDoc templates (flat array)
get(id)Single template by ID
create(body)Create template
update(id, body)Full update
delete(id)Permanently delete
archive(id, reason?)Soft-archive
unarchive(id)Restore archived
getCsvFields(id)CSV column metadata for bulk uploads

Placement templates

Placement templates control where the DataMatrix barcode is rendered on the PDF: position (x/y), size, page number, and optional label text. Access them via client.tcode.placement.

Manage placement templates
// List all placement templates
const placements = await client.tcode.placement.list();

// Create a placement template
const placement = await client.tcode.placement.create({
  name: "Bottom-right corner",
  pageNumber: 1,
  x: 450,
  y: 50,
  size: 80,
  labelText: "Scan to verify",
});

// Duplicate an existing placement
const copy = await client.tcode.placement.duplicate("placement-id", "Copy of bottom-right");
MethodDescription
list(opts?)All placement templates
get(id)Single placement template
create(body)Create placement template
update(id, body)Full update
delete(id)Delete
duplicate(id, name)Copy with a new name

Stamp a PDF

Use client.tcode.documents.stamp() to embed a DataMatrix barcode into a PDF. Each stamp gets a unique reference and is cryptographically anchored.

Stamp a PDF
import { readFileSync } from "node:fs";

const stamp = await client.tcode.documents.stamp({
  name:              "Invoice #1234",
  tdocTemplateId:    "tdoc-uuid",
  placementTemplateId: "placement-uuid",    // optional: uses default if omitted
  dataMatrixFields: {
    documentId: "INV-1234",
    issuedAt:   "2026-01-15",
    amount:     "1500.00",
  },
  pdf:         readFileSync("./invoice.pdf"),
  pdfFileName: "invoice-1234.pdf",
});

console.log(stamp.reference);  // "TCR-XXXX"
console.log(stamp.status);     // "Active"

Request fields

FieldTypeRequiredDescription
namestringYesHuman-readable document name
tdocTemplateIdstringYesTDoc template defining the payload schema
placementTemplateIdstringNoWhere to render the barcode on the PDF
dataMatrixFieldsobjectYesKey/value pairs matching the TDoc template fields
pdfPdfInputNoSource PDF (Buffer, base64, Blob, File)
pdfFileNamestringNoOriginal filename
recipientEmailstringNoOptional delivery email

List & manage stamps

List and download stamps
// Paged listing
const page = await client.tcode.documents.list({
  tdocTemplateId: "tdoc-uuid",
  status: "Active",
  page: 1,
  pageSize: 25,
});

// Auto-paginate all stamps
for await (const stamp of client.tcode.documents.listAll()) {
  console.log(stamp.reference);
}

// Get full details
const detail = await client.tcode.documents.get("TCR-XXXX");

// Download the stamped PDF
const blob = await client.tcode.documents.download("TCR-XXXX");

Lifecycle management

Stamp lifecycle
// Suspend a stamp (temporary)
await client.tcode.documents.suspend("TCR-XXXX");

// Revoke permanently
await client.tcode.documents.revoke("TCR-XXXX");

// Reactivate a suspended stamp
await client.tcode.documents.reactivate("TCR-XXXX");

// Replace with updated data / PDF
const replaced = await client.tcode.documents.replace("TCR-XXXX", {
  dataMatrixFields: { documentId: "INV-1234-v2", issuedAt: "2026-03-01" },
  pdf: readFileSync("./invoice-v2.pdf"),
});

// Duplicate check before stamping
const check = await client.tcode.documents.duplicateCheck({
  tdocTemplateId: "tdoc-uuid",
  dataMatrixFields: { documentId: "INV-1234" },
});
if (check.isDuplicate) {
  console.log("Already exists:", check.existingReference);
}

Verification

Verify Ananke TCode stamps by reference, hash, or raw DataMatrix payload. Query scan history and digital twin records.

Server-side verification
// Verify by stamp reference
const result = await client.tcode.verify.byReference("TCR-XXXX");
console.log(result.verdict);  // "valid" | "revoked" | "suspended" | "not_found"

// Verify a raw decoded DataMatrix payload
const rawPayload = "v1:TCR-XXXX:sig:...";  // from camera scan
const scanResult = await client.tcode.verify.verify(rawPayload);

// Lookup by hash
const lookup = await client.tcode.verify.lookupByHash("sha256:abcd...");

// Verification history
const history = await client.tcode.verify.history("TCR-XXXX");

// Scan events for a stamp
const scans = await client.tcode.verify.documentScans("TCR-XXXX");

// Digital twin record
const twin = await client.tcode.verify.digitalTwin("TCR-XXXX");

// Get the active signing public key
const key = await client.tcode.verify.getPublicKey();

Mobile offline sync

Mobile scanners can pre-fetch stamp data for offline verification using syncScans:

Offline sync
const sync = await client.tcode.verify.syncScans({
  since:       "2026-01-01T00:00:00Z",
  templateIds: ["tdoc-uuid"],
});
// sync.records — compact verification data for offline use
// sync.syncedAt — use as "since" for next incremental sync

Payload verifier (offline)

The payload verifier performs client-side ECDSA signature verification against raw DataMatrix payloads — no network roundtrip after the initial key cache fetch. Ideal for high-throughput scan pipelines (warehouse scanners, event gate readers).

Client-side payload verification
// Via the client instance (shares key cache)
const verifier = client.tcode.payloadVerifier;

// Or standalone (useful in workers / edge functions)
import { TCodePayloadVerifier } from "@ananke/sdk";
const verifier = new TCodePayloadVerifier({
  baseUrl: "https://api.anankelabs.io",
  apiKey:  process.env.ANANKE_API_KEY!,
});

// Verify a raw DataMatrix payload
const result = await verifier.verify("v1:TCR-XXXX:sig:...");
console.log(result.verdict);    // "valid" | "invalid_signature" | "revoked" | "suspended" | "not_found" | "error"
console.log(result.reference);  // "TCR-XXXX"
console.log(result.fields);     // decoded payload fields

// Parse without verifying (useful for UI display before verification completes)
const parsed = verifier.parse("v1:TCR-XXXX:sig:...");
console.log(parsed.reference, parsed.fields);

// Force key cache refresh (e.g. after key rotation)
await verifier.refreshCache();

Verdicts

VerdictMeaning
validSignature verified, stamp is active
invalid_signatureECDSA check failed — payload may be forged or corrupted
revokedSignature valid but stamp has been permanently revoked
suspendedSignature valid but stamp is temporarily suspended
not_foundReference not found in this tenant
errorVerification could not be completed (network or key fetch failure)

Bulk jobs

Bulk stamping lets you stamp hundreds of PDFs from a CSV plus an optional ZIP of source documents.

Bulk stamping workflow
import { readFileSync } from "node:fs";

// 1. Get CSV field schema
const fields = await client.tcode.templates.getCsvFields("tdoc-uuid");

// 2. Submit the bulk job
const csv = readFileSync("./batch.csv");
const zip = readFileSync("./pdfs.zip");

const job = await client.tcode.bulkJobs.create({
  tdocTemplateId:      "tdoc-uuid",
  placementTemplateId: "placement-uuid",
  csvFileBase64: csv.toString("base64"),
  zipFileBase64: zip.toString("base64"),
});
console.log(job.id, job.status);  // "Queued"

// 3. Poll for completion
let progress;
do {
  progress = await client.tcode.bulkJobs.getProgress(job.id);
  console.log(`${progress.status}: ${progress.successCount}/${progress.totalCount}`);
  if (progress.status !== "Completed" && progress.status !== "Failed") {
    await new Promise(r => setTimeout(r, 2000));
  }
} while (progress.status !== "Completed" && progress.status !== "Failed");

// 4. Get full details (includes all rows in response body)
const detail = await client.tcode.bulkJobs.get(job.id);
const failed = detail.rows.filter(r => r.status === "Failed");

// 5. Retry failed rows
if (failed.length) await client.tcode.bulkJobs.retry(job.id);
MethodDescription
create(body)Submit a new bulk stamping job
list(opts?)Paged list of jobs
listAll(opts?)Async generator — all jobs
get(jobId)Job detail including all rows
getProgress(jobId)Lightweight status poll
retry(jobId)Retry failed rows
cancel(jobId)Cancel queued/running job

Note: tcode.bulkJobs.get() returns all rows embedded in the response. For large jobs this can be a sizable payload. Use getProgress() for lightweight status polling.