Skip to main content
Prove document integrity & existence • Blockchain notarization API • eIDAS qualified timestamping

Documentation

Get started with the Anchorify API

REST API

The Anchorify API is a simple REST JSON API. Authenticate with an API key via the X-API-Key header.

Base URL

https://api.anchorify.cloud

OpenAPI Specification

openapi.json

Quickstart

The Anchorify API allows you to create tamper-proof evidence by anchoring hashes on public blockchains. Here's how to get started:

1

1. Get your API key

Sign up for an account and generate an API key from the console.

2

2. Hash your data locally

Generate a hash of your file or data. The original data never needs to leave your system.

# Generate SHA-256 hash (hex output)
openssl dgst -sha256 document.pdf
# SHA256(document.pdf)= e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

# Use the hex string directly in the API
3

3. Submit the hash

Send the hex-encoded hash to our REST API to create a notarization. You'll receive a notarization ID for tracking.

POST /v1/notarizations
curl -X POST https://api.anchorify.cloud/v1/notarizations \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "algorithm": "SHA256",
    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "name": "document.pdf"
  }'
Response (201)
{
  "id": "019c0e1a-d891-7f1f-ab6e-5588c46c2a4b",
  "algorithm": "SHA256",
  "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
  "status": "pending",
  "blockchain_proofs": [
    { "provider_name": "Algorand", "status": "pending", "created_at": "2026-01-30T08:52:46Z" },
    { "provider_name": "Base", "status": "pending", "created_at": "2026-01-30T08:52:46Z" },
    { "provider_name": "Polygon", "status": "pending", "created_at": "2026-01-30T08:52:46Z" }
  ],
  "created_at": "2026-01-30T08:52:46Z",
  "name": "document.pdf"
}
4

4. Poll for status

Check the proof status. Once anchored on the blockchain, you can export your signed proof document.

GET /v1/notarizations/{id}
curl https://api.anchorify.cloud/v1/notarizations/019c0e1a-d891-7f1f-ab6e-5588c46c2a4b \
  -H "X-API-Key: YOUR_API_KEY"
Response (200)
{
  "id": "019c0e1a-d891-7f1f-ab6e-5588c46c2a4b",
  "algorithm": "SHA256",
  "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
  "status": "completed",
  "timestamp_proof": {
    "status": "timestamped",
    "tsa_provider": { "name": "FreeTSA" },
    "data": "MIIB...",
    "validation": {
      "chain": "MIIDXj...",
      "ocsp": "MIIBpg...",
      "this_update": "2026-01-30T08:52:49Z",
      "captured_at": "2026-01-30T08:52:51Z"
    },
    "timestamped_at": "2026-01-30T08:52:48Z",
    "created_at": "2026-01-30T08:52:47Z"
  },
  "blockchain_proofs": [
    {
      "provider_name": "Polygon",
      "transaction_id": "0xabc123...",
      "explorer_url": "https://polygonscan.com/tx/0xabc123...",
      "status": "anchored",
      "anchored_at": "2026-01-30T08:53:32Z",
      "created_at": "2026-01-30T08:52:47Z"
    }
  ],
  "merkle_proof": {
    "hash_algorithm": "SHA256",
    "leaf_index": 2,
    "siblings": [{ "position": "left", "hash": "9f8e7d6c..." }]
  },
  "merkle_root": "a1b2c3d4...",
  "accumulator": {
    "merkle_root": "a1b2c3d4...",
    "sealed_at": "2026-01-30T08:52:47Z"
  },
  "created_at": "2026-01-30T08:52:46Z",
  "updated_at": "2026-01-30T08:53:32Z",
  "notarized_at": "2026-01-30T08:53:32Z",
  "name": "document.pdf"
}

API reference

POST /v1/notarizations

Create a notarization

Headers: X-API-Key (required), Idempotency-Key (optional, see idempotent retries)

Body: algorithm (required, SHA256 | SHA384 | SHA512), hash (required, hex string), name (optional), qualified_timestamp (optional)

GET /v1/notarizations/{id}

Get a notarization by ID

Path: id (required, UUID)

Quotas and spending limit

Every notarization counts in your organization's billing period, which follows the anniversary of your subscription. Each plan includes a quota of standard notarizations; Pro and Enterprise also include qualified (eIDAS) timestamps. A qualified notarization counts only in the qualified quota.

Beyond the included quota, each notarization is billed at your plan's overage price, and each qualified one pay as you go, within the spending limit your organization's owners and admins set on the console's billing page: one amount excluding VAT per billing period, from €0 to €1,000, for both. The limit is €0 by default: notarizations stop at the included quota until you set one.

A notarization the spending limit cannot cover is refused with 429 quota_exceeded, without Retry-After: the quota comes back with the next billing period, or as soon as the limit is raised.

