alltio.
← Part of How it works
For AI labs · Live

Registry Lookup API

Check any document against the registry by fingerprint, by text, by page address, or in batches. Free, with no key and no account, and every answer is signed.

Live. Every example on this page is a response from production, captured on 13 October 2026 against the record for “The Death of Provenance”, and every signature in it verifies against the published key. Questions and corrections: contact@alltio.com.

Send a fingerprint, the text of a work, or the address of a page, and the registry answers whether that work is on the record, who claims it, and on what terms. The lookup is free, needs no key and no account, and every answer is signed so it can be kept as evidence of the check.

1. What a lookup does and does not tell you

  1. It answers for one exact work. A lookup resolves a fingerprint to a record. Text and page lookups compute the fingerprint for you under the published reading rule (§8), so the same article on two platforms gives one answer.
  2. Nothing you send is kept. Fingerprints, text, and addresses sent to a lookup are not stored, published, or added to the register. The service logs aggregate counts only: how many lookups of each kind, and how many were registered.
  3. Not registered is not permission. Every not-registered answer says so in its notice field: absence from the registry says nothing about a work's copyright status.
  4. A record is a claim. Each answer carries the owner's verification tier, and licensing_offered says whether the registry offers the work for licensing at all. Terms are shown as the owner stated them.

2. Endpoints

All routes are served at https://alltio.com/v1/.

RouteWhat it takesWhat it returns
GET /v1/lookup/{fingerprint}One SHA-256 fingerprint, 64 hex charactersOne signed entry
POST /v1/lookup/text{"text": "..."}, at most 800,000 charactersOne signed entry, with the profile used
POST /v1/lookup/url{"url": "..."} of an openly readable pageOne signed entry, or 422 if the page could not be read
POST /v1/lookup/batch{"fingerprints": ["...", "..."]}A signed envelope of entries with a Merkle root
GET /v1/registry/keysNothingThe public keys that verify every signature

3. Access and limits

  • No key, no account. Send the request. There is no Authorization header to set.
  • From a browser or a server. Every route answers any origin with Access-Control-Allow-Origin: *, including the preflight a browser sends before a POST, so a page on any site can call the single, text, page, and batch lookups, and so can a script on a server. The routes take no cookies and no credentials.
  • Rate limits. About 120 requests per minute per address, and 10 per minute per address for the page lookup, which fetches a page on each call. The counters are kept per server instance, so the effective ceiling can be higher. Stay under these figures. Each response carries X-RateLimit-Limit and X-RateLimit-Remaining. Over the limit, the answer is 429 with a Retry-After header and retryAfterSeconds in the body.
  • Batch size. A batch call accepts up to 10,000 fingerprints, and it has 60 seconds to answer. Today a batch of 10,000 answers in about 12 seconds and a batch of 1,000 in about 3. Send batches of up to 10,000.
  • Not cached. An answer is evidence at the moment in its checked_at field, so lookups are never served from a cache.
  • Errors. A malformed fingerprint returns 400. A route that does not exist returns 404 with the list of routes.

4. One fingerprint

curl https://alltio.com/v1/lookup/b6af830f7b277fa6f02c1c4e1881e485079c1aad6ad705cf4d6f9a75b9474879
{
  "fingerprint": "b6af830f7b277fa6f02c1c4e1881e485079c1aad6ad705cf4d6f9a75b9474879",
  "registered": true,
  "registered_at": "2026-09-24T04:26:22.392Z",
  "owner": {
    "id": "alltio:owner:e198b6",
    "name": "Carl Gunell",
    "verification_tier": 1,
    "verification": "account"
  },
  "licensing_offered": false,
  "licensing_note": "Registered and visible. Not offered for licensing: licensing requires at least a domain-verified owner.",
  "terms": {
    "training": "licensable",
    "retrieval": "licensable",
    "inference": "licensable",
    "pricing_model": "flat",
    "terms_version": 1,
    "effective_from": "2026-09-20T19:56:34.045Z",
    "terms_url": "https://alltio.com/r/wk_efafb0ce7d10/terms?v=1"
  },
  "record_url": "https://alltio.com/r/bf6f1c943aadb3a7ef717517d705d405e5f1152f6bbea0774d2c6feb22217d50",
  "registry": {
    "checked_at": "2026-10-13T18:48:55.236Z",
    "signature": "eyJhbGciOiJFUzI1NiIsImtpZCI6ImFsbHRpby1yZWctMjAyNi0yIiwiYjY0IjpmYWxzZSwiY3JpdCI6WyJiNjQiXX0..4TLJrMI50jpvkO3S0xBIlAdca-wYtK55XBOmlUYrugaeNOHAA-Q-cAs6IgHUv7yfHbJJw2EhI_dH9rtaHuro9Q",
    "key_id": "alltio-reg-2026-2"
  }
}

