Sealing from your systems
A REST API for companies that label content at scale — newsrooms, agencies, e-shops, digital archives. Our own website uses the same interface; there is no special "enterprise" version with different rules.
Principles
- You never send us your files. You send only the
sha256fingerprint (and, for images, optionally thedhashappearance fingerprint). The content stays with you. - The issuer is a verified domain. Register your company once on the Ankorith page (a DNS TXT record) and you receive a main key.
- Every seal goes into the public log (RFC 6962, ML-DSA-65 signature), which is anchored into Bitcoin regularly. Verification is free for anyone.
- Authorization: header
Authorization: Bearer ak_…. Base URL:https://ankorith.com/seal/api/
Overview
| Path | What it does |
|---|---|
| POST bulk | Seals up to 100 files at once (fingerprints only; files are not changed). |
| POST reserve POST issue | Two-step sealing when you want the label written into the file: the reservation returns an id and the swirl content → you embed XMP / the swirl → you send the fingerprint of the resulting file. |
| GET verify?sha256=&dhash= GET verify?id= | Public verification: exact match, appearance match (after social-media recompression), or nothing. |
| GET/POST keys DELETE keys/{id} | Sub-keys for departments and systems. Only the main key can issue and revoke them. |
| GET usage | Number of seals per month and per key — the basis for invoicing. |
| POST webhook | A notification of every issued seal to your URL, signed with HMAC-SHA256. |
| …/test-receiver/{token} | A test webhook receiver — try the integration without your own server. |
Origin declarations
The declaration field — it also sets the IPTC Digital Source Type value read by the major platforms:
| declaration | Meaning | IPTC |
|---|---|---|
| photo | photograph taken with a camera | digitalCapture |
| human | made by a human, without generative AI | humanEdits |
| ai-edited | real content edited with AI | compositeWithTrainedAlgorithmicMedia |
| ai-generated | generated by artificial intelligence | trainedAlgorithmicMedia |
Bulk sealing
curl -X POST https://ankorith.com/seal/api/bulk \
-H "Authorization: Bearer $ANKORITH_KEY" -H "Content-Type: application/json" \
-d '{"items":[
{"sha256":"8e4adba0…", "declaration":"ai-generated", "tool":"Midjourney", "name":"Banner 01"},
{"sha256":"1f09c2d7…", "dhash":"fba9a999fffd7cfc", "declaration":"photo", "name":"Product 4711"}
]}'
Response: {"ok":true,"vydano":2,"results":[{"id":"…","zaznam":581,"verifyUrl":"https://…/pecet/?id=…"},…]}. A failing item does not stop the others — it gets chyba with a readable reason.
Python
import hashlib, pathlib, requests
KEY = "ak_…"
files = list(pathlib.Path("output").glob("*.jpg"))
items = [{"sha256": hashlib.sha256(f.read_bytes()).hexdigest(),
"declaration": "ai-generated", "name": f.name} for f in files]
r = requests.post("https://ankorith.com/seal/api/bulk",
headers={"Authorization": "Bearer " + KEY}, json={"items": items})
for f, v in zip(files, r.json()["results"]):
print(f.name, v.get("verifyUrl") or v.get("error"))
Label inside the file
When the file should carry a machine-readable label (and optionally a visible swirl), proceed in two steps:
1) POST reserve → {"id", "content" (vír, 36 B hex), "verifyUrl"}
2) embed XMP in the file: Iptc4xmpExt:DigitalSourceType = http://cv.iptc.org/newscodes/digitalsourcetype/…
ankorith:Seal = id, ankorith:Verify = verifyUrl
(optionally draw the swirl from "content" into the image corner)
3) POST issue {"id", "sha256" of the RESULTING file, "dhash", "grid", "declaration"}
grid = 48×48 tile fingerprint (Ankorith grid (mrizka2ZObrazu)) — lets us show WHERE an image was changed
A ready JavaScript implementation (writing XMP into JPEG/PNG, corner swirl, appearance fingerprint) is in pecet.js — it runs in the browser and in Node.js.
Sub-keys and usage
# new key for a department (main key only)
curl -X POST …/seal/api/keys -H "Authorization: Bearer $PRIMARY_KEY" -d '{"name":"marketing"}'
# list and revoke
curl …/seal/api/keys -H "Authorization: Bearer $PRIMARY_KEY"
curl -X DELETE …/seal/api/keys/k_3f9a0c12bb41 -H "Authorization: Bearer $PRIMARY_KEY"
# usage per month and key
curl …/seal/api/usage -H "Authorization: Bearer $KEY"
→ {"months":{"2026-10":{"total":1240,"keys":{"primary":40,"k_3f9a0c12bb41":1200}}}}
Notifications (webhook)
curl -X POST …/seal/api/webhook -H "Authorization: Bearer $PRIMARY_KEY" \
-d '{"url":"https://yourcompany.com/ankorith/webhook"}'
→ {"secret":"…"} (shown only once)
Every notification carries the header x-ankorith-signature: sha256=… = HMAC-SHA256 of the body with the key secret. Verifying in Python:
import hmac, hashlib
ok = hmac.compare_digest("sha256=" + hmac.new(bytes.fromhex(SECRET), body, hashlib.sha256).hexdigest(),
request.headers["x-ankorith-signature"])
Without your own server: set the URL https://ankorith.com/seal/api/test-receiver/<32 hex characters> and read the received notifications with GET at the same address.
Errors
400 bad data (reason in chyba) · 401 invalid or revoked key · 403 main-key-only action · 404 unknown seal/reservation · 409 seal already issued · 413 more than 100 items at once.