Skip to main content
A Document represents a file (PDF, image, or other binary) uploaded to or ingested into a workspace. It is the raw file substrate for the invoice extraction pipeline — every Invoice may reference one Document, and every Transaction may be linked to at most one active Document via the TransactionDocument junction. Documents arrive either through direct user upload (multipart POST to /v1/documents), ambient capture from email connectors, or connector sync (tracked via DocumentWorkspaceConnector with a direction of input or output). The entity carries GCS storage coordinates (bucket, path), MIME type, file size, a content-fingerprint checksum for cross-format deduplication, and an AI-classified document type code drawn from the UN/EDIFACT document type taxonomy.

API operations

Data model

Attributes

Relationships

System-computed

  • document_id is generated by gen_random_uuid() at INSERT time and is the public API identifier. The internal pk (auto-increment integer) is never exposed.
  • created_at is set once via MikroORM onCreate: () => new Date() and never changed thereafter.
  • updated_at is set by both onCreate and onUpdate hooks, so it reflects the latest ORM flush against this row.
  • deleted_at is the soft-delete sentinel. Active queries must filter deleted_at IS NULL. No physical row deletion occurs; the column is set to the deletion timestamp.
  • content_checksum is computed by the upload service after stripping format-specific metadata bytes (EXIF for JPEG, metadata chunks for PNG, /Info+date+ID for PDF), then taking SHA-256. It equals the raw file SHA-256 for unsupported types. The partial unique index idx_documents_workspace_content_checksum_active enforces per-workspace L2 deduplication: (workspace_pk, content_checksum) WHERE deleted_at IS NULL AND content_checksum IS NOT NULL.
  • document_type is classified by the AI enrichment pipeline. The @Enrichable decorator marks this field for the enrichment worker. Until classification completes, the field is NULL. normalizeDocumentTypeCode() maps unrecognised values to DocumentTypeCodeEnum.OTHER (999).
  • uploaded_at is set by the caller (upload controller or connector sync) and may represent the file’s original creation time from the provider rather than the time of ingest into Well.
  • The partial index idx_documents_workspace_created_active (workspace_pk, created_at DESC WHERE deleted_at IS NULL) optimises the default records-page sort for the documents root.
  • sourceWorkspaceConnector carries ingestion provenance. NULL means user-originated upload; non-NULL means the document was created by a connector sync flow.
  • DocumentWorkspaceConnector junction rows are appended (never mutated) to track each connector that ingested (direction=input) or received (direction=output) the document. A single Document can have multiple DWC rows from different connectors. Note: the inverse collection is NOT declared on the Document entity — DWC rows are accessed via the DocumentWorkspaceConnector repository, not via a collection on Document.
  • DocumentExtraction rows (one per document × parser pair) cache parsed text output to avoid redundant LlamaParse / LiteParse calls. The partial unique index uniq_document_extraction_per_parser_checksum on (document_pk, parser_name, parser_version, COALESCE(source_checksum, ”)) WHERE deleted_at IS NULL enforces one extraction row per parser version per document content state.
  • DocumentStructuredExtraction rows extend the extraction pipeline with structured field output beyond raw text (see Migration20260528120000_document_structured_extractions).

Example

Source: apps/api/src/database/entities/Document.ts · domain: ingestion · tier: Main