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...",
    "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

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)

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.

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) is only included if the endpoint opts in via the include_timestamp_token setting. 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.
  • 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