A fingerprint that is not 64 hex characters is refused, and nothing is looked up:

curl https://alltio.com/v1/lookup/not-a-hash
{
  "ok": false,
  "error": "fingerprint must be 64 hex characters (SHA-256)."
}

5. The text of a work

Send the body of the work as plain text. The registry computes its canonical fingerprint, looks it up, and discards the text.

curl -X POST https://alltio.com/v1/lookup/text \
  -H 'Content-Type: application/json' \
  -d '{"text": "A short paragraph that nobody has put on the record."}'
{
  "fingerprint": "ab249568792963096ae71ffde37dabb917d2dc10d9573cfeb3d90b015e852e78",
  "registered": false,
  "notice": "Not registered with Alltio. This says nothing about the work's copyright status; not registered does not mean free to use.",
  "canonical": {
    "profile": "alltio-text-2",
    "chars": 52
  },
  "registry": {
    "checked_at": "2026-10-13T18:48:57.258Z",
    "signature": "eyJhbGciOiJFUzI1NiIsImtpZCI6ImFsbHRpby1yZWctMjAyNi0yIiwiYjY0IjpmYWxzZSwiY3JpdCI6WyJiNjQiXX0..rc6Mzcs2xYFaIUAjEXpd0olyFTA0TfVSF_h4InRN2WHDC34PHIo49PZ66ZwSgh7sQ3MdCz1Zzx026rxaVXlPnQ",
    "key_id": "alltio-reg-2026-2"
  }
}

The text is read under every published profile, newest first. canonical.profile names the profile of the record that matched, or, when nothing matched, the profile new registrations are filed under. canonical.chars is the length of the canonical text that was read.

6. A page address

The registry fetches the page, honours the site's robots.txt, reads the body of the work, and answers for its canonical text. It tries each candidate reading of the page under every published profile, starting with the body the page declares. canonical.source says which reading answered, and canonical.profile which profile.

curl -X POST https://alltio.com/v1/lookup/url \
  -H 'Content-Type: application/json' \
  -d '{"url": "https://alltio.com/blog/the-death-of-provenance"}'
{
  "fingerprint": "b6af830f7b277fa6f02c1c4e1881e485079c1aad6ad705cf4d6f9a75b9474879",
  "registered": true,
  "registered_at": "2026-09-24T04:26:22.392Z",
  "owner": {
    "id": "alltio:owner:e198b6",
    "name": "Carl Gunell",
    "verification_tier": 1,
    "verification": "account"
  },
  "licensing_offered": false,
  "licensing_note": "Registered and visible. Not offered for licensing: licensing requires at least a domain-verified owner.",
  "terms": {
    "training": "licensable",
    "retrieval": "licensable",
    "inference": "licensable",
    "pricing_model": "flat",
    "terms_version": 1,
    "effective_from": "2026-09-20T19:56:34.045Z",
    "terms_url": "https://alltio.com/r/wk_efafb0ce7d10/terms?v=1"
  },
  "record_url": "https://alltio.com/r/bf6f1c943aadb3a7ef717517d705d405e5f1152f6bbea0774d2c6feb22217d50",
  "url": "https://alltio.com/blog/the-death-of-provenance",
  "canonical": {
    "profile": "alltio-text-2",
    "chars": 9377,
    "source": "declared"
  },
  "registry": {
    "checked_at": "2026-10-13T18:48:55.535Z",
    "signature": "eyJhbGciOiJFUzI1NiIsImtpZCI6ImFsbHRpby1yZWctMjAyNi0yIiwiYjY0IjpmYWxzZSwiY3JpdCI6WyJiNjQiXX0..TnPxchXtluR6kwrj8WlTTVqshH1hEdSTKOG_14IVjaMz5nqp-Jk3wZCNg_nDxSjJ-7_WW9ajNtl9tpXMfSNZ6w",
    "key_id": "alltio-reg-2026-2"
  }
}

