Back to API Docs
Public API — No Authentication Required

Verification API

Verify signed documents programmatically. Look up by code, upload a PDF, query the transparency log, or verify entirely offline using embedded W3C credentials.

How Verification Works

Every document signed through SignForge embeds cryptographic proof directly inside the PDF. Verification uses a multi-layer cascade — each layer is self-sufficient, so verification works even if some layers are unavailable.

Layer 1

W3C VC

Validate the embedded Verifiable Credential with ECDSA P-256

Layer 2

DB Lookup

Match verification code against the signing database

Layer 3

Hash Lookup

Match document SHA-256 against the transparency log

Layer 4

ECDSA Fallback

Verify embedded JSON signature without any database

Endpoints

All verification endpoints are public and require no API key. Rate limited to prevent abuse.

Document Verification

GET
/api/verify/{code}
POST
/api/verify/upload

Transparency Log

GET
/api/transparency/latest
GET
/api/transparency/hash/{sha256}
GET
/api/transparency/entry/{id}
GET
/api/transparency/keys

DID Discovery

GET
/.well-known/did.json24h cache

Verify by Code

Every signed PDF includes a verification code (printed on the document and embedded in the QR code). The code format is sf_ followed by 12 alphanumeric characters.

bash
curl https://signforge.io/api/verify/sf_abc123def456

Response

json
{
  "verified": true,
  "verification_code": "sf_abc123def456",
  "envelope_title": "Service Agreement",
  "sender": {
    "name": "Alex Chen",
    "email": "alex@company.com"
  },
  "signer": {
    "name": "Jane Doe",
    "email": "jane@example.com"
  },
  "signed_at": "2026-03-25T11:59:00Z",
  "original_sha256": "a1b2c3...",
  "signed_sha256": "d4e5f6...",
  "page_count": 3,
  "document_size_bytes": 245760,
  "hash_match": true,
  "method": "verifiable_credential",
  "vc_valid": true,
  "transparency_log": {
    "entry_id": 142,
    "merkle_root": "7f8e9d...",
    "merkle_proof": ["a1b2c3...", "d4e5f6..."],
    "signed_tree_head": "eyJhbGci...",
    "logged_at": "2026-03-25T12:00:00Z"
  }
}

method indicates which verification layer confirmed the document: verifiable_credential, embedded_json, hash_lookup, or cryptographic_signature.

Verify by Upload

Upload a signed PDF and the API will extract the embedded proof, verify the cryptographic signatures, and check the transparency log. Supports files up to 15 MB.

Python

python
import requests

with open("signed_contract.pdf", "rb") as f:
    resp = requests.post(
        "https://signforge.io/api/verify/upload",
        files={"file": ("signed_contract.pdf", f, "application/pdf")},
    )

result = resp.json()
print("Verified:", result["verified"])
print("Method:", result["method"])
print("VC Valid:", result["vc_valid"])

if result.get("transparency_log"):
    log = result["transparency_log"]
    print(f"Log entry #{log['entry_id']}, root: {log['merkle_root'][:16]}...")

Node.js

javascript
const fs = require("fs");

const form = new FormData();
form.append("file", new Blob([fs.readFileSync("signed_contract.pdf")]), "signed_contract.pdf");

const resp = await fetch("https://signforge.io/api/verify/upload", {
  method: "POST",
  body: form,
});

const result = await resp.json();
console.log("Verified:", result.verified);
console.log("Method:", result.method);

cURL

bash
curl -X POST https://signforge.io/api/verify/upload \
  -F "file=@signed_contract.pdf"

Verify by Document Hash

If you have the SHA-256 hash of a signed PDF, you can look it up directly in the transparency log without uploading the file.

bash
# Compute the SHA-256 hash of the signed PDF
HASH=$(sha256sum signed_contract.pdf | cut -d' ' -f1)

# Look up in the transparency log
curl https://signforge.io/api/transparency/hash/$HASH

Response

json
{
  "entry_id": 142,
  "document_hash": "d4e5f6a1b2c3...",
  "envelope_id": "550e8400-e29b-41d4-a716-446655440000",
  "verification_code": "sf_abc123def456",
  "merkle_root": "7f8e9d...",
  "merkle_proof": ["a1b2c3...", "d4e5f6..."],
  "signed_tree_head": "eyJhbGci...",
  "created_at": "2026-03-25T12:00:00Z"
}

Offline Verification

The showpiece: no network required

Every signed PDF embeds a self-verifying signforge_proof.html containing the full proof bundle — W3C VC, JAdES JWS, public keys, Merkle proof, RFC 3161 timestamp, and signer identities. Open it in any browser to verify, or use our standalone packages below.

Python

bash
pip install signforge-verify[pdf]
python
"""Offline verification of a SignForge-signed PDF.
Requires: pip install signforge-verify[pdf]
PyPI: https://pypi.org/project/signforge-verify/
"""
from signforge_verify import verify

# Verify a signed PDF
result = verify("signed_contract.pdf")
print("Verified:", result["valid"])

# Check individual proofs
for name, check in result["checks"].items():
    print(f"  {name}: {check['status']} — {check.get('detail', '')}")

