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

Build a verification page

Design and implement a frontend verification experience. Covers UX states, component mapping, accessibility, and visual language for verification results.

Use case

You are building a public-facing page where anyone can verify a document. They enter a reference code, scan a barcode, or upload a file — and your page shows the verification result.

This is one of the most important user-facing surfaces in any Ananke Labs integration. Getting the UX right builds trust in the verification process itself.

UX states

Your verification page must handle five distinct states:

StateTriggerVisual
IdlePage load, no input yetInput form with clear instructions
LoadingRequest in flightSpinner or skeleton, disable inputs
Verified (valid)verdict: "valid"Green success with document details
Warning (suspended/expired)verdict: "suspended" or "expired"Amber warning with explanation
Error (revoked/not found/network)verdict: "revoked", "not_found", or fetch errorRed error or neutral "not found"

Loading state

Loading state component
function VerificationLoading() {
  return (
    <div className="animate-pulse rounded-lg border p-6">
      <div className="h-6 w-48 rounded bg-neutral-200" />
      <div className="mt-4 space-y-2">
        <div className="h-4 w-full rounded bg-neutral-100" />
        <div className="h-4 w-3/4 rounded bg-neutral-100" />
      </div>
    </div>
  );
}

Never show a cached or stale result during loading. Always clear the previous result and show the loading state.

Verified state

When verdict is "valid", show:

  • A green success indicator (icon + background)
  • Issuer name and logo (if available)
  • Recipient name
  • Document title and type
  • Issue date
  • Document reference
Verified state
function VerifiedResult({ result }) {
  return (
    <div className="rounded-lg border-2 border-green-200 bg-green-50 p-6">
      <div className="flex items-center gap-3">
        <CheckCircleIcon className="h-8 w-8 text-green-600" />
        <div>
          <h2 className="text-lg font-semibold text-green-900">
            Document Verified
          </h2>
          <p className="text-sm text-green-700">
            This document is authentic and currently valid.
          </p>
        </div>
      </div>
      <dl className="mt-6 grid grid-cols-2 gap-4 text-sm">
        <div>
          <dt className="font-medium text-neutral-500">Issuer</dt>
          <dd className="text-neutral-900">{result.issuerName}</dd>
        </div>
        <div>
          <dt className="font-medium text-neutral-500">Recipient</dt>
          <dd className="text-neutral-900">{result.recipientName}</dd>
        </div>
        <div>
          <dt className="font-medium text-neutral-500">Document</dt>
          <dd className="text-neutral-900">{result.documentTitle}</dd>
        </div>
        <div>
          <dt className="font-medium text-neutral-500">Issued</dt>
          <dd className="text-neutral-900">
            {new Date(result.issuedAt).toLocaleDateString()}
          </dd>
        </div>
      </dl>
    </div>
  );
}

Error states

Different errors need different visual treatments:

  • Revoked — Red error state. Clearly state the document was permanently invalidated. Include revocation reason if available.
  • Suspended — Amber warning. Explain this is temporary and the document may be reinstated.
  • Not found — Neutral grey state. Do NOT say "invalid" — the reference may just be mistyped. Suggest checking the input.
  • Network error — Show a retry option. Never display "invalid" when the real issue is a failed network request.

Component mapping

Map API fields to UI components:

API fieldUI component
verdictStatus indicator (icon + colour + label)
issuerNameIssuer section with optional logo
recipientNameRecipient name display
documentTitleDocument title heading
documentTypeType badge or label
issuedAtFormatted date
referenceMonospace reference code

Accessibility

  • Use ARIA live regions for dynamic result updates
  • Don't rely on colour alone — include text labels and icons
  • Make the input form keyboard-navigable
  • Provide meaningful alt text for status icons
  • Ensure sufficient contrast ratios (4.5:1 minimum)
  • Announce results to screen readers when they appear

Ananke Trust verification page

Ananke Trust verification page skeleton
"use client";
import { useState } from "react";
import { AnankeClient } from "@ananke/sdk";

const client = new AnankeClient({ baseUrl: "/api/proxy" });

export default function TrustVerifyPage() {
  const [reference, setReference] = useState("");
  const [result, setResult] = useState(null);
  const [loading, setLoading] = useState(false);

  async function handleVerify(e: React.FormEvent) {
    e.preventDefault();
    setLoading(true);
    setResult(null);
    try {
      const res = await client.trust.verify.byReference(reference);
      setResult(res);
    } catch (err) {
      setResult({ verdict: "error", message: "Verification failed" });
    } finally {
      setLoading(false);
    }
  }

  return (
    <form onSubmit={handleVerify}>
      <input
        value={reference}
        onChange={(e) => setReference(e.target.value)}
        placeholder="Enter document reference (TRF-...)"
      />
      <button type="submit" disabled={loading}>
        {loading ? "Verifying..." : "Verify"}
      </button>
      {result && <VerificationResult result={result} />}
    </form>
  );
}

Ananke TCode scan page

For Ananke TCode, the verification page typically includes a barcode scannerusing the device camera. The scan decodes the DataMatrix payload and sends it to the verify endpoint.

Scan verification flow
// After the barcode scanner decodes the DataMatrix:
async function onBarcodeScan(payload: string) {
  const result = await client.tcode.verify.byPayload({ payload });
  // Display the result using the same component mapping
}

Next steps