Proovik
REST · JSON · no separate host

Proovik API

Prove that your document already existed at a specific date and time, from your own code. It is the same API this website uses: every endpoint on this page exists today and answers in production.

Base URL https://proovik.com/api

Authentication · Header Authorization: Bearer YOUR_API_KEY on the endpoints marked "key". Those marked "public" need no key.

How to get an API key today

It is not self-service yet. Proovik enables the key manually for your account; once enabled it shows up in your dashboard, under API. One key covers both certification and traceability.

  1. Create an account (free).
  2. Request API activation through the contact form, subject "API", stating your account email and what you will use it for.
  3. Once enabled, copy the key from the dashboard. If it is not there yet, the request has not been processed: there is no other route.

Document certification

Anchoring a file's SHA-256 on the Kaspa chain

The document is never uploaded: you compute its SHA-256 on your side and send only the hash (64 hex characters).

POST/api/certifykeyCertify a hash

Anchors the hash on-chain. Uses one credit from your account. If the same hash was already certified, the existing record is returned (already_exists: true) at no cost.

Request
curl -X POST https://proovik.com/api/certify \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "description": "Contrato v2.1"
  }'
Response · 200 · confirmed
{
  "success": true,
  "status": "success",
  "confirmed": true,
  "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
  "txid": "b173e1ff…802889",
  "explorer_url": "https://explorer.kaspa.org/txs/b173e1ff…802889",
  "timestamp": "2026-01-12 20:48:30",
  "credit_consumed": true,
  "certificate_url": "/api/certificate?hash=e3b0c442…&t=…"
}
202 · sent and pending. The transaction has been broadcast but is not visible yet: resend the same hash after a few seconds to get the final state (there is no webhook to notify you).

Errors: 400 invalid_hash · 401 invalid_api_key · 401 identification_required (without a key the endpoint is reserved for the web form) · 429 no_credits · 502 notary_failed (nothing is charged).

GET/api/verify?hash={sha256}publicCheck whether a hash is certified

Anyone can query it, no key needed. Only chain-confirmed certifications are returned.

Request
curl "https://proovik.com/api/verify?hash=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
Response
{
  "success": true,
  "exists": true,
  "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
  "txid": "b173e1ff…802889",
  "explorer_url": "https://explorer.kaspa.org/txs/b173e1ff…802889",
  "timestamp": "2026-01-12 20:48:30",
  "has_merkle_proof": true
}
// {"success": true, "exists": false}  si no está certificado
GET/api/certificate?hash={sha256}&t={token}publicDownload the PDF certificate

The /api/certify response includes certificate_url: a signed link (parameter t) that also shows your private metadata. Without t the same endpoint serves the public version of the certificate to anyone. The PDF embeds the PVK-PROOF/1 evidence package, which can be verified without Proovik.

Request
curl -o certificado.pdf \
  "https://proovik.com/api/certificate?hash=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"

Errors: 404 not_found · 409 certification_pending (JSON, not a PDF: no certificate yet) · 429 rate_limited (60 downloads per hour and IP).

GET/api/certify/proof?hash={sha256}keyMerkle proof of a certification

Returns the Merkle path and anchored root of a certification made with your key. Other accounts' records answer 404.

Request
curl "https://proovik.com/api/certify/proof?hash=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" \
  -H "Authorization: Bearer YOUR_API_KEY"
Response
{
  "success": true,
  "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
  "status": "success",
  "txid": "b173e1ff…802889",
  "anchored": true,
  "merkle_root": "d4735e3a…13ab35",
  "merkle_proof": [ { "hash": "…", "position": "left" }, … ]
}

Batch traceability

Batches and events hash-chained and anchored in Merkle batches

Each event links to the previous event's hash; the first one links to the batch hash. Records are anchored on the Kaspa chain in cycles of a few minutes: status moves from pending to confirmed with no action on your side. GS1 identifiers (GTIN, SGTIN, GLN) are check-digit validated.

POST/api/trace/batchkeyCreate a batch

Required: product_name. Optional: batch_id (generated if absent), gtin, sgtin (system-wide unique; a repeat answers 409), origin, origin_gln, origin_lat/origin_lng, quantity, quantity_unit, metadata. Supports an Idempotency-Key header.

Request
curl -X POST https://proovik.com/api/trace/batch \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "product_name": "Aceite de oliva virgen extra 500 ml",
    "gtin": "8412345678905",
    "origin": "Jaén",
    "quantity": 5000,
    "quantity_unit": "bottles",
    "metadata": { "variety": "Picual", "harvest_year": 2025 }
  }'
Response · 201
{
  "success": true,
  "batch_id": "BATCH-20260905-A1B2C3D4",
  "payload_hash": "7f83b165…6d9069",
  "created_at": "2026-09-05T10:00:00+00:00"
}
POST/api/trace/eventkeyAdd an event