# Or verify a .proof.html file
result = verify("document.proof.html")
print("Verified:", result["valid"])
bash
# CLI usage
signforge-verify signed_contract.pdf
signforge-verify document.proof.html --json

Node.js / TypeScript

bash
npm install @signforge/verify
javascript
// Offline verification of a SignForge-signed document
// npm: https://www.npmjs.com/package/@signforge/verify
// Zero runtime dependencies — uses Web Crypto API (Node 18+)

import { SignForgeVerifier } from '@signforge/verify';
import fs from 'fs';

const verifier = new SignForgeVerifier();

// Verify a .proof.html file
const html = fs.readFileSync('document.proof.html', 'utf-8');
const result = await verifier.verifyFromHtml(html);
console.log('Verified:', result.valid);

// Verify a signed PDF
const pdf = fs.readFileSync('signed_contract.pdf');
const pdfResult = await verifier.verifyFromPdf(pdf);
console.log('Verified:', pdfResult.valid);

// Check individual proofs
for (const [name, check] of Object.entries(result.checks)) {
  console.log(`  ${name}: ${check.status} — ${check.detail || ''}`);
}
bash
# CLI usage
npx @signforge/verify signed_contract.pdf
npx @signforge/verify document.proof.html --json

Transparency Log

SignForge maintains a public Merkle transparency log — the same cryptographic structure used by Certificate Transparency to secure the web's TLS certificates. Every signed document is appended to the log, and the tree head is independently signed with a separate key.

Get current tree state

bash
curl https://signforge.io/api/transparency/latest
json
{
  "tree_size": 142,
  "merkle_root": "7f8e9da1b2c3d4e5f6...",
  "signed_tree_head": "eyJhbGciOiJFUzI1NiIs...",
  "updated_at": "2026-04-14T08:30:00Z"
}

Get public keys

Two separate keys protect the system. The issuer key signs Verifiable Credentials. The log key signs tree heads. Compromise of one does not break the other.

bash
curl https://signforge.io/api/transparency/keys
json
{
  "version": 1,
  "issuer": {
    "did_key": "did:key:zDnae...",
    "fingerprint": "sha256:f83OJ3D2...",
    "algorithm": "ECDSA P-256",
    "purpose": "Signs W3C Verifiable Credential signing receipts",
    "publicKeyJwk": {
      "kty": "EC",
      "crv": "P-256",
      "x": "f83OJ3D2...",
      "y": "x_FEzRu9..."
    }
  },
  "log": {
    "did_key": "did:key:zDnae...",
    "fingerprint": "sha256:kH7bEMqz...",
    "algorithm": "ECDSA P-256",
    "purpose": "Signs transparency log Signed Tree Heads",
    "publicKeyJwk": {
      "kty": "EC",
      "crv": "P-256",
      "x": "kH7bEMqz...",
      "y": "pR2sTuVw..."
    }
  }
}

DID:web document

The keys are also published as a W3C DID document at the well-known endpoint, enabling automated discovery by verifiers and wallets.

bash
curl https://signforge.io/.well-known/did.json

Response Schemas

Verify Response

FieldTypeDescription
verifiedbooleanWhether the document is verified
verification_codestringThe sf_ code for this document
envelope_titlestringTitle of the signed envelope
senderobject{ name, email } of the sender
signerobject{ name, email } of the signer
signed_atISO 8601When the document was signed
original_sha256stringHash of the original PDF
signed_sha256stringHash of the signed PDF
methodenumverifiable_credential | embedded_json | hash_lookup | cryptographic_signature
vc_validbooleanWhether the W3C VC signature is valid
transparency_logobject | nullMerkle proof and tree head if available

Transparency Log Entry

FieldTypeDescription
entry_idintegerSequential log entry ID
document_hashstringSHA-256 hash of the signed PDF
envelope_idUUIDID of the envelope
verification_codestringThe sf_ verification code
merkle_rootstringMerkle tree root hash at time of logging
merkle_proofstring[]Proof path for independent verification
signed_tree_headstringECDSA-signed tree head (JWS)
created_atISO 8601When the entry was logged

Rate Limits

EndpointLimit
GET /api/verify/{code}30 req/min
POST /api/verify/upload10 req/min
GET /api/transparency/*30 req/min
GET /.well-known/did.json30 req/min

Rate limits are per IP address. Exceeding the limit returns HTTP 429. No authentication is needed for these endpoints.

AI-Assisted Verification

Users can verify documents using AI tools. Upload a signed PDF to ChatGPT or Claude, and the AI can hash it, query the transparency log API, and confirm authenticity.

Example prompt for ChatGPT or Claude

“I have a signed PDF from SignForge. Please compute the SHA-256 hash and check it against the SignForge transparency log at https://signforge.io/api/transparency/hash/[HASH]. The PDF contains an embedded signforge_proof.html with the full proof bundle (W3C VC, keys, Merkle proof). Extract it and verify the ECDSA signature.”

API Reference

Full REST API documentation with endpoint reference.

MCP Server

Use SignForge from Claude Desktop and AI agents.

Code Examples

Integration samples for n8n, Retool, cURL, and more.