A page that cannot be read is never reported as not registered. If the page is blocked, behind a sign-in, missing, or too short to read, the answer is 422, because a body that could not be read looks the same as a genuine mismatch. Paste the text into the text lookup instead.

curl -X POST https://alltio.com/v1/lookup/url \
  -H 'Content-Type: application/json' \
  -d '{"url": "https://alltio.com/blog/no-such-page"}'
HTTP 422
{
  "ok": false,
  "readable": false,
  "error": "That page could not be read from here.  Some sites only show their pages to signed-in readers, and Alltio does not sign in.  Open the page yourself, copy the text of the work, and paste it instead — the answer is the same."
}

7. A batch of fingerprints

curl -X POST https://alltio.com/v1/lookup/batch \
  -H 'Content-Type: application/json' \
  -d '{"fingerprints": ["b6af830f7b277fa6f02c1c4e1881e485079c1aad6ad705cf4d6f9a75b9474879",
                       "0000000000000000000000000000000000000000000000000000000000000000"]}'
{
  "count": 2,
  "merkle_root": "95b8614e4ed142caaf94e469b24d1e426a7bd8ea67f6f04d25894cca4fe28c4e",
  "entries": [
    {
      "fingerprint": "b6af830f7b277fa6f02c1c4e1881e485079c1aad6ad705cf4d6f9a75b9474879",
      "registered": true,
      "registered_at": "2026-09-24T04:26:22.392Z",
      "owner": {
        "id": "alltio:owner:e198b6",
        "name": "Carl Gunell",
        "verification_tier": 1,
        "verification": "account"
      },
      "licensing_offered": false,
      "licensing_note": "Registered and visible. Not offered for licensing: licensing requires at least a domain-verified owner.",
      "terms": {
        "training": "licensable",
        "retrieval": "licensable",
        "inference": "licensable",
        "pricing_model": "flat",
        "terms_version": 1,
        "effective_from": "2026-09-20T19:56:34.045Z",
        "terms_url": "https://alltio.com/r/wk_efafb0ce7d10/terms?v=1"
      },
      "record_url": "https://alltio.com/r/bf6f1c943aadb3a7ef717517d705d405e5f1152f6bbea0774d2c6feb22217d50"
    },
    {
      "fingerprint": "0000000000000000000000000000000000000000000000000000000000000000",
      "registered": false,
      "notice": "Not registered with Alltio. This says nothing about the work's copyright status; not registered does not mean free to use."
    }
  ],
  "registry": {
    "checked_at": "2026-10-13T18:48:57.550Z",
    "signature": "eyJhbGciOiJFUzI1NiIsImtpZCI6ImFsbHRpby1yZWctMjAyNi0yIiwiYjY0IjpmYWxzZSwiY3JpdCI6WyJiNjQiXX0..2QI2_MkZQt0lWYg-UEmPYspOv2b3Kn2ubpFfrsGL1jFMh2k4A_g_SYAxFgF-lVfvoSlR-hXscSMS9trjLp0nzw",
    "key_id": "alltio-reg-2026-2"
  }
}

Entries come back in the order sent. A value that is not a 64-hex fingerprint comes back in its place as "registered": false, "error": "malformed", so the count always matches the request. The whole envelope is signed once, and merkle_root lets a single entry be kept as evidence without the rest (§9).

7.1 The fields of an entry

FieldMeaning
fingerprintThe SHA-256 that was looked up, in lower case.
registeredtrue if an active record holds this fingerprint.
registered_atWhen the current record was filed.
ownerThe owner's registry ID, name, verification tier, and the tier's label: 0 unclaimed, 1 account, 2 domain-verified, 3 identity-verified.
licensing_offeredWhether the registry offers the work for licensing. It requires at least a domain-verified owner.
licensing_noteWhy, in a sentence, when licensing is not offered.
termsThe owner's terms for training, retrieval, and inference, the pricing_model, the terms version, when it took effect, and a link to it.
record_urlThe public record (§10).
copyright_officeOnly when the owner has entered one: the work's U.S. Copyright Office registration number, as registration_number, with "stated_by": "owner". It is the owner's statement. Alltio does not file with the Copyright Office and does not check the number; anyone can look it up in the Copyright Office public records. When no number has been entered, the key is left out.
noticeOn a not-registered answer only: absence from the registry says nothing about copyright status.
registryWhen the check was made, the signature, and the ID of the key that made it.

