HashAnchor Guide

Quick Start

A key, a hash, and about twenty minutes — most of it waiting for the first batch to close. You can follow along with the SDK or with plain curl.

Before you start#

You need Node.js 18+ for the SDK, or just curl. The SDK is not on npm: install a pinned release from Git.

bash
npm install github:TLAY-IO/hashanchor#v1.0.1
1

Get a free API key

The key is self-serve; there is no form to fill in.

provision.mjs
import { HashAnchor } from "@tlay/hashanchor-client";

const { apiKey, plan, quota } = await HashAnchor.provision("my-app");
or with curl
curl -X POST https://hashanchor.xid.network/auth/provision \
  -H "Content-Type: application/json" \
  -d '{"name":"my-app"}'
response
{ "tenantId": "…", "apiKey": "ha_…", "plan": "free",
  "quota": { "limit": 100, "remaining": 100 } }
Careful

Keep it in an environment variable. Do not commit it, and do not flash it into firmware.

2

Submit a record

Hash the record yourself and only 32 bytes leave your system — or hand over the content and let the service hash it.

anchor.mjs
const client = new HashAnchor({ apiKey: process.env.HASHANCHOR_API_KEY });

// a hash you computed yourself …
const { hash } = await client.submitHash("0x" + "ab".repeat(32));

// … or content for the service to hash
await client.anchor("my first anchored record");
response
{ "status": "accepted", "id": "…", "hash": "0x…" }

Keep the hash — it is how you look the record up. accepted (or duplicate) means queued, not anchored.

3

Wait for the batch

Your hash joins the next Merkle batch, the batch's root is anchored in one transaction, and only then does a receipt exist. On the managed deployment a batch closes every 15 minutes.

0 min submit "accepted" pending within the window in a batch batched ≈ 15–20 min root anchored anchored receipt ready verify anytime GET /v1/receipts/:hash → 404 until anchored — that is correct
A first record typically reaches anchored in 15–20 minutes. Until then the receipt endpoint answers 404 — the correct answer to "is it anchored yet?" asked too early.
wait.mjs
async function waitUntilAnchored(client, hash) {
  const deadline = Date.now() + 30 * 60_000;
  let delay = 5_000;
  while (Date.now() < deadline) {
    const { status } = await client.getStatus(hash);
    if (status === "anchored") return client.getReceipt(hash);
    if (status === "failed") throw new Error("anchoring failed");
    await new Promise((r) => setTimeout(r, delay));
    delay = Math.min(delay * 2, 60_000);
  }
  throw new Error("timed out");
}

For a long-running application, record the hash and sweep later with batchStatus(hashes) instead of holding a request open.

4

Get the receipt and check it offline

verify.mjs
import { verifyReceiptProof } from "@tlay/hashanchor-client";

const receipt = await client.getReceipt(hash);
verifyReceiptProof(receipt);   // true — no network involved
receipt
{ "@context": "https://hashanchor.io/receipt/v1",
  "hash": "0x…", "merkleRoot": "0x…",
  "proof": [ { "position": "right", "data": "0x…" }, … ],
  "anchor": { "chainId": 137, "contractAddress": "0x…",
              "txHash": "0x…", "blockNumber": …, "blockTimestamp": … } }

The proof walks from your hash, pair by pair, to merkleRoot. You can also try this on the HashAnchor page, which runs the same check in your browser.

5

Check the anchor yourself

The offline check cannot see the chain. Ask the service — client.verify(hash), no key needed — or, for assurance that trusts no one, open anchor.txHash on the explorer for anchor.chainId and confirm the transaction committed the receipt's merkleRoot.

SDK · OFFLINE Check the proof verifyReceiptProof(r) Walks the path to the root on your machine. Cannot see the chain. trusts: nobody · proves: inclusion HOSTED · ONE CALL Ask the service client.verify(hash) Checks the proof and the anchor transaction. No API key needed. trusts: the HashAnchor service ON-CHAIN · YOURSELF Read the anchor anchor.txHash → explorer Compare the root committed on chain with the receipt's root. trusts: the public chain only
Three checks, from most convenient to most independent.

Ask getChains() for the chains the deployment anchors to today rather than hardcoding them.

Tip

Your record now has a receipt that anyone can verify without your database, and without trusting TLAY. Store it with whoever may need to check it later.

Next#