Owners and admins receive an email at 80% and 100% of each included quota, then at 80% and 100% of the spending limit. A notarization that fails for good is not billed.

Qualified timestamps also depend on the stock we hold with the qualified timestamp authority. When it runs out, a qualified request is refused with 429 qualified_capacity_exhausted, while standard notarizations go on: retry the qualified one later.

Idempotent retries

A timeout or a network error leaves you unsure whether a notarization was created. Send an Idempotency-Key header with POST /v1/notarizations, and retry with the same key and the same body: the notarization is created, and billed, once.

POST /v1/notarizations
curl -X POST https://api.anchorify.cloud/v1/notarizations \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 5f0c2b8e-6d1a-4c3e-9b7a-2e4f8d1c6a90" \
  -d '{
    "algorithm": "SHA256",
    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "name": "document.pdf"
  }'

A retry within 24 hours of the first request creates nothing and is not counted again: it answers 201 with the notarization as it now stands, and the Idempotent-Replayed: true header.

Response to a retry (201)
HTTP/1.1 201 Created
Content-Type: application/json
Idempotent-Replayed: true

The key is yours to choose: up to 255 visible ASCII characters, without spaces; a UUID works. Keys are scoped to your organization and kept 24 hours; after that, the same key creates a new notarization. Use a new key for each notarization you want.

The same key with a different body answers 422 idempotency_key_reused, and creates nothing.

Errors

An error answers a JSON body with a machine-readable code and a human-readable message. Handle the code: the message may change.

Response (429)
{
  "error": {
    "code": "quota_exceeded",
    "message": "quota exceeded: the spending limit does not cover this notarization"
  }
}
Status Code Meaning
400 invalid_argument The request is invalid: its JSON, algorithm, hash or Idempotency-Key header. Fix it before retrying.
401 unauthenticated The API key is missing or invalid.
402 subscription_inactive Your organization has no subscription that grants notarization: none, pending, cancelled, unpaid, or past due for more than 7 days. Your data stays readable.
403 permission_denied Your plan does not include what the request uses: SHA-384, SHA-512 or qualified timestamps.
404 not_found GET /v1/notarizations/{id} only: the notarization does not exist, or belongs to another organization.
422 idempotency_key_reused The Idempotency-Key was already used within 24 hours for a different request. Nothing was created: send a new key.
429 resource_exhausted Your plan's rate limit is exceeded. Retry after the Retry-After seconds.
429 quota_exceeded Your included quota is used, and your spending limit does not cover the notarization. No Retry-After: raise the limit, or wait for the next billing period.
429 qualified_capacity_exhausted Qualified timestamps are unavailable for now. Standard notarizations still go through; retry the qualified one later.
500 internal Unexpected error. Retry with the same Idempotency-Key: the notarization is not created twice.

Webhooks

Pro

Webhooks let your application receive real-time HTTP POST notifications when notarization events occur. Configure endpoint URLs and subscribe to specific event types from the console. An endpoint URL must use HTTPS and point to a public address: loopback, private networks and cloud metadata services are refused.

Event types

Subscribe to the events relevant to your workflow. Each webhook delivery includes a full notarization snapshot with the current state of proofs.

Event type Description
notarization.created Notarization submitted and accepted.
notarization.sealed Accumulator sealed, Merkle proof assigned. Blockchain anchoring begins.
notarization.timestamped RFC 3161 timestamp obtained. The event_context includes timestamp_proof details.
notarization.anchor_submitted Blockchain transaction submitted for one provider. Fires per provider.
notarization.anchored Blockchain transaction confirmed on-chain for one provider.
notarization.completed Terminal success: both the RFC 3161 timestamp and the blockchain anchoring finished. The event_context includes completed_at.
notarization.failed Terminal failure: the timestamp or the blockchain anchoring could not complete. The event_context includes failure_reason.

Terminal event guarantees

notarization.completed and notarization.failed are terminal, mutually exclusive, and delivered exactly once per notarization. You will never receive both for the same notarization, in either order.

  • completed is only sent once both the timestamp and the blockchain anchoring have finished — never after just the first of the two.
  • A notarization anchored by at least one blockchain provider completes, even if other providers of the same anchor profile failed. Check anchors[] for the per-provider outcome.
  • If every provider fails permanently, the notarization is reported as failed — it has no blockchain proof.
  • Delivery order is not guaranteed. Events are emitted in lifecycle order, but deliveries are independent HTTP requests: order your processing on the envelope created_at, not on arrival order.

Payload structure

Every webhook delivery sends a JSON envelope with a full notarization snapshot. The data.notarization object is self-contained — you do not need to call GET /v1/notarizations/{id}.