8. How the fingerprint is computed

For written work, the fingerprint is computed from the text of the work, not from the file or the page around it. The published reading rule reduces the words to one canonical form, so the same article published on different platforms produces one fingerprint.

The rule, its test vectors, and the module that implements it are public, and the lookup uses the same code:

  • alltio-text-3, the profile new registrations are filed under, which leaves captions and alt text outside the work: rule, vectors, module.
  • alltio-text-2, the profile earlier registrations were filed under, which keeps captions inside the work: rule, vectors, module.

Every record names the profile it was filed under and verifies under that profile permanently. The text and page lookups compute the fingerprint under every published profile, newest first, so a record is found whichever profile it was filed under. For plain text the two profiles give the same fingerprint; they differ only where a page carries captions or alt text. A SHA-256 of a registered file's exact bytes resolves too.

9. Signatures

Every answer carries registry.signature: a detached JWS (RFC 7515) using ES256, with the unencoded-payload option (b64: false). The signed payload is the RFC 8785 canonical JSON of the response body with registry.signature removed. The kid in the signature header names a key at /v1/registry/keys. Verification needs only public standards and no Alltio code.

curl https://alltio.com/v1/registry/keys
{
  "keys": [
    {
      "kid": "alltio-reg-2026-2",
      "kty": "EC",
      "crv": "P-256",
      "x": "ajBclNM605_uo4w9ck_-g6Jn-t9s9RRbt0HTbKySep8",
      "y": "-1oegN15wl9G7rIguZI8kwuG9spEjw8Ck7hpbdi0hQA",
      "alg": "ES256",
      "use": "sig",
      "status": "active",
      "not_before": "2026-08-18T00:00:00Z",
      "not_after": null
    },
    {
      "kid": "alltio-reg-2026-1",
      "kty": "EC",
      "crv": "P-256",
      "x": "EEqWHfvjGD8TwuG9FIPJFji-KzGAmvdEQ7O3FSAvEVc",
      "y": "7nFpu-4AO0kHq9uC0WA0kYAFhZu02VhJOfvUoQcjNAc",
      "alg": "ES256",
      "use": "sig",
      "status": "retired",
      "not_before": "2026-08-04T00:00:00Z",
      "not_after": "2026-08-18T00:00:00Z",
      "note": "Never used in production; retired before first deploy."
    }
  ]
}

Verifying a saved answer in Node, with the jose and canonicalize packages:

import { flattenedVerify, importJWK } from "jose";
import canonicalize from "canonicalize";

const body = JSON.parse(savedResponse);
const [protectedHeader, , signature] = body.registry.signature.split(".");
const payload = structuredClone(body);
delete payload.registry.signature;

const { kid } = JSON.parse(Buffer.from(protectedHeader, "base64url"));
const { keys } = await (await fetch("https://alltio.com/v1/registry/keys")).json();
const key = await importJWK(keys.find((k) => k.kid === kid), "ES256");

await flattenedVerify({ protected: protectedHeader, payload: canonicalize(payload), signature }, key);
// throws if the answer was altered after Alltio signed it

The Merkle root. Each leaf is the SHA-256 of an entry's RFC 8785 canonical JSON, in the order of entries. Leaves are hashed in pairs, left then right, and an odd last node is paired with itself, up to one root. To keep one entry as evidence, keep the entry, the hashes on its path to the root, and the signed envelope. The caller computes the path from the envelope; the registry does not store it.

