Documentación de la API de Fileverity
Comprobaciones de documentos en un flujo de recepción
Añada comprobaciones de documentos a un flujo de recepción: suba el archivo, reciba el webhook, derive cada caso según el nivel de riesgo, ponga la revisión humana en cola, guarde el ID del informe y verifíquelo después.
Esta guía práctica añade comprobaciones de documentos antes de un proceso que recibe documentos de fuentes externas: solicitudes de préstamo, siniestros de seguros, alta de proveedores o informes de gastos. Cada documento se comprueba una vez, se deriva según su nivel de riesgo y su ID de informe se conserva como registro de auditoría. La API devuelve un nivel y una acción recomendada. La decisión la toman su política y sus revisores.
Usa el modelo fvdoc-3 con las llamadas de Archivos, Análisis y Webhooks. Con una clave de prueba, la subida y la comprobación devuelven un ejemplo al instante, pero no se envía ningún webhook y no hay PDF de ejemplo. Use Enviar prueba en un endpoint de webhook de la Consola para probar su receptor.
El flujo
- Suba el archivo con
POST /filese inicie la comprobación conPOST /analyses. Ponga el ID de su caso enmetadatay envíe unaIdempotency-Key. - Responda a su usuario de inmediato. Los documentos pueden quedar en cola, así que no mantenga abierta su solicitud; el webhook le avisa cuando termina la comprobación.
- Reciba
analysis.completedoanalysis.failed, verifique la firma y ponga un trabajo en cola. - En el trabajo, obtenga el análisis, derívelo según su resumen y guarde el resultado y el informe en PDF con el caso.
- Envíe a una cola de revisión, con el PDF adjunto, los casos que necesiten a una persona.
- Más tarde, verifique el informe con el archivo almacenado.
1. Subir el archivo e iniciar la comprobación
Los archivos de hasta 4 MB se suben como multipart. Para archivos más grandes, de hasta 50 MB, use una subida prefirmada como se describe en Archivos. Al crear el análisis se devuelve 202 con status: processing (una clave de prueba devuelve 200 con un ejemplo terminado), y su metadata vuelve en el análisis y en cada webhook correspondiente.
FILE_ID=$(curl -s https://api.fileverity.com/v1/files \ -H "Authorization: Bearer $FILEVERITY_API_KEY" \ -H "Idempotency-Key: intake-A-10492-file" \ -F file=@application.pdf | jq -r .id) curl https://api.fileverity.com/v1/analyses \ -H "Authorization: Bearer $FILEVERITY_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: intake-A-10492-check" \ -d "{\"model\": \"fvdoc-3\", \"file\": \"$FILE_ID\", \"metadata\": {\"case_id\": \"A-10492\"}}"import { readFile } from "node:fs/promises";import { basename } from "node:path"; const base = "https://api.fileverity.com/v1";const auth = { Authorization: "Bearer " + process.env.FILEVERITY_API_KEY }; async function call(path, init) { const response = await fetch(base + path, init); const body = await response.json(); if (!response.ok) throw new Error(body.error.code + " (" + body.error.request_id + ")"); return body;} export async function submit(caseId, path) { const form = new FormData(); form.append("file", new Blob([await readFile(path)]), basename(path)); const file = await call("/files", { method: "POST", headers: { ...auth, "Idempotency-Key": `intake-${caseId}-file` }, body: form, }); return call("/analyses", { method: "POST", headers: { ...auth, "Content-Type": "application/json", "Idempotency-Key": `intake-${caseId}-check` }, body: JSON.stringify({ model: "fvdoc-3", file: file.id, metadata: { case_id: caseId } }), });}import osimport requests BASE = "https://api.fileverity.com/v1"HEADERS = {"Authorization": "Bearer " + os.environ["FILEVERITY_API_KEY"]} def call(method, path, **kwargs): headers = {**HEADERS, **kwargs.pop("headers", {})} response = requests.request(method, BASE + path, headers=headers, timeout=90, **kwargs) body = response.json() if not response.ok: raise RuntimeError(body["error"]["code"] + " (" + body["error"]["request_id"] + ")") return body def submit(case_id, path): with open(path, "rb") as source: file = call("POST", "/files", headers={"Idempotency-Key": f"intake-{case_id}-file"}, files={"file": source}) return call( "POST", "/analyses", headers={"Idempotency-Key": f"intake-{case_id}-check"}, json={"model": "fvdoc-3", "file": file["id"], "metadata": {"case_id": case_id}}, ){ "id": "an_8Hk2pQ4vN9sL1xR7cW3mY6zB", "object": "analysis", "created": 1793700120, "model": "fvdoc-3-2026-11-03", "livemode": true, "status": "processing", "file": { "name": "application.pdf", "bytes": 18874368, "format": "pdf" }, "report_id": null, "report_url": null, "summary": null, "result": null, "error": null, "progress": { "stage": "queued", "done": 0, "total": 0, "position": 1 }, "usage": { "amount": 599, "currency": "usd", "funding": "balance" }, "metadata": { "case_id": "A-10492" }, "completed_at": null, "expires_at": null}Guarde id con el caso. Como las claves se derivan del ID del caso, volver a ejecutar submit tras un tiempo de espera agotado devuelve las primeras respuestas en lugar de subir un segundo archivo o cobrar una segunda comprobación. Las claves se conservan 24 horas; consulte Idempotencia.
2. Recibir el resultado
Verifique la firma con la función verify de Webhooks, guardada como verify.js o verify.py. Responda 2xx con rapidez y haga el procesamiento en un trabajo. enqueue representa su propia cola de trabajos. Guarde cada trabajo bajo el ID del evento, para que un envío duplicado no cree un segundo trabajo.
import express from "express";import { verify } from "./verify.js"; const app = express(); app.post("/webhooks/fileverity", express.raw({ type: "application/json" }), async (req, res) => { let event; try { event = verify(process.env.FILEVERITY_WEBHOOK_SECRET, req.headers, req.body.toString("utf8")); } catch { return res.sendStatus(400); } if (event.type === "analysis.completed" || event.type === "analysis.failed") { await enqueue({ eventId: event.id, analysisId: event.data.id }); } res.sendStatus(200);});import os from fastapi import FastAPI, HTTPException, Request from verify import verify app = FastAPI() @app.post("/webhooks/fileverity")async def receive(request: Request): body = await request.body() try: event = verify(os.environ["FILEVERITY_WEBHOOK_SECRET"], request.headers, body) except (KeyError, ValueError): raise HTTPException(status_code=400) if event["type"] in ("analysis.completed", "analysis.failed"): enqueue(event["id"], event["data"]["id"]) return {"ok": True}3. Derivar según el nivel de riesgo
summary es el veredicto estable y legible por máquinas de una comprobación de documento. level va de L1 a L5 y action es el siguiente paso recomendado. Derive según ambos y trate cualquier valor que no reconozca como un caso para una persona.
| Resumen | Qué significa | Derivación de ejemplo |
|---|---|---|
L1 o L2, accept | No se encontraron indicios de manipulación. L1 significa que se verificó el origen. | Continúe automáticamente. Guarde el ID del informe. |
L1 o L2, note | Sin indicios de manipulación, pero el informe marca texto escrito con IA o una etiqueta de IA. | Continúe y marque el caso si su política limita el contenido escrito con IA. |
L3, review | Requiere revisión manual. | Cola de revisión estándar. |
L4, review | Riesgo alto. | Cola de revisión prioritaria. |
L5, reject | El informe acredita problemas. | Cola de revisión prioritaria. Una persona confirma antes de rechazar nada. |
status es failed o canceled | La comprobación no terminó y no se le cobró. | Suba de nuevo el original almacenado o envíe el caso a una persona. |
La asignación la define su política. Mueva L3 a la cola prioritaria, o haga que note pase a revisión, cambiando la función. El conjunto de acciones también contiene resubmit, que la función trata como una solicitud de otra copia del archivo. Cada campo del resumen se describe en Análisis.
export function route(analysis) { if (analysis.status !== "succeeded") return "manual"; const { action, level } = analysis.summary; if (action === "accept") return "accept"; if (action === "note") return "accept_with_note"; if (action === "resubmit") return "request_new_copy"; if (action === "reject" || level === "L4" || level === "L5") return "priority_review"; return "review";}def route(analysis): if analysis["status"] != "succeeded": return "manual" action = analysis["summary"]["action"] level = analysis["summary"]["level"] if action == "accept": return "accept" if action == "note": return "accept_with_note" if action == "resubmit": return "request_new_copy" if action == "reject" or level in ("L4", "L5"): return "priority_review" return "review"4. Guardar el ID del informe y el PDF
El trabajo obtiene el análisis, lo deriva y guarda lo que necesitará más adelante. Reutiliza call, route y las credenciales de los pasos anteriores, y saveCase y saveReport representan su propio almacenamiento. Descargue el PDF en el mismo trabajo: el resultado del análisis se conserva 7 días, las imágenes de páginas y los extractos de texto de un documento, 3 horas, y después Fileverity conserva solo el registro de verificación, no el informe completo.
export async function handleResult(analysisId) { const analysis = await call(`/analyses/${analysisId}`, { headers: auth }); const decision = route(analysis); await saveCase(analysis.metadata.case_id, { analysisId: analysis.id, reportId: analysis.report_id, reportUrl: analysis.report_url, summary: analysis.summary, model: analysis.model, decision, }); if (analysis.status === "succeeded") { const pdf = await fetch(`${base}/analyses/${analysis.id}/report.pdf`, { headers: auth }); await saveReport(analysis.report_id, Buffer.from(await pdf.arrayBuffer())); } return decision;}def handle_result(analysis_id): analysis = call("GET", f"/analyses/{analysis_id}") decision = route(analysis) save_case(analysis["metadata"]["case_id"], { "analysis_id": analysis["id"], "report_id": analysis["report_id"], "report_url": analysis["report_url"], "summary": analysis["summary"], "model": analysis["model"], "decision": decision, }) if analysis["status"] == "succeeded": pdf = requests.get(f"{BASE}/analyses/{analysis['id']}/report.pdf", headers=HEADERS, timeout=90) pdf.raise_for_status() save_report(analysis["report_id"], pdf.content) return decisionreport_id(FV-XXXX-XXXX-XXXX) es la clave del registro de auditoría. Guárdelo con el caso, en su base de datos y en cualquier carta de decisión.modeles la versión fechada que produjo el resultado, de modo que más adelante pueda indicar con exactitud qué versión comprobó el documento.- Conserve el archivo original en su propio almacenamiento. Fileverity elimina el archivo subido cuando empieza el análisis, y una comprobación fallida requiere una subida nueva. Consulte Conservación de datos y seguridad.
5. Gestionar la cola de revisión humana
Cree una tarea por cada caso derivado. Dé al revisor lo que necesita para decidir sin volver a llamar a la API:
- El ID del caso,
level,action,reasony los ID de hallazgo defindings, con el informe en PDF adjunto. El PDF enumera cada hallazgo con su ubicación y su evidencia. - El archivo original que almacenó.
report_url, la página de verificación pública. Confirma el registro del informe y la huella del archivo, pero no sustituye la lectura del informe.
Registre al revisor, la decisión y la hora en su propio sistema, junto a report_id.
Una comprobación sin hallazgos no prueba que un documento sea auténtico, y un hallazgo no prueba un fraude. No rechace un caso solo por el resultado de la API. En documentos de ingresos, de identidad o de propiedad, confirme con el emisor a través de un contacto que haya encontrado usted mismo. La guía de detección de fraude documental explica qué comprobar según el tipo de documento.
6. Verificar el informe más tarde
Meses después, un auditor o un cliente puede comprobar el registro sin una cuenta de Fileverity. Abra el report_url almacenado. Muestra el tipo de informe, el hallazgo y el siguiente paso registrados, la huella SHA-256 del archivo examinado y la marca de tiempo RFC 3161. Suelte su original almacenado en la página y su navegador calcula su SHA-256 y lo compara con el registro; no se sube nada. Cualquier copia vuelta a guardar, convertida o editada da una huella distinta.
Puede ejecutar la misma comparación en su propio sistema:
sha256sum application.pdfimport { createHash } from "node:crypto";import { readFile } from "node:fs/promises"; const digest = createHash("sha256").update(await readFile("application.pdf")).digest("hex");console.log(digest);import hashlib print(hashlib.sha256(open("application.pdf", "rb").read()).hexdigest())La verificación no vuelve a analizar el archivo ni certifica que un documento sea auténtico. Registra qué archivo se examinó, qué dijo el informe y cuándo. El informe completo solo existe en el PDF que almacenó. Los campos del informe se describen en el objeto de informe.
Antes de pasar a producción
- Desarrolle con una clave de prueba. Devuelve un ejemplo instantáneo, nivel
L2con acciónaccept, sin ejecutar el modelo. Los webhooks y la descarga del PDF requieren una clave Live, que puede probar con su inicio gratuito. Pase los demás resúmenes de Análisis porroutepara probar cada rama. - Gestione
503 service_unavailable: espere los segundos indicados enretry-aftery reintente con la mismaIdempotency-Key. Consulte Cuando el servicio está ocupado. - Fije un límite de gasto mensual en la clave y añada un webhook
balance.low, para que la recepción no se detenga coninsufficient_balance. Consulte Precios y facturación. - Llame a la API desde su servidor y guarde las claves en un gestor de secretos, nunca en código del navegador.
- Indique el valor del encabezado
x-request-idcuando contacte con soporte.