Integrate Provelio into your application with our REST API.
Go to Settings > Advanced and create an API key.
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" }
}'curl https://api.provelio.org/v1/verify/abc123def456const 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()
}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()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.
| Endpoint | Limit |
|---|---|
| GET /v1/certificates | 120/min |
| POST /v1/certificates | 30/min |
| GET /v1/verify/* | 30/min |
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.
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"
}
}'// 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()
})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.
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.
| Event | When it fires |
|---|---|
| certificate.created | Certificate recorded, RFC 3161 timestamp issued |
| certificate.anchoring | Bitcoin anchoring in progress |
| certificate.confirmed | Bitcoin block confirmed — proof is final |
| certificate.failed | Anchoring failed after retries |
{
"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" }
}
}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 '', 200The 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.
PATCH /v1/webhooks/:id with { "active": true }Full machine-readable spec: api.provelio.org/v1/openapi.yaml