Documentation Index
Fetch the complete documentation index at: https://docs.wellapp.ai/llms.txt
Use this file to discover all available pages before exploring further.
EnrichmentTask is the durable work-item record for Well’s asynchronous enrichment pipeline. Each row represents one unit of background AI or data-processing work — logo resolution, AI company/people/invoice extraction, OCR, bank-sync reconciliation, provider scoring, custom column compute, field rule evaluation, or monthly invoice closure — targeted at an arbitrary entity identified by the polymorphic (entity_type, entity_id) pair. Tasks are scoped to a Workspace, can be grouped into a batch_id (one Magic-button press), and support parent/subtask nesting via a self-referencing parent_task relationship. The enrichment pipeline writes and advances all lifecycle states; users and operators observe tasks read-only.
| Naming | Value |
|---|
| Object | EnrichmentTask |
Resource type (JSON:API type) | enrichment_task |
| Collection / records root | — (not a records root) |
| REST base | /v1/enrichment-tasks |
| Entity class | EnrichmentTask |
Internal object. Not currently exposed on the public REST API. The operations below describe the intended contract.
API operations
| Operation | Method & path | Status |
|---|
| List | GET /v1/enrichment-tasks | 🟡 Planned |
| Retrieve | GET /v1/enrichment-tasks/{id} | 🟡 Planned |
| Create | POST /v1/enrichment-tasks | 🟡 Planned |
| Update | PATCH /v1/enrichment-tasks/{id} | 🟡 Planned |
| Delete | DELETE /v1/enrichment-tasks/{id} | 🟡 Planned |
Data model
Attributes
| Field | Type | Required | Constraints | Allowed values | Description |
|---|
| enrichment_task_id | UUID (🔒 system) | ✅ Yes | UNIQUE | — | Public stable identifier. Auto-generated by gen_random_uuid() at insert. This is the id exposed in JSON:API responses; the internal pk is never surfaced. |
| entity_type | string (varchar 100) | ✅ Yes | max 100 chars; NOT NULL; composite index with entity_id (enrichment_tasks_entity_idx) | — | Polymorphic discriminator identifying the kind of entity this task targets (e.g. company, person, invoice, document, transaction). Pairs with entity_id to form the generic entity reference introduced in Migration20260323100000 to replace per-type FK columns. |
| entity_id | string (varchar 255) | ✅ Yes | max 255 chars; NOT NULL; composite index with entity_type; partial unique index on (entity_type, entity_id, enrichment_type) WHERE status = 'pending' AND deleted_at IS NULL AND enrichment_type = 'monthly_close' enforces one active monthly_close task per target month | — | Public UUID of the target entity (the *_id column of the referenced row, e.g. company_id, person_id, invoice_id). Stored as a string to support multiple entity types without per-type FK constraints. Composite index with entity_type. |
| status | enum (🔒 system) — native Postgres type enrichment_task_status_enum | ✅ Yes | DEFAULT ‘pending’; NOT NULL | pending | processing | completed | awaiting_approval | failed | rejected | Lifecycle state of the enrichment task. Default pending. Transitions are driven exclusively by the enrichment pipeline workers; users cannot write this field. |
| enrichment_type | string (text) — backed by Postgres type enrichment_type_enum historically but stored as text on the column | ✅ Yes | NOT NULL | logo | ai_company | ai_people | ai_invoice | ocr_extract | reconcile | provider_score | document_reconciliation_backfill | monthly_close | monthly_close_backfill | custom_column | field_rule | Identifies the enrichment worker that should process this task. Determines which Cloud Tasks handler is dispatched. See EnrichmentTypeEnum for the full controlled vocabulary. |
| input | jsonb | ⚪ No | nullable | — | Worker input payload. Shape is worker-specific. For monthly_close the dedup key is input->>'record_id' (month label); a JSONB expression partial index (idx_enrichment_tasks_input_record_id) covers lookups on that path within a workspace + status window. |
| output | jsonb | ⚪ No | nullable | — | Worker output payload written on task completion. Shape is worker-specific. For monthly_close_backfill, holds aggregate fan-out counts and request parameters. |
| target_fields | jsonb (string[]) | ⚪ No | nullable | — | Optional list of field names the enrichment worker should populate or re-evaluate. Used by custom_column and field_rule workers to scope work to a subset of columns. |
| error | text | ⚪ No | nullable | — | Error message or stack trace written by the worker when the task transitions to failed. Null on success. |
| source_channel | enum — native Postgres type source_channel_enum | ⚪ No | nullable | company_create | person_create | document_upload | ocr_completion | bank_sync | email_import | mcp_hub | cell_edit | manual | Records which product surface triggered the task. Used for attribution and observability. Added in Migration20260319120000. |
| input_hash | text | ⚪ No | nullable; index enrichment_tasks_input_hash_idx | — | Deduplication fingerprint of the input payload. Allows workers to detect duplicate enqueues with identical inputs and skip redundant work. Added in Migration20260319120000. |
| description | text | ⚪ No | nullable | — | Human-readable markdown summary of the enrichment task, populated by the worker or the pipeline orchestrator for display in the UI. Added in Migration20260327100000. |
| batch_id | UUID | ⚪ No | nullable; index enrichment_tasks_batch_id_idx; composite index idx_enrichment_tasks_batch_status on (batch_id, status) | — | Groups tasks triggered by a single user action (Magic button press). All tasks sharing a batch_id were enqueued together and can be tracked collectively. Index idx_enrichment_tasks_batch_status covers hot-path batch completion detection. Added in Migration20260327100000. |
| created_at | timestamptz (🔒 system) | ✅ Yes | NOT NULL; set once on insert | — | Row creation timestamp set by MikroORM onCreate hook. Not writable after insert. |
| updated_at | timestamptz (🔒 system) | ⚪ No | nullable; updated on every write | — | Last modification timestamp, maintained automatically by MikroORM onUpdate hook on every flush. |
| deleted_at | timestamptz | ⚪ No | nullable | — | Soft-delete timestamp. When set the row is logically deleted and filtered out of standard queries. The partial unique index on monthly_close tasks is scoped to deleted_at IS NULL. |
| completed_at | timestamptz | ⚪ No | nullable | — | Timestamp written by the worker when the task reaches completed, failed, or rejected. Distinct from updated_at to allow precise pipeline latency measurement. |
Relationships
| Name | Type | Required | Description |
|---|
| workspace | to-one (ManyToOne) | ✅ Yes | The workspace that owns this enrichment task. All task queries are tenant-scoped via this relationship. deleteRule: cascade — deleting a workspace removes all its tasks. FK column workspace_pk. |
| parent_task | to-one (ManyToOne, self-referencing) | ⚪ No | Optional reference to a parent EnrichmentTask. Used by the Monthly Invoice Closure pipeline to link per-counterparty sub-tasks to their macro parent task. FK column parent_task_pk. deleteRule: set null — deleting a parent task nullifies the FK on children without cascading deletion. Added in Migration20260327100000. |
| subtasks | to-many (OneToMany, self-referencing) | — | Inverse collection of child EnrichmentTask rows that reference this task as their parent_task. Populated for macro tasks in the Monthly Invoice Closure pipeline. Mapped by parent_task on the child side. |
System-computed
- enrichment_task_id — auto-generated by gen_random_uuid() Postgres default at INSERT; never set by application code
- created_at — set by MikroORM onCreate hook; never writable after insert
- updated_at — set and maintained by MikroORM onUpdate hook on every flush
- deleted_at — soft-delete; set by the pipeline or admin tooling, never by user PATCH
- status — default ‘pending’ at creation; all state transitions (pending → processing → completed / failed / rejected / awaiting_approval) are driven exclusively by enrichment pipeline workers via Cloud Tasks handlers
- input_hash — computed by the enqueuing service as a fingerprint of the input payload for dedup detection; not computed by the database
- batch_id — assigned at enqueue time by the orchestrator when multiple tasks are triggered together (Magic button); not user-assignable
- completed_at — written by the worker on task terminal state; not set by application business logic outside the worker
- entity_type / entity_id — set at enqueue time from the triggering entity’s public UUID; the generic polymorphic reference replaced per-type FK columns (company_pk, person_pk, document_pk, invoice_pk, transaction_pk) in Migration20260323100000
- subtasks collection — populated by ORM from the OneToMany inverse of parent_task; not a stored column
Example
{
"data": {
"type": "enrichment_task",
"id": "c3a1f9e2-4b7d-4e2a-8f1c-9a0b3d5e7f21",
"attributes": {
"enrichment_task_id": "c3a1f9e2-4b7d-4e2a-8f1c-9a0b3d5e7f21",
"entity_type": "company",
"entity_id": "a1b2c3d4-0000-0000-0000-000000000001",
"status": "completed",
"enrichment_type": "ai_company",
"input": { "company_id": "a1b2c3d4-0000-0000-0000-000000000001" },
"output": { "domain": "acme.com", "registered_name": "Acme Corp" },
"target_fields": ["domain", "registered_name"],
"error": null,
"source_channel": "company_create",
"input_hash": "sha256:abc123",
"description": null,
"batch_id": "f7e6d5c4-3b2a-1908-7654-321098fedcba",
"created_at": "2026-05-15T10:30:00.000Z",
"updated_at": "2026-05-15T10:31:05.000Z",
"deleted_at": null,
"completed_at": "2026-05-15T10:31:05.000Z"
},
"relationships": {
"workspace": {
"data": { "type": "workspace", "id": "ws-uuid-0001" }
},
"parent_task": {
"data": null
}
}
}
}
Source: apps/api/src/database/entities/EnrichmentTask.ts · domain: ingestion · tier: Infrastructure