Required: batch_id and event_type, one of COMMISSION, OBSERVE, TRANSFORM, SHIP, RECEIVE, AGGREGATE, DISAGGREGATE, CUSTOM. Optional: location {name, gln, lat, lng}, actor {name, id, role}, data, evidence_hash, gs1_bizstep.

Request
curl -X POST https://proovik.com/api/trace/event \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "batch_id": "BATCH-20260905-A1B2C3D4",
    "event_type": "SHIP",
    "location": { "name": "Almacén central", "gln": "8412345000010" },
    "data": { "carrier": "Transportes Sur", "pallets": 12 }
  }'
Response · 201
{
  "success": true,
  "event_id": 17,
  "sequence": 3,
  "event_hash": "e3b0c442…52b855",
  "prev_event_hash": "9f86d081…00a08",
  "status": "pending"
}
GET/api/trace/batch/{batch_id}key optionalRead a batch with its events

Without a key you get the public view of the batch; with your key, the full data of your own batches.

Request
curl "https://proovik.com/api/trace/batch/BATCH-20260905-A1B2C3D4" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET/api/trace/verify/{batch_id}key optionalVerify a batch's integrity

Recomputes the batch hash, the event chain and the Merkle proofs against what is anchored. overall_valid is true only when everything matches; fully_verified additionally requires every event to be confirmed.

Request
curl "https://proovik.com/api/trace/verify/BATCH-20260905-A1B2C3D4"
Response
{
  "success": true,
  "batch_id": "BATCH-20260905-A1B2C3D4",
  "verification": {
    "overall_valid": true,
    "batch_integrity": { "valid": true, … },
    "chain_integrity": { "valid": true, … },
    "fully_verified": true
  }
}

Also available, with a key: GET /api/trace/search?gtin=|sgtin=|lot=|product=, GET /api/trace/event/{event_id}, GET /api/trace/event/{event_id}/proof, GET /api/trace/batch/{batch_id}/proof and GET /api/trace/batch/{batch_id}/qr. Details in the full reference.

Per-key limits: 60 writes and 300 reads per minute. Beyond that you get 429 with Retry-After.

Independent verification (PVK-PROOF/1)

Checking a proof without trusting Proovik

The verifier recomputes the four links of the proof: the document hash is in the transaction, the transaction is in the block's Merkle tree, the header reproduces the block hash, and that header carries valid proof-of-work. You always provide the document hash yourself: it is never read from the package. Three verdicts: VALIDO, INCOMPLETO (could not be determined; never counts as valid) and INVALIDO.

POST/api/verify/proofpublicVerify a proof package

Send the .pvkproof.json file (the one attached to the PDF) or the package as JSON, together with the SHA-256 of your own copy of the document. It does not touch the network: it verifies what you give it. No key.

Request
curl -X POST https://proovik.com/api/verify/proof \
  -F "hash=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" \
  -F "[email protected]"
Response
{
  "spec": "PVK-PROOF/1",
  "modo": "paquete",
  "documento_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
  "txid": "b173e1ff…802889",
  "resultado": "VALIDO",
  "valido": true,
  "completo": true,
  "eslabones": [
    { "n": 1, "estado": "OK", … },
    { "n": 2, "estado": "OK", … },
    { "n": 3, "estado": "OK", … },
    { "n": 4, "estado": "OK", … }
  ],
  "algoritmo": { … }
}
GET/api/verify/proof/{txid}?hash={sha256}publicRebuild and verify from the chain

With just the txid and the hash, it rebuilds the package by querying the Kaspa network. The full block header is only available for about 40 hours: after that, links 3 and 4 come back INCOMPLETO. No key; CORS enabled; 30 requests per hour and IP.

Request
curl "https://proovik.com/api/verify/proof/b173e1ff9d73e09b51db78b9050a4be578f232f3d5e106a214950a8265802889?hash=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"

Specification and online verifier

What this API does not have (yet)

  • Outgoing webhooks. Nothing notifies you when a transaction confirms. For certifications, resend the same hash to POST /api/certify or poll GET /api/verify?hash=; for traceability, poll GET /api/trace/verify/{batch_id}.
  • SDKs. There are no official libraries in any language. It is HTTP and JSON: any HTTP client works.
  • OpenAPI file. The field and response reference lives in the technical documentation linked below.
  • Self-service keys, multiple keys or scopes. One key per account, enabled manually.
  • Sandbox. Every real certification uses a credit. To try things out at no cost, use the public query and verification endpoints.
PVK-PROOF/1

Full reference and verifier

The technical documentation describes every field and response (Spanish only for now). The online verifier checks any certificate without an account.