Autenticazione
Tutte le API REST richiedono un'API key nel formato ufficiale vlk_* (32 byte base64url). Inviare nel header Authorization: Bearer vlk_xxxxxxxx.
veylon_live_* / veylon_test_* ritornano 401 legacy_format_not_supported. Migrare al nuovo formato dal Dev Portal.
curl -H "Authorization: Bearer vlk_xxxxxxxx..." \
https://api.veylon.app/v1/tag/cert?codice=VLN-TAG-2026-AB23-9KMP
Quota mensile per piano: free 10 / pro 1.000 / business 50.000 / enterprise 500.000. Lo scope di lettura non scala quota. Header standard restituiti: X-RateLimit-Limit, X-RateLimit-Remaining, X-Veylon-Piano, X-Veylon-Api-Version: v1.
API keys self-service
Le API key vlk_* si generano e gestiscono dal Dev Portal interno. Ogni chiave porta uno o piu scope granulari: sigillo:create, tag:write, tag:read, tag:read:any, tag:transfer, admin.
Il valore plain della chiave appare una sola volta: copiarla subito in un secret manager. Il backend persiste solo lo SHA-256 hex. La revoca e immediata (active=false, propagata su tutte le edge function al successivo lookup).
audit_log con actor_type: api_key, api_key_id, scope richiesti, IP hash, user agent.
Sigillo
Crea un sigillo Veylon per un documento. Trasmesso solo l'hash SHA-256 (il documento non lascia il vostro perimetro).
Request body
{
"sha256": "a3f5b8c7d9e1f2a4...", // 64 char hex, obbligatorio
"doc_name": "Contratto Acme 2026", // string max 200
"anchor_method": "opentimestamps", // opzionale, default opentimestamps
"metadata": { "modulo": "contratto" } // opzionale, jsonb
}
Response 200
{
"success": true,
"atto_id": "uuid",
"sha256": "a3f5b8c7d9e1f2a4...",
"ots_status": "pending",
"verifica_url": "https://veylon.app/verifica.html?h=a3f5b8c7...",
"created_at": "2026-06-16T20:45:00Z"
}
Esempi
curl -X POST https://api.veylon.app/v1/sigillo \
-H "Authorization: Bearer veylon_live_xxx" \
-H "Content-Type: application/json" \
-d '{"sha256":"a3f5b8c7d9e1f2a4b6c8d0e2f4a6b8c0d2e4f6a8b0c2d4e6f8a0b2c4d6e8f0a2","doc_name":"Contratto Acme"}'
import crypto from "node:crypto";
const buf = await fs.readFile("contratto.pdf");
const sha = crypto.createHash("sha256").update(buf).digest("hex");
const r = await fetch("https://api.veylon.app/v1/sigillo", {
method: "POST",
headers: { "Authorization": `Bearer ${process.env.VEYLON_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ sha256: sha, doc_name: "Contratto Acme" }),
});
const atto = await r.json();
console.log(atto.verifica_url);
import hashlib, requests, os
sha = hashlib.sha256(open("contratto.pdf", "rb").read()).hexdigest()
r = requests.post("https://api.veylon.app/v1/sigillo",
headers={"Authorization": f"Bearer {os.environ['VEYLON_KEY']}"},
json={"sha256": sha, "doc_name": "Contratto Acme"})
print(r.json()["verifica_url"])
Tag · Emissione singola
Emette un singolo certificato Veylon Tag (DPP UE 2024/1781, batteria Annex XIII, EUDR DDS, tessile Prato 2027, pharma GMP, lusso). 1 sigillo per chiamata.
Request body (estratto)
{
"produttore_id": "uuid", // obbligatorio
"sha256_dichiarazione": "<64 hex>", // obbligatorio
"codice_prodotto": "BAG-001", // opzionale, max 200
"lotto": "L-2026-04", // opzionale
"applicazione": "qr_adesivo", // qr_adesivo | qr_inciso | nfc_ntag424_dna
"categoria_dpp": "textile_general", // opzionale, classifica DPP
"gtin": "08001234567890", // opzionale, GS1
"regulatory_compliance": { "reach": true },
"visibility_jsonb": { "public": ["origin"] },
"metadata": {}
}
Response 200
{
"success": true,
"atto_id": "uuid",
"certificato_id": "uuid",
"codice_pubblico": "VLN-TAG-2026-AB23-9KMP",
"qr_url": "https://veylon.app/t/VLN-TAG-2026-AB23-9KMP",
"verifica_url": "...",
"ots_status": "pending",
"quota_used": 124, "quota_remaining": 49876, "quota_monthly": 50000,
"piano": "business"
}
Esempi
curl -X POST https://api.veylon.app/v1/tag/emit \
-H "Authorization: Bearer $VEYLON_KEY" \
-H "Content-Type: application/json" \
-d '{"produttore_id":"...","sha256_dichiarazione":"a3f5...","codice_prodotto":"BAG-001","lotto":"L-2026-04","applicazione":"qr_adesivo"}'
import { createHash } from "node:crypto";
import { readFile } from "node:fs/promises";
const sha = createHash("sha256").update(await readFile("dichiarazione.pdf")).digest("hex");
const r = await fetch("https://api.veylon.app/v1/tag/emit", {
method: "POST",
headers: { "Authorization": `Bearer ${process.env.VEYLON_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
produttore_id: process.env.VEYLON_PRODUTTORE_ID,
sha256_dichiarazione: sha,
codice_prodotto: "BAG-001", lotto: "L-2026-04",
applicazione: "qr_adesivo",
}),
});
const out = await r.json();
console.log(out.qr_url, "quota rimanente:", out.quota_remaining);
import hashlib, os, requests
sha = hashlib.sha256(open("dichiarazione.pdf", "rb").read()).hexdigest()
r = requests.post(
"https://api.veylon.app/v1/tag/emit",
headers={"Authorization": f"Bearer {os.environ['VEYLON_KEY']}"},
json={
"produttore_id": os.environ["VEYLON_PRODUTTORE_ID"],
"sha256_dichiarazione": sha,
"codice_prodotto": "BAG-001", "lotto": "L-2026-04",
"applicazione": "qr_adesivo",
},
timeout=15,
)
print(r.json()["qr_url"])
Tag · Bulk asincrono
Enqueue di un lotto fino a 100.000 righe per call (lotti grandi fino a 10M con worker dedicato, contattare [email protected]). Risposta HTTP 202 + job_id. Polling con GET /v1/tag/job-status?job_id=.... Header Idempotency-Key supportato (replay 24h).
Request body
{
"produttore_id": "uuid",
"codice_lotto": "LOT-2026-04",
"webhook_url": "https://acme.example.com/hooks/veylon",
"righe": [
{ "sha256_dichiarazione": "...", "codice_prodotto": "BAG-001", "lotto": "LOT-2026-04", "applicazione": "qr_adesivo" }
// ...fino a 100000 oggetti
]
}
Response 202
{
"success": true,
"job_id": "uuid",
"status": "queued",
"righe_richieste": 4523,
"codice_lotto": "LOT-2026-04",
"polling_url": "https://veylon.app/functions/v1/api-v1-tag-job-status?job_id=...",
"eta_sec_stimato": 7,
"idempotency_key": "lotto-2026-04-retry-1"
}
Esempi
curl -X POST https://api.veylon.app/v1/tag/bulk \
-H "Authorization: Bearer $VEYLON_KEY" \
-H "Idempotency-Key: lotto-2026-04-retry-1" \
-H "Content-Type: application/json" \
-d @lotto-2026-04.json
const r = await fetch("https://api.veylon.app/v1/tag/bulk", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.VEYLON_KEY}`,
"Idempotency-Key": "lotto-2026-04-retry-1",
"Content-Type": "application/json",
},
body: JSON.stringify({
produttore_id: process.env.VEYLON_PRODUTTORE_ID,
codice_lotto: "LOT-2026-04",
webhook_url: "https://acme.example.com/hooks/veylon",
righe: items.map((it) => ({
sha256_dichiarazione: it.sha,
codice_prodotto: it.sku,
lotto: "LOT-2026-04",
applicazione: "qr_adesivo",
})),
}),
});
const job = await r.json();
console.log("job", job.job_id, "ETA", job.eta_sec_stimato, "s");
import os, requests
r = requests.post(
"https://api.veylon.app/v1/tag/bulk",
headers={
"Authorization": f"Bearer {os.environ['VEYLON_KEY']}",
"Idempotency-Key": "lotto-2026-04-retry-1",
},
json={
"produttore_id": os.environ["VEYLON_PRODUTTORE_ID"],
"codice_lotto": "LOT-2026-04",
"webhook_url": "https://acme.example.com/hooks/veylon",
"righe": righe,
},
timeout=60,
)
print("job", r.json()["job_id"])
_shared/merkle.ts + tag-ots-merkle-finalize-cron). Il certificato di lotto ritorna comunque ots_status per ogni riga, derivato dalla prova Merkle.Verifica
Recupera lo stato di un sigillo dato il suo SHA-256. Endpoint pubblico (no auth) per integrazioni di verifica indipendenti (dogana EU, distributori, audit pharma).
Esempio chiamata
curl https://api.veylon.app/v1/verifica?sha256=a3f5b8c7d9e1f2a4...
Response 200
{
"found": true,
"sha256": "a3f5b8c7...",
"doc_name": "Contratto Acme 2026",
"ots_status": "confirmed",
"btc_block": 892341,
"btc_tx": "7f3e5a9c1d4b8f2e...",
"created_at": "2026-06-14T15:30:00Z",
"confirmed_at": "2026-06-14T17:42:00Z"
}
Multi-firma (K-S.2)
Crea richiesta firma multi-parte. Veylon invia OTP via email a ogni firmatario, traccia firme, notifica owner al completamento.
{
"doc_name": "NDA Acme",
"doc_sha256": "a3f5...",
"ordine_tipo": "parallelo", // o "sequenziale"
"scadenza_hours": 168,
"firmatari": [
{ "email": "[email protected]", "nome": "Mario Rossi" },
{ "email": "[email protected]", "nome": "Manuel Finelli" }
]
}
Webhook outbound
Veylon notifica via HTTPS POST gli eventi importanti al vostro endpoint registrato. Configurare in admin.html → tab Webhook.
Eventi disponibili
atto.creato // appena ricevuto POST /v1/sigillo
atto.confermato // OTS Bitcoin block confirmation
firma.completata // singola firma OTP
multi_firma.completata // tutti firmatari hanno firmato
cert.emesso // certificato istituzionale rilasciato
tag.trasferito // proprieta tag prodotto cambiata
tag.bulk.completed // job bulk REST v1 terminato (manifest ZIP signed_url)
tag.bulk.failed // job bulk REST v1 fallito
tag.cert.transferred // /v1/tag/transfer chiuso o pending
tag.cert.revoked // /v1/tag/revoke emesso
Payload
{
"event": "atto.confermato",
"timestamp": "2026-06-16T21:00:00Z",
"data": {
"atto_id": "uuid",
"sha256": "a3f5...",
"btc_block": 892341,
"btc_tx": "7f3e..."
},
"signature": "sha256=hmac..." // HMAC con webhook_secret
}
Errori
Tutti gli errori usano status HTTP standard + body JSON con error e opzionalmente detail.
400 Bad Request // input malformato
401 Unauthorized // API key mancante o invalida
402 Payment Required // quota esaurita
403 Forbidden // piano non Business
404 Not Found // risorsa inesistente
422 Unprocessable // validazione semantica
429 Too Many Requests // rate limit (vedi sotto)
500 Internal // errore Veylon (riprova + apri ticket)
502 Bad Gateway // OTS calendari irraggiungibili
503 Service Unavailable // manutenzione pianificata
Rate limit + Quota
Quota mensile applicata SOLO agli scope di scrittura (*:write, *:transfer, sigillo:create). Gli scope di lettura non scalano quota.
| Piano | Quota / mese | SLA |
|---|---|---|
| free | 10 | — |
| pro | 1.000 | 99,5% |
| business | 50.000 | 99,9% |
| enterprise | 500.000 | 99,95% |
Quota esaurita → 402 Payment Required con upgrade_url nella risposta. Reset al primo hit del nuovo mese UTC (CAS atomic, vedi _shared/api-bearer.ts).
X-RateLimit-Limit: 50000
X-RateLimit-Remaining: 49876
X-Veylon-Piano: business
X-Veylon-Api-Version: v1
Header speciale su replay idempotente del bulk: X-Veylon-Idempotent-Replay: 1.
SLA
Piano Business include SLA:
- Uptime mensile 99.9% (monitorato su system-status.html)
- Latenza P95 endpoint sigillo: < 800ms
- OTS conferma media: 1-3 ore (dipende blockchain Bitcoin)
- Risposta supporto: < 4 ore business day
- Bug critici: < 24 ore