bounding_boxes) dentro del PDF original. Lo usas para construir UIs auditables: al mostrar un dato extraído, resaltas la región del documento de donde Trébol lo extrajo. Trébol genera el artifact cuando termina la verificación y te entrega una URL firmada para descargarlo.
Endpoints con soporte de citations
| Endpoint | Sección/Tipo | Dónde aparece citations | Paths de ejemplo |
|---|---|---|---|
GET /v2/verifications/{id}/people | people | data.citations.url (único artifact) | key_people[0].roles[0].role_name, signatory_groups.executives[0].names |
GET /v2/companies/{tag}/people | people | data.citations.url (único artifact) | ídem |
GET /v2/verifications/{id}/shareholders | shareholders | data.citations.url (único artifact) | shareholders[0].name, shareholders[0].id_number, shareholders[0].fixed_value |
GET /v2/companies/{tag}/shareholders | shareholders | data.citations.url (único artifact) | ídem |
GET /v2/verifications/{id}/sources | sources (acta) | data.sources[i].citations.url (por item) | notary_info.notaryName, corporate_purpose.object, assembly_minute.resolutions, shareholders[0].name, board_members[0].name |
GET /v2/companies/{tag}/sources | sources (acta) | data.sources[i].citations.url (por item) | ídem |
GET /verifications/{id} | items de acta | items[i].citations.url (por item de acta) | ídem |
GET /verification-items/{id} | item de acta | citations.url (en el objeto raíz) | ídem |
Cómo activarlo
Agrega el query parameter?with_citations=true al endpoint de la sección. El response añade un campo data.citations con la URL firmada al JSON. Tienes dos rutas equivalentes: por verification_id o por etiqueta.
- people
- sources (acta)
Estructura del artifact
El artifact es un JSON con un schema versionado. Hoy la versión activa escitations/v1.
Top level
| Campo | Tipo | Descripción |
|---|---|---|
schema_version | string | Versión del schema. Actualmente "citations/v1". |
verification_id | string | ID de la verificación a la que pertenece el artifact. |
section | string | Sección citada: "people", "shareholders" o "acta". Los artifacts de sources tienen section: "acta" y además incluyen item_id. |
fields | Field[] | Lista plana de fields citados con sus coordenadas. |
unresolved | UnresolvedField[] | Fields que Trébol extrajo pero no logró mapear a coordenadas. Vacío en el caso normal. Cada objeto trae path y value, pero sin bounding_boxes. |
item_id | number | (Solo artifacts de sources) ID del item al que pertenece el artifact. Corresponde al id del source en el response de la sección. |
Objeto Field
| Campo | Tipo | Descripción |
|---|---|---|
path | string | Ruta del field en el response de la sección, en notación de punto + índices (ej. key_people[3].roles[0].powers[0].power_name, shareholders[0].id_number). El índice refleja la posición en el array del endpoint — misma posición que el UI renderiza. |
value | string | number | boolean | Valor extraído. Coincide con el valor en el response de /people. |
source | Source | Metadata del documento fuente: item_id, item_type, document_date, document_number. |
bounding_boxes | BoundingBox[] | Una o más coordenadas. Un mismo field puede tener varios bboxes si la información aparece en distintas partes del documento. |
Objeto BoundingBox
| Campo | Tipo | Descripción |
|---|---|---|
page | number | Página del PDF (1-indexed). |
vertices | [number, number][] | 4 puntos [x, y] con coordenadas normalizadas en [0, 1] respecto al tamaño de la página renderizada. Origen top-left (y crece hacia abajo). |
text | string | Snippet de texto OCR del bbox. Útil para mostrar contexto sin renderizar el PDF. |
Ejemplos
- people
- sources (acta)
value refleja el valor extraído originalmente del documento. Para people, algunos fields usan valores centinela: duration se expresa en años y -1 significa cargo indefinido.
Cómo manejar unresolved
Cuando Trébol extrae un dato pero no logra ubicarlo en el PDF (texto re-flujado, tablas complejas, OCR de baja confianza), el field cae en unresolved en vez de fields. Estos objetos traen path y value, pero no bounding_boxes.
En tu UI, muestra el valor normalmente y omite el botón de “ver cita” para esos fields. No los escondas: el dato sigue siendo válido, solo no tiene coordenadas que resaltar.
Cómo implementarlo en tu UI
Obtener el PDF original
El artifact te da las coordenadas, pero no el PDF. Cadafield trae source.item_id: ese es el documento del que salió el dato. Para resaltar la cita necesitas el PDF de ese mismo item_id.
La URL del PDF vive en el response de la sección, no en el artifact. Para people, cada objeto source dentro del response incluye item_id y document_url. Para shareholders, la URL del PDF del acta está en data.source.document_url (un único documento fuente para todos los accionistas). Para sources, cada item del array expone document_url directamente — ese es el PDF del acta. El artifact de ese item trae item_id en el nivel raíz: úsalo para identificar el item correcto cuando hay múltiples actas; no cruces por field.source.item_id dentro del artifact (todos los fields de un artifact de acta pertenecen al mismo documento).
document_url también es una URL firmada que caduca en 1 hora. Resuélvela en el momento de renderizar, no la persistas. Si está vacía o null, ese item_id no tiene PDF disponible para citar.Receta de renderizado
La receta que recomendamos: usa el componentePDFViewer de pdfjs-dist (no pdf.js core directo) y monta un <canvas> overlay como hijo del div que el viewer crea para cada página. Dibuja el polígono con moveTo/lineTo sobre los 4 vértices reales. Esto te da text layer, scroll, virtualización y zoom sin escribir código.
Arquitectura mínima
Cargar pdfjs-dist y setear el worker
Importa
pdfjs-dist y asigna GlobalWorkerOptions.workerSrc para que el worker quede disponible.Instanciar PDFViewer
Crea un
EventBus y un PDFViewer apuntando a un contenedor con position: absolute (requerido por el viewer).Al seleccionar un field, dibuja el bbox
Toma el
bbox, multiplica cada vértice por pageView.viewport.width/height, dibuja el polígono en un <canvas> agregado como hijo de pageView.div y scrollea con viewer.scrollPageIntoView().Ejemplo funcional
Copia este snippet a un archivo local, reemplazaPDF_URL y ARTIFACT_URL por valores reales, y ábrelo en el navegador.
Detalles importantes de integración
- Worker de pdf.js: con bundlers modernos usa
new URL("pdfjs-dist/build/pdf.worker.mjs", import.meta.url).toString(). Sin bundler, sirvepdf.worker.mjsdesde tu propio host o desde un CDN. - CSS del viewer: importa
pdfjs-dist/web/pdf_viewer.csso las páginas se ven sin estilo. - Coordenadas normalizadas: multiplica
nx * viewport.widthyny * viewport.height. No usesviewport.convertToViewportPoint: asume coordenadas en espacio PDF (bottom-left) y aquí las coordenadas son top-left normalizadas. bbox.pagees 1-indexed, peroviewer.getPageView()espera 0-indexed. Resta 1.- Zoom: el overlay vive en píxeles fijos. Sin escuchar
scalechangingy redibujar, el highlight se desfasa. - Polígonos rotados: dibuja siempre como polígono (
moveTo+lineTo+closePath), no comorectaxis-aligned. - Páginas virtualizadas:
PDFViewerno renderiza todas las páginas hasta que entran al viewport. SigetPageView()devuelveundefined, llama primeroscrollPageIntoViewy espera al eventopagerendered. - CORS: el bucket del PDF y el del artifact deben permitir el origen de tu app. Si tu backend hace fetch del artifact y lo sirve a tu front, este punto desaparece.
Siguientes pasos
Leer una verificación
Cómo consumir el endpoint
/people y otras secciones de la verificación.Webhooks
Suscríbete a
verification.v2.finished para saber cuándo el artifact está listo.Tipos de item
Qué items alimentan la sección
people y, por tanto, generan citas.