Skip to content

Queue Jobs ​

Background work is processed by the BullMQ worker service (services/worker). The API enqueues jobs; the worker processes them independently.

Queues ​

Queue nameConstantPurpose
invoice-processingQUEUE_NAMES.INVOICE_PROCESSINGOCR extraction for uploaded invoices
email-syncQUEUE_NAMES.EMAIL_SYNCIMAP inbox polling to import invoices
automationQUEUE_NAMES.AUTOMATIONPer-user automation triggers
webhook-deliveryQUEUE_NAMES.WEBHOOK_DELIVERYDeliver webhook payloads to subscriber URLs

Job data interfaces ​

typescript
// invoice-processing
interface InvoiceProcessingJobData {
  invoiceId: string
  tenantId: string
}

// email-sync
interface EmailSyncJobData {
  tenantId: string
}

// automation
interface AutomationJobData {
  tenantId: string
  tenantUserId: string
}

// webhook-delivery
interface WebhookDeliveryJobData {
  webhookId: string
  webhookDeliveryId: string
  tenantId: string
  event: string
  url: string
  secret: string | null
  payload: Record<string, unknown>
}

Invoice processing flow ​

  1. Worker receives InvoiceProcessingJobData from the invoice-processing queue
  2. Downloads the invoice file from RustFS using the presigned URL
  3. Sends the document inline to an AI model via OpenRouter (native PDF/image input, strict JSON-schema output)
  4. The model returns a full transcription plus structured fields (amount, date, invoice number, etc.) in one call
  5. Updates the Invoice record in the database with the extracted fields
  6. Sets invoice.status to processed (or error if any step fails)
  7. Runs a best-effort classification pass (failures are logged, never fail the job):
    • logs the detected document type when it differs from the stored one (never applied — re-typing would desync the per-type numbering counters)
    • on a gasoil invoice with no vehicle, matches org vehicles' license plates against the extracted text and fills vehicleId on an unambiguous single hit
    • flags a probable duplicate (duplicateOfId) when another org invoice shares the vendor and either the invoice number or the date + TTC amount; the admin can dismiss it in the review drawer

Webhook delivery flow ​

  1. An API event (e.g. entry.approved) fires — the webhook service looks up active subscriptions for the event
  2. Creates a WebhookDelivery record and enqueues a webhook-delivery job
  3. Worker POSTs the payload to the subscriber's URL
  4. If a secret is set, signs the payload with HMAC-SHA256 and includes the signature in the X-Signature-256 header
  5. Updates WebhookDelivery with the response status and duration

Retry behaviour ​

Jobs are retried with exponential backoff on failure. Failed jobs remain in the queue's failed list and can be inspected or retried via the BullMQ dashboard (if configured).

TT Time Tracker — Internal Documentation