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.jsonQuickstart
The Anchorify API allows you to create tamper-proof evidence by anchoring hashes on public blockchains. Here's how to get started:
1. Get your API key
Sign up for an account and generate an API key from the console.
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. Submit the hash
Send the hex-encoded hash to our REST API to create a notarization. You'll receive a notarization ID for tracking.
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"
}' import hashlib
import requests
with open("document.pdf", "rb") as f:
hash_hex = hashlib.sha256(f.read()).hexdigest()
resp = requests.post(
"https://api.anchorify.cloud/v1/notarizations",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"algorithm": "SHA256",
"hash": hash_hex,
"name": "document.pdf",
},
)
print(resp.json()["id"]) package main
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
func main() {
f, _ := os.Open("document.pdf")
defer f.Close()
h := sha256.New()
io.Copy(h, f)
hashHex := hex.EncodeToString(h.Sum(nil))
body, _ := json.Marshal(map[string]string{
"algorithm": "SHA256",
"hash": hashHex,
"name": "document.pdf",
})
req, _ := http.NewRequest("POST",
"https://api.anchorify.cloud/v1/notarizations",
bytes.NewReader(body))
req.Header.Set("X-API-Key", "YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result["id"])
} {
"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. Poll for status
Check the proof status. Once anchored on the blockchain, you can export your signed proof document.
curl https://api.anchorify.cloud/v1/notarizations/019c0e1a-d891-7f1f-ab6e-5588c46c2a4b \
-H "X-API-Key: YOUR_API_KEY" resp = requests.get(
"https://api.anchorify.cloud/v1/notarizations/019c0e1a-d891-7f1f-ab6e-5588c46c2a4b",
headers={"X-API-Key": "YOUR_API_KEY"},
)
notarization = resp.json()
print(f"Status: {notarization['status']}") req, _ := http.NewRequest("GET",
"https://api.anchorify.cloud/v1/notarizations/019c0e1a-d891-7f1f-ab6e-5588c46c2a4b",
nil)
req.Header.Set("X-API-Key", "YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var notarization map[string]any
json.NewDecoder(resp.Body).Decode(¬arization)
fmt.Printf("Status: %s\n", notarization["status"]) {
"id": "019c0e1a-d891-7f1f-ab6e-5588c46c2a4b",
"algorithm": "SHA256",
"hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"status": "completed",
"timestamp_proof": {
"status": "timestamped",
"tsa_provider": { "name": "FreeTSA" },
"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_root": "a1b2c3d4...",
"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
ProWebhooks 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 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 | All processing finished. The event_context includes completed_at. |
notarization.failed | Processing failed. The event_context includes failure_reason. |
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}.
{
"id": "019c0e1a-e7a2-7f3b-8c1d-4a9b2e6f8d3c",
"type": "notarization.completed",
"api_version": "2026-03-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",
"hash_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",
"timestamp": {
"status": "timestamped",
"serial_number": "1A2B3C",
"tsa_provider": "FreeTSA",
"tsa_cert_subject": "CN=FreeTSA,O=Free TSA,C=DE",
"tsa_cert_serial": "04B9A5",
"tsa_cert_ski": "A1B2C3D4E5F6...",
"policy_oid": "1.2.3.4.1",
"timestamped_at": "2026-01-30T08:52:48.123456Z"
},
"anchors": [
{
"provider": "Polygon",
"status": "anchored",
"transaction_hash": "0xabc123...",
"explorer_url": "https://polygonscan.com/tx/0xabc123...",
"submitted_at": "2026-01-30T08:52:50Z",
"anchored_at": "2026-01-30T08:53:32Z"
}
],
"accumulator": {
"merkle_root": "a1b2c3d4e5f6..."
}
},
"event_context": {
"completed_at": "2026-01-30T08:53:32Z"
}
}
} Note: The timestamp.token_base64 and proof fields are only included if the endpoint opts in via include_timestamp_token / include_merkle_proof settings.
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) const crypto = require("crypto");
function verifyWebhook(payload, header, secret) {
const parts = Object.fromEntries(
header.split(",").map((p) => p.split("=", 2))
);
// Check timestamp freshness (5 min tolerance)
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) {
throw new Error("Timestamp expired");
}
const expected = crypto
.createHmac("sha256", secret)
.update(`${parts.t}.${payload}`)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(parts.v1)
);
} package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"math"
"strings"
"time"
)
func verifyWebhook(payload []byte, header, secret string) error {
parts := make(map[string]string)
for _, p := range strings.Split(header, ",") {
kv := strings.SplitN(p, "=", 2)
parts[kv[0]] = kv[1]
}
// Check timestamp freshness (5 min tolerance)
ts, _ := time.Parse("", parts["t"])
if math.Abs(float64(time.Now().Unix()-ts.Unix())) > 300 {
return fmt.Errorf("timestamp expired")
}
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(parts["t"] + "."))
mac.Write(payload)
expected := hex.EncodeToString(mac.Sum(nil))
if !hmac.Equal([]byte(expected), []byte(parts["v1"])) {
return fmt.Errorf("invalid signature")
}
return nil
} 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
Enterprise plans can add eIDAS-qualified timestamps for enhanced legal standing. Pro