API

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

Overview

PathWhat it does
POST bulkSeals 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 usageNumber of seals per month and per key — the basis for invoicing.
POST webhookA 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:

declarationMeaningIPTC
photophotograph taken with a cameradigitalCapture
humanmade by a human, without generative AIhumanEdits
ai-editedreal content edited with AIcompositeWithTrainedAlgorithmicMedia
ai-generatedgenerated by artificial intelligencetrainedAlgorithmicMedia

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.