API Documentation

Integrate Provelio into your application with our REST API.

Quick Start

1. Get an API Key

Go to Settings > Advanced and create an API key.

2. Create a Certificate

curl -X POST https://api.provelio.org/v1/certificates \
  -H "Authorization: Bearer prv_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "sha256Hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "fileName": "document.pdf",
    "fileSize": 2048576,
    "metadata": { "customerId": "cust_abc123", "orderId": "ord_xyz789" }
  }'

3. Verify a Certificate

curl https://api.provelio.org/v1/verify/abc123def456

JavaScript Example

const API_KEY = 'prv_live_your_key'
const BASE = 'https://api.provelio.org/v1'

// Hash a file in the browser
async function hashFile(file) {
  const buffer = await file.arrayBuffer()
  const hash = await crypto.subtle.digest('SHA-256', buffer)
  return Array.from(new Uint8Array(hash))
    .map(b => b.toString(16).padStart(2, '0')).join('')
}

// Create a certificate
async function createCertificate(file) {
  const sha256Hash = await hashFile(file)
  const res = await fetch(`${BASE}/certificates`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      sha256Hash,
      fileName: file.name,
      fileSize: file.size,
      fileType: file.type,
    }),
  })
  return res.json()
}

Python Example

import hashlib
import os
import requests

API_KEY = "prv_live_your_key"
BASE = "https://api.provelio.org/v1"

def hash_file(path: str) -> str:
    sha256 = hashlib.sha256()
    with open(path, "rb") as f:
        for chunk in iter(lambda: f.read(8192), b""):
            sha256.update(chunk)
    return sha256.hexdigest()

def create_certificate(path: str):
    file_hash = hash_file(path)
    resp = requests.post(
        f"{BASE}/certificates",
        headers={"Authorization": f"Bearer {API_KEY}"},
        json={
            "sha256Hash": file_hash,
            "fileName": path.split("/")[-1],
            "fileSize": os.path.getsize(path),
        },
    )
    return resp.json()

Authentication

All API requests require a Bearer token in the Authorization header.

Test keys (prv_test_) create test-mode certificates. Live keys (prv_live_) create real certificates anchored to the blockchain.

Rate Limits

EndpointLimit
GET /v1/certificates120/min
POST /v1/certificates30/min
GET /v1/verify/*30/min

Platform & Reseller Pattern

If you manage certificates on behalf of many end-customers, use metadata to attach routing data to each certificate. It is stored with the certificate and echoed back in every API response and webhook payload — so a single webhook endpoint can fan out to any number of customers.

1. Stamp metadata at creation

curl -X POST https://api.provelio.org/v1/certificates \
  -H "Authorization: Bearer prv_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "sha256Hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "fileName": "contract.pdf",
    "fileSize": 204800,
    "metadata": {
      "customerId": "cust_abc123",
      "orderId":    "ord_xyz789",
      "region":     "eu-west"
    }
  }'

2. Route in your webhook handler

// One endpoint — fan out to any number of customers
app.post('/webhooks/provelio', (req, res) => {
  const { event, data } = req.body
  const { customerId, orderId } = data.metadata   // your routing keys

  if (event === 'certificate.confirmed') {
    notifyCustomer(customerId, { orderId, publicId: data.publicId })
  }
  res.status(200).end()
})
Metadata constraints
  • Maximum 50 keys per certificate
  • Key: 1–64 characters, string only
  • Value: max 500 characters, string only

Webhooks

Webhooks push certificate status changes to your server in real time. Your API key needs the webhooks:write scope — include it when creating a key in Settings > Advanced.

1. Register an endpoint

curl -X POST https://api.provelio.org/v1/webhooks \
  -H "Authorization: Bearer prv_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/provelio",
    "events": ["certificate.confirmed", "certificate.failed"],
    "mode": "live"
  }'

The response includes a secret (whsec_...) — it is shown once only. Store it immediately in your environment variables.

2. Events

EventWhen it fires
certificate.createdCertificate recorded, RFC 3161 timestamp issued
certificate.anchoringBitcoin anchoring in progress
certificate.confirmedBitcoin block confirmed — proof is final
certificate.failedAnchoring failed after retries

3. Payload

{
  "event": "certificate.confirmed",
  "timestamp": "2026-04-19T08:00:00.000Z",
  "data": {
    "certificateId": "550e8400-e29b-41d4-a716-446655440000",
    "publicId": "abc123def456",
    "status": "confirmed",
    "fileName": "document.pdf",
    "sha256Hash": "e3b0c44298fc1c149afbf4c8996fb924...",
    "testMode": false,
    "metadata": { "customerId": "cust_abc123", "orderId": "ord_xyz789" }
  }
}

4. Verify the signature

Every delivery includes X-Provelio-Signature — an HMAC-SHA256 hex digest of the raw request body signed with your webhook secret. Always verify it before processing.

// Node.js — Express
import crypto from 'crypto'

app.post('/webhooks/provelio', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-provelio-signature']
  const expected = crypto
    .createHmac('sha256', process.env.PROVELIO_WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex')

  if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
    return res.status(401).end()
  }

  const event = JSON.parse(req.body)
  if (event.event === 'certificate.confirmed') {
    // your logic here
  }
  res.status(200).end()
})
# Python — Flask
import hmac, hashlib, os
from flask import Flask, request, abort

app = Flask(__name__)

@app.route('/webhooks/provelio', methods=['POST'])
def webhook():
    sig = request.headers.get('X-Provelio-Signature', '')
    expected = hmac.new(
        os.environ['PROVELIO_WEBHOOK_SECRET'].encode(),
        request.data,
        hashlib.sha256,
    ).hexdigest()

    if not hmac.compare_digest(sig, expected):
        abort(401)

    event = request.json
    if event['event'] == 'certificate.confirmed':
        pass  # your logic here
    return '', 200

Metadata constraints

The metadata field on certificates lets you attach routing data that is echoed in every API response and webhook payload — useful for fan-out to per-customer handlers.

  • Maximum 50 keys per certificate
  • Key length: 1–64 characters
  • Value length: max 500 characters
  • All keys and values must be strings

Limits & retries

  • Maximum 5 webhooks per account
  • 10-second delivery timeout per attempt
  • Endpoint auto-disabled after 10 consecutive failures
  • Re-enable via PATCH /v1/webhooks/:id with { "active": true }

OpenAPI Specification

Full machine-readable spec: api.provelio.org/v1/openapi.yaml