Documenti in ingresso, dati strutturati in uscita.
Invia un documento e uno schema di campi; ricevi JSON con un punteggio di affidabilità calibrato per ogni campo e — come add-on — le coordinate sul documento. Disponibile come API REST e come widget integrabile di cui i tuoi utenti si fideranno davvero.
https://api.docsense.energyhub.cloud/v1 ·
AUTHAuthorization: Bearer sk_test_… | sk_live_…Errori in formato RFC 9457
application/problem+json ·
Idempotency-Key rispettato sulle POST · elaborazione solo in UE (responsabile ex art. 28 GDPR).Console: accedi con email e password (solo su invito — link monouso dal tuo amministratore). Le chiavi API si creano e si ruotano dalla sezione Chiavi API della console, riservata agli amministratori dello spazio di lavoro.
Quickstart — estrai in due chiamate
1 · Pubblica un template (versioni immutabili)
curl -X POST $BASE/v1/templates \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{
"name": "electricity-invoice",
"language_hint": "it",
"features": {"grounding": true},
"fields": [
{"key": "holder_name", "type": "string", "label": "Intestatario", "required": true},
{"key": "pod", "type": "string", "label": "Codice POD", "validate": "pod"},
{"key": "iban", "type": "string", "validate": "iban"},
{"key": "total_amount", "type": "number", "required": true},
{"key": "due_date", "type": "date"}
]
}'
Tipi scalari: string · number · integer · boolean · date · enum, più
group (oggetto piatto) e table (righe di celle scalari).
Validatori: iban · codice_fiscale · partita_iva · pod · pdr · cap · date · email —
l'esito arriva campo per campo in checks e alimenta il punteggio di affidabilità.
2 · Esegui un'estrazione
curl -X POST $BASE/v1/extractions \
-H "Authorization: Bearer $KEY" \
-F "file=@bolletta.pdf" -F "template=electricity-invoice" -F "mode=sync"
{
"status": "completed",
"fields": {
"pod": {
"value": "IT001E12345678",
"raw_text": "IT 001E 1234 5678",
"confidence": 0.99,
"status": "found",
"checks": [{"name": "pod", "passed": true}],
"locations": [{"page": 1, "bbox": {"x": 0.155, "y": 0.178, "w": 0.129, "h": 0.012}}]
}
},
"usage": {"pages": 1},
"processing": {"engine": "haiku", "duration_ms": 4800}
}
Usa mode=async (i documenti oltre 10 pagine girano sempre in async) e segui
GET /v1/extractions/{id}/events — uno stream SSE che emette ogni campo nel
momento in cui viene estratto, poi completed.
locations) sono l'add-on
grounding a pagamento; la qualità dell'affidabilità è identica con o senza.Caricamento file & limiti
I documenti sono limitati a 50 MB su ogni percorso. Per tenere gli upload voluminosi fuori dal tuo percorso di richiesta (i byte vanno direttamente allo storage) — o per riusare lo stesso documento in più estrazioni — carica prima il file, poi riferiscilo per id:
curl -X POST $BASE/v1/files \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"filename": "bolletta.pdf", "content_type": "application/pdf"}'
# → { "id": "file_…", "upload": { "url": "https://…", "method": "PUT" }, … }
# carica i byte all'URL restituito (PUT S3 presignata, valida 15 minuti)
curl -X PUT "$UPLOAD_URL" -H "Content-Type: application/pdf" --data-binary @bolletta.pdf
curl -X POST $BASE/v1/extractions \
-H "Authorization: Bearer $KEY" \
-F "file_id=file_…" -F "template=electricity-invoice" -F "mode=async"
Verifica lo stato con GET /v1/files/{id}. I file caricati restano riusabili
per 24 ore e poi vengono eliminati — ogni estrazione conserva la propria copia del documento
secondo la retention del tuo workspace.
mode=sync) sono limitate per workspace in
concorrenza: le risposte portano X-RateLimit-Limit /
X-RateLimit-Remaining, e una richiesta oltre il limite riceve 429 con
Retry-After. Il percorso async assorbe i picchi tramite la coda.Widget integrabile
Il widget offre ai tuoi utenti finali caricamento → estrazione progressiva in tempo reale → revisione con evidenziazioni sul documento → conferma, dentro la tua pagina. Nessun account, nessun cookie; il documento va dall'iframe direttamente a Docsense, senza mai passare dalla tua origine.
1 · Crea una sessione (lato server, chiave segreta)
curl -X POST $BASE/v1/widget/sessions \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{
"template": "electricity-invoice",
"allowed_origins": ["https://app.yourcompany.com"],
"locale": "it",
"consent": "notice",
"upload_hint": "Carica la tua ultima bolletta"
}'
# → { "token": "swt_…", "expires_at": "…" } (TTL 30 min, rinnovabile dal widget)
L'allowlist di origini governa anche la CSP frame-ancestors dell'iframe —
solo le origini elencate possono incorporare la sessione. Il widget richiede un template con
features.grounding (le evidenziazioni sono il widget).
2 · Integra (lato client)
<script src="https://api.docsense.energyhub.cloud/sdk/docex.js"></script>
<div id="docsense-slot"></div>
<script>
var widget = DocEx.create({
sessionToken: 'swt_…', // dal tuo backend
container: '#docsense-slot',
locale: 'it',
appearance: { variables: { colorPrimary: '#0E8A6A', borderRadius: '8px' } }
})
widget.on('fields.confirmed', function (p) {
// p.values = mappa piatta chiave → valore finale rivisto dall'utente
form.nome.value = p.values.holder_name || ''
form.pod.value = p.values.pod || ''
})
</script>
| Eventi (widget → te) | Comandi (te → widget) |
|---|---|
ready · upload.started · upload.progress ·
extraction.started · field.extracted (progressivo) ·
extraction.completed · fields.confirmed · error ·
resize · exit |
prefill(values) · setLocale('it'|'en') ·
setTheme(appearance) · retry() · destroy() |
I tipi TypeScript arrivano con @docex/js. Tutti i
messaggi sono nel namespace docex.*, versionati, exact-origin. Prova il flusso
completo nella pagina demo.
Webhook
Ricevi una notifica quando un'estrazione termina, senza fare polling. Consegna at-least-once con backoff esponenziale (5 s → 30 s → 2 m → 10 m → 1 h, 6 tentativi).
curl -X POST $BASE/v1/webhook-endpoints \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"url": "https://app.yourcompany.com/hooks/docsense",
"events": ["extraction.completed", "extraction.failed"]}'
# → { "id": "whe_…", "secret": "whsec_…" } (secret mostrato una sola volta)
Ogni richiesta porta gli header webhook-id, webhook-timestamp e
webhook-signature (schema compatibile svix). Verifica la firma prima di fidarti:
# python
import base64, hmac, hashlib
def verify(secret, headers, body: bytes) -> bool:
key = base64.b64decode(secret.removeprefix("whsec_"))
signed = f'{headers["webhook-id"]}.{headers["webhook-timestamp"]}.'.encode() + body
expected = base64.b64encode(hmac.new(key, signed, hashlib.sha256).digest()).decode()
got = [s.split(",", 1)[1] for s in headers["webhook-signature"].split() if s.startswith("v1,")]
return any(hmac.compare_digest(expected, g) for g in got)
Payload: {"type": "extraction.completed", "created_at": …, "data": …} dove
data è lo stesso body restituito da GET /v1/extractions/{id}.
I tentativi di consegna sono ispezionabili su GET /v1/webhook-endpoints/{id}/deliveries.
Consumi e fatturazione
Due contatori, per pagina: pages_basic (valore + testo grezzo + affidabilità +
verifiche) e pages_grounded (aggiunge le locations per campo).
Il widget conta sempre grounded. Controlla i totali in ogni momento con
GET /v1/usage. Le chiavi in modalità test (sk_test_…) non vengono
mai fatturate.
Docsense · elaborazione solo in UE ·
riferimento OpenAPI ·
demo widget
© 2026 iBill S.r.l. con Socio Unico · Via dei Castani, 144 – 00172 Roma