Integrations Framework Implementation Guide

Task 41 turns the integrations layer into a reusable framework instead of a collection of one-off partner hooks.

2 min read

Purpose

Task 41 turns the integrations layer into a reusable framework instead of a collection of one-off partner hooks.

The implementation now provides:

  • adapter registration in @tov-plus/integrations
  • connection lifecycle and credential redaction
  • scoped integration principals
  • planner management APIs and UI
  • admin visibility APIs and UI
  • inbound callback handling
  • worker-driven background sync execution
  • migration artifacts for durable SQL authoring

Adapter model

Adapters are registered in packages/integrations/src/contracts/integration.ts.

Each adapter declares:

  • identity: integration_key, name, description, category, direction, mode
  • supported capabilities
  • supported action keys
  • config fields
  • secret fields
  • whether background sync and inbound callbacks are supported
  • validation rules
  • optional sync implementation
  • optional inbound callback mapping

The framework currently ships two example adapters:

  • printing_partner
  • venue_ops

printing_partner is the primary proof-of-framework adapter. It supports validation, background sync, inbound callbacks, redacted credentials, and planner-managed lifecycle.

Connection lifecycle

Planner lifecycle routes:

  • GET /v1/occasions/:occasion_id/integrations
  • POST /v1/occasions/:occasion_id/integrations/connections
  • PATCH /v1/occasions/:occasion_id/integrations/connections/:connection_id

Lifecycle states:

  • draft
  • enabled
  • disabled
  • revoked

Validation behavior:

  • adapters validate config, secrets, and scope
  • invalid connections may exist as draft
  • enabled is blocked when validation has errors
  • degraded health is surfaced when configuration drift exists

Principal and scope model

Each connection gets a generated integration principal:

  • principal_type = integration_principal
  • subject_id = integration:<connection_id>
  • unique key_id
  • explicit scope with platform, occasion, or event
  • explicit allowed action_keys

The server creates direct grants for the integration principal during connection creation so the permission model stays explicit and auditable.

Credential handling

The current runtime keeps raw secrets only in the shared in-memory integration store and returns only redacted summaries from APIs and UI payloads.

Current baseline:

  • raw secrets are never returned from planner/admin APIs
  • UI surfaces display only redacted_secrets
  • secret rotation creates a new key_id
  • last-used metadata is tracked for resolved integration keys
  • inbound callbacks validate x-tov-integration-secret

The SQL migration adds the durable table structure needed for future encrypted-at-rest persistence.

Worker handoff

The worker now consumes the same shared integration store as the server bootstrap seam.

Background flow:

  1. enabled pull-sync connections are discovered by integrationSyncScheduler
  2. the scheduler enqueues integrations.sync_partner_state
  3. integrationSyncHandler resolves the adapter
  4. adapter sync updates health and last_sync_at
  5. activity is recorded and integration.sync_completed webhook events are emitted

Planner and admin surfaces

Planner:

Admin:

Inbound and outbound interaction seams

Inbound:

  • POST /v1/integrations/callbacks/:integration_key/:connection_id
  • adapter validation and payload mapping
  • shared-secret validation
  • activity recording

Outbound/background:

  • adapter syncConnection
  • worker retries through the normal job framework
  • webhook emission for sync completion

Future extension points

The next task-specific integrations should plug into the existing seams rather than bypass them:

  • add a new adapter definition
  • add adapter validation
  • add sync/callback mapping
  • extend UI labels or helper copy only if needed
  • keep connection persistence generic unless the data is truly adapter-specific