POST (webhook delivery)
{
  "id": "019c0e1a-e7a2-7f3b-8c1d-4a9b2e6f8d3c",
  "type": "notarization.completed",
  "api_version": "2026-09-01",
  "created_at": "2026-01-30T08:53:32Z",
  "organization_id": "019b7a3c-5d2e-7f1a-9b4c-3e8f1a2d5c7b",
  "data": {
    "notarization": {
      "id": "019c0e1a-d891-7f1f-ab6e-5588c46c2a4b",
      "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
      "algorithm": "SHA256",
      "status": "completed",
      "name": "document.pdf",
      "created_at": "2026-01-30T08:52:46Z",
      "updated_at": "2026-01-30T08:53:32Z",
      "notarized_at": "2026-01-30T08:53:32Z",
      "merkle_root": "a1b2c3d4e5f6...",
      "timestamp_proof": {
        "status": "timestamped",
        "serial_number": "1A2B3C",
        "tsa_provider": {
          "name": "FreeTSA",
          "cert_subject": "CN=FreeTSA,O=Free TSA,C=DE",
          "cert_serial": "04B9A5",
          "cert_ski": "A1B2C3D4E5F6...",
          "policy_oid": "1.2.3.4.1"
        },
        "timestamped_at": "2026-01-30T08:52:48.123456Z",
        "created_at": "2026-01-30T08:52:47.987654Z"
      },
      "blockchain_proofs": [
        {
          "provider_name": "Polygon",
          "status": "anchored",
          "transaction_id": "0xabc123...",
          "explorer_url": "https://polygonscan.com/tx/0xabc123...",
          "submitted_at": "2026-01-30T08:52:50Z",
          "anchored_at": "2026-01-30T08:53:32Z",
          "created_at": "2026-01-30T08:52:47.987654Z"
        }
      ],
      "accumulator": {
        "merkle_root": "a1b2c3d4e5f6..."
      }
    },
    "event_context": {
      "completed_at": "2026-01-30T08:53:32Z"
    }
  }
}

Note: The timestamp_proof.data field (the RFC 3161 ASN.1 token, base64-encoded) and timestamp_proof.validation (the material needed to verify that token years from now) are only included if the endpoint opts in via the include_timestamp_token setting — they share one flag because the validation material describes the token and is meaningless without it. The merkle_proof is always delivered.

HTTP headers

Every webhook request includes the following headers:

Header Value
Content-Type application/json
User-Agent Anchorify-Webhooks/1.0
X-Anchorify-Signature t=<unix>,v1=<hex> (HMAC-SHA256)
X-Anchorify-Event-Type Event type string
X-Anchorify-Event-ID Event UUID
X-Anchorify-Delivery-ID Delivery attempt UUID

Signature verification

Every webhook is signed with HMAC-SHA256 using your endpoint secret. The signature header format is t=<timestamp>,v1=<hex_signature>, computed over <timestamp>.<raw_body>. We recommend verifying the signature and checking the timestamp freshness (5-minute tolerance).

import hmac
import hashlib
import time

def verify_webhook(payload: bytes, header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    timestamp = parts["t"]
    signature = parts["v1"]

    # Check timestamp freshness (5 min tolerance)
    if abs(time.time() - int(timestamp)) > 300:
        raise ValueError("Timestamp expired")

    expected = hmac.new(
        secret.encode(),
        f"{timestamp}.".encode() + payload,
        hashlib.sha256,
    ).hexdigest()

    return hmac.compare_digest(expected, signature)

Retry policy

Failed deliveries are retried with exponential backoff. Your endpoint must respond with a 2xx status within 30 seconds.

  • Up to 5 attempts per delivery.
  • Exponential backoff: min(30s × 2^attempt, 24h) + 0–5s jitter.
  • 2xx — 2xx — delivery successful.
  • 429 / 5xx — 429 or 5xx — retried with backoff.
  • 410 — 410 Gone — endpoint immediately disabled.
  • 4xx — Other 4xx — permanent failure, no retry.
  • 3xx — Redirects are not followed — permanent failure, no retry. Register the final URL.
  • Endpoints are automatically disabled after 10 consecutive failures.

Supported Hash Algorithms

Supported hash algorithms: SHA-256 (all plans), SHA-384 and SHA-512 (Pro and Enterprise). The hash must be provided as a hex-encoded string.

Async API

The API is asynchronous. Submit your hash and poll for status, or use webhooks (Pro) for notifications.

Signed Proof Documents

Pro and Enterprise plans can export signed proof documents containing all cryptographic evidence. Pro

Qualified timestamps

Pro and Enterprise plans can add eIDAS-qualified timestamps for enhanced legal standing. Pro