10. Records

  • Record ID. The SHA-256 of the record's own canonical payload, so a record cannot change. A revision is a new record.
  • Record page. https://alltio.com/r/{recordId}: the public page for the record, and the same record as JSON with Accept: application/json or at /r/{recordId}.json. It shows the owner, the work, its fingerprint and profile, the terms, the verification tier, the version chain, and whether Alltio's countersignature verifies.
  • Work ID. https://alltio.com/r/{workId}, where a work ID looks like wk_efafb0ce7d10, redirects to the work's current record. Cite the work ID for the work across its versions, and the record ID for one version at one moment.
  • Terms history. /r/{recordId}/terms lists every version of the terms behind a record.
  • Verify API. https://alltio.com/api/verify/{recordId} returns the record as JSON. Records filed before record pages existed carry this address, and it stays permanent.

10.1 Versions and supersession

A revision of a work is a new registration with a new fingerprint and a new record. The new record may name the record it replaces in supersedes and must then carry a version label. The registry checks that the earlier record exists and, when the same owner filed both, records a forward pointer on the earlier one (supersededBy), held outside its signed content because signed content never changes after issue.

A superseded record stays resolvable and is marked "superseded": true. Lookups answer with the newest record in the chain. A published work that no longer matches its record's fingerprint, with no new record filed, is a mismatch.

11. The page embed

When a work is registered, the registry generates a snippet for the page where it is published. Owners paste it once and replace it when they register a new version. It has two parts:

  • The mark: an image served from /r/{recordId}/mark.svg, linked to the record page. It carries data-alltio-mark, which keeps it out of the canonical text if it is placed inside the body of the work.
  • A JSON-LD block: schema.org CreativeWork carrying the record ID under propertyID: "alltio:recordId", the fingerprint under propertyID: "sha256", the record URL as subjectOf and license, and, when the record declares them, version, datePublished, and dcterms:replaces.

The embed is optional. A lookup finds a registered work with or without it, and a reader of the embed should check it against the registry rather than trust the page. The Death of Provenance carries one. The plain-language guide is at /embed.

12. Checking a corpus

A developer can check a whole corpus without sending any of it:

  1. Compute the fingerprint of each document locally with the published module. It is plain JavaScript and runs in Node as well as in a browser.
  2. Send the fingerprints to /v1/lookup/batch, up to 10,000 per call. There is no charge and no account.
  3. Keep each signed response. It is the record of what was checked, when, and what the registry answered.
import { digestText } from "./alltio-text-3.js";   // from https://alltio.com/assets/

const fingerprints = [];
for (const doc of corpus) fingerprints.push((await digestText(doc.text)).sha256);

for (let i = 0; i < fingerprints.length; i += 10000) {
  const res = await fetch("https://alltio.com/v1/lookup/batch", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ fingerprints: fingerprints.slice(i, i + 10000) }),
  });
  await save(await res.text());   // the signed envelope is the evidence of the check
}

Only fingerprints leave the caller. No document is sent to Alltio.

13. Not live yet

Earlier drafts of this page described the following. None of it runs today, and it is listed here so nobody builds against it.

  • Site declaration. A file at /.well-known/alltio.json listing a domain's registered works, generated and signed by the registry. The format is drafted, and the home-page check reads the file where a site publishes one, but the registry does not yet generate or sign it. Registry profile pages (/p/{name}) and registry-hosted asset indexes (assets_index) belong to the same design and do not exist yet.
  • Retained receipts and audit export. Alltio keeps no copy of a lookup answer. The signed response is the receipt, and the caller keeps it.
  • API keys and volume tiers. There are no keys. The lookup is anonymous.
  • Per-request context and correlation IDs. A batch takes fingerprints only, and entries come back in the order sent.
  • C2PA. Carrying the record URL as an assertion in a content-credentials manifest.
  • Other media. Fingerprints for photographs, video, and audio that survive re-encoding, returned with a confidence field.
  • Excerpts. Fingerprints of passages within long documents, for matching excerpts in retrieval.
  • Agent credentials. The signed answer extended to credentials an AI agent presents.

14. The flow

Owner registers  ──►  fingerprint + claim + terms on the record
        │
        └──► pastes the embed on the published page   (optional)

Developer has a document  ──►  canonical fingerprint  ──►  /v1/lookup
        │
        ├── registered      ──► owner, tier, terms, record URL, signed
        └── not registered  ──► signed, with the notice that this is not permission

Put your knowledge on the record.

Free. Read and fingerprinted in your browser, not stored.

Create your record