# diwenne/openreply architecture

> Architecture diagram of the GitHub repository diwenne/openreply, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-08-02). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/diwenne/openreply
- Repository: https://github.com/diwenne/openreply

## Overview

#### Purpose and Runtime Shape
OpenReply is a self-hosted Instagram comment-to-DM automation SaaS. It receives official Meta webhooks, matches comments against configured campaigns, queues delivery, and sends private/public replies through Meta APIs. The Next.js application serves dashboard, public pages, OAuth, and HTTP endpoints; a separate BullMQ worker performs durable sending. Entry points: `app/api/webhook/route.ts`, `worker/dm-worker.ts`, and `app/(dashboard)/layout.tsx`.

#### Web Application and API Boundary
Next.js 16 App Router provides authenticated operational UI plus route handlers. Dashboard pages manage campaigns, automations, inbox, logs, settings, diagnostics, and overview; public pages include shared reports, templates, tracking redirects, and legal/SEO content. Key paths: `app/(dashboard)/campaigns/[id]/page.tsx`, `app/api/dashboard/stats/route.ts`, `app/r/[slug]/route.ts`.

#### Identity, Workspaces, and Account Connection
Auth.js uses email magic links via Resend. Users operate within role-based workspaces (owner/admin/member), invitations, and one or more connected professional Instagram accounts. Meta OAuth/account lifecycle is isolated behind Instagram API routes and Meta modules. Key paths: `app/api/auth/[...nextauth]/route.ts`, `app/api/instagram/connect/route.ts`, `lib/auth.ts`, `lib/workspace-access.ts`, `lib/meta/oauth.ts`.

#### Campaign Definition and Matching
Campaigns bind Instagram posts/reels to keyword triggers and message behavior: personalized DM text, one or two tracked-link buttons, optional public reply, follow gate, follow-up, and targeting/builder fields. The dashboard builder is the primary authoring interface; shared matching logic determines whole-word or partial keyword hits. Key paths: `components/campaign-builder.tsx`, `app/api/automations/route.ts`, `lib/utils/keyword-matcher.ts`, `prisma/schema.prisma`.

#### Event-to-Delivery Flow
Meta calls the webhook endpoint for comment and messaging events. The handler validates/parses events, identifies the Instagram account/workspace and active campaign, filters invalid/self comments, and enqueues matching DM work in Redis. The worker consumes jobs, applies per-account Meta reply limits, performs private reply/public reply/follow-gate interactions, records outcomes, and retries/defers overflow rather than dropping it. Key paths: `app/api/webhook/route.ts`, `lib/meta/webhook.ts`, `lib/queue/dm-worker.ts`, `worker/dm-worker.ts`.

#### Persistence, Queueing, and External Services
PostgreSQL is the system of record through Prisma for users, workspaces, accounts, campaigns, event/send logs, tracked links, reports, and follower history. Redis supports BullMQ delivery and likely cache/import coordination. External boundaries are Meta’s Instagram Login/Graph APIs and webhooks, plus Resend authentication email. Deployments separate web hosting from long-running worker hosting; local infrastructure is defined in `docker-compose.yml`. Key paths: `lib/db/client.ts`, `lib/queue/client.ts`, `lib/meta/client.ts`, `docker-compose.yml`.

#### Reporting, Tracking, and Operations
Tracked links route through a server redirect that records clicks; reporting aggregates campaign/link performance and can expose shareable reports. Scheduled endpoints refresh tokens, attach next-reel targets, and snapshot followers; reconciliation recovers comment-processing gaps. Diagnostics and health endpoints expose operational status, including worker health. Key paths: `lib/tracking/server.ts`, `lib/reports/data.ts`, `app/api/cron/refresh-tokens/route.ts`, `lib/polling/comment-reconciler.ts`, `app/api/admin/diagnostics/route.ts`.

Main entry points: [`lib/db/client.ts`](https://github.com/diwenne/openreply/blob/main/lib/db/client.ts) (PostgreSQL system of record), [`lib/meta/client.ts`](https://github.com/diwenne/openreply/blob/main/lib/meta/client.ts) (Meta Instagram APIs), [`lib/meta/webhook.ts`](https://github.com/diwenne/openreply/blob/main/lib/meta/webhook.ts) (Webhook event processing), [`lib/queue/dm-worker.ts`](https://github.com/diwenne/openreply/blob/main/lib/queue/dm-worker.ts) (DM delivery queue)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_web["Next.js web app"]
  node_dashboard["Dashboard &amp; campaign builder<br/>Next.js UI"]
  node_auth["Auth and workspace access<br/>Auth.js<br/>[auth.ts]"]
  node_auth_route["Auth endpoint<br/>route handler<br/>[route.ts]"]
  node_instagram_connect["Instagram connection<br/>OAuth route<br/>[route.ts]"]
  node_dashboard_stats["Dashboard stats API<br/>route handler<br/>[route.ts]"]
  node_public_redirect["Public tracking route<br/>route handler<br/>[route.ts]"]
end

subgraph group_automation["Automation pipeline"]
  node_webhook{{"Meta webhook endpoint<br/>route handler<br/>[route.ts]"}}
  node_webhook_logic["Webhook event processing<br/>Meta module<br/>[webhook.ts]"]
  node_campaign_matching["Campaign matching<br/>matching logic<br/>[keyword-matcher.ts]"]
  node_delivery_queue["DM delivery queue<br/>BullMQ<br/>[dm-worker.ts]"]
  node_dm_worker["DM worker<br/>worker runtime<br/>[dm-worker.ts]"]
end

subgraph group_data["Data and operations"]
  node_database[("PostgreSQL system of record<br/>PostgreSQL + Prisma<br/>[client.ts]")]
  node_redis[("Redis<br/>queue backend<br/>[client.ts]")]
  node_tracking["Tracked-link redirect<br/>server tracking<br/>[server.ts]"]
  node_reporting["Reporting data<br/>reporting service<br/>[data.ts]"]
  node_maintenance["Reconciliation and scheduled ops<br/>background maintenance"]
  node_infrastructure["Local service infrastructure<br/>Docker Compose<br/>[docker-compose.yml]"]
end

node_meta{{"Meta Instagram APIs<br/>external platform<br/>[client.ts]"}}
node_resend{{"Resend<br/>email provider"}}

node_dashboard -->|"authorizes workspace access"| node_auth
node_auth_route -->|"serves authentication"| node_auth
node_auth -->|"sends magic links"| node_resend
node_dashboard -->|"manages campaigns and accounts"| node_database
node_instagram_connect -->|"OAuth/account lifecycle"| node_meta
node_instagram_connect -->|"stores connections"| node_database
node_dashboard_stats -->|"reads performance"| node_reporting
node_webhook -->|"parses validated events"| node_webhook_logic
node_meta -->|"comment and messaging events"| node_webhook
node_webhook_logic -->|"checks active triggers"| node_campaign_matching
node_campaign_matching -->|"reads campaigns and accounts"| node_database
node_webhook_logic -->|"enqueues matching work"| node_delivery_queue
node_delivery_queue -->|"persists jobs"| node_redis
node_delivery_queue -->|"delivers jobs"| node_dm_worker
node_dm_worker -->|"sends replies and follows"| node_meta
node_dm_worker -->|"records outcomes and limits"| node_database
node_public_redirect -->|"handles tracked links"| node_tracking
node_tracking -->|"records clicks"| node_database
node_reporting -->|"aggregates logs and clicks"| node_database
node_maintenance -->|"reconciles and snapshots"| node_database
node_maintenance -->|"refreshes and recovers"| node_meta
node_infrastructure -.->|"runs locally"| node_database
node_infrastructure -.->|"runs locally"| node_redis

click node_dashboard "https://github.com/diwenne/openreply/blob/main/components/campaign-builder.tsx"
click node_auth "https://github.com/diwenne/openreply/blob/main/lib/auth.ts"
click node_auth_route "https://github.com/diwenne/openreply/blob/main/app/api/auth/%5B...nextauth%5D/route.ts"
click node_instagram_connect "https://github.com/diwenne/openreply/blob/main/app/api/instagram/connect/route.ts"
click node_dashboard_stats "https://github.com/diwenne/openreply/blob/main/app/api/dashboard/stats/route.ts"
click node_webhook "https://github.com/diwenne/openreply/blob/main/app/api/webhook/route.ts"
click node_webhook_logic "https://github.com/diwenne/openreply/blob/main/lib/meta/webhook.ts"
click node_campaign_matching "https://github.com/diwenne/openreply/blob/main/lib/utils/keyword-matcher.ts"
click node_delivery_queue "https://github.com/diwenne/openreply/blob/main/lib/queue/dm-worker.ts"
click node_dm_worker "https://github.com/diwenne/openreply/blob/main/worker/dm-worker.ts"
click node_database "https://github.com/diwenne/openreply/blob/main/lib/db/client.ts"
click node_redis "https://github.com/diwenne/openreply/blob/main/lib/queue/client.ts"
click node_tracking "https://github.com/diwenne/openreply/blob/main/lib/tracking/server.ts"
click node_public_redirect "https://github.com/diwenne/openreply/blob/main/app/r/%5Bslug%5D/route.ts"
click node_reporting "https://github.com/diwenne/openreply/blob/main/lib/reports/data.ts"
click node_maintenance "https://github.com/diwenne/openreply/blob/main/lib/polling/comment-reconciler.ts"
click node_meta "https://github.com/diwenne/openreply/blob/main/lib/meta/client.ts"
click node_infrastructure "https://github.com/diwenne/openreply/blob/main/docker-compose.yml"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_dashboard,node_auth,node_auth_route,node_instagram_connect,node_dashboard_stats,node_public_redirect toneBlue
class node_webhook,node_webhook_logic,node_campaign_matching,node_delivery_queue,node_dm_worker toneAmber
class node_database,node_redis,node_tracking,node_reporting,node_maintenance,node_infrastructure toneMint
class node_meta,node_resend toneNeutral
```

## Components

### Next.js web app

Dashboard, public routes, and HTTP handlers.

- [Dashboard & campaign builder](https://github.com/diwenne/openreply/blob/main/components/campaign-builder.tsx): Next.js UI · Authenticated workspace operations and campaign authoring. · `components/campaign-builder.tsx`
- [Auth and workspace access](https://github.com/diwenne/openreply/blob/main/lib/auth.ts): Auth.js · Magic-link identity and role-based workspace authorization. · `lib/auth.ts`
- [Auth endpoint](https://github.com/diwenne/openreply/blob/main/app/api/auth/%5B...nextauth%5D/route.ts): route handler · `app/api/auth/[...nextauth]/route.ts`
- [Instagram connection](https://github.com/diwenne/openreply/blob/main/app/api/instagram/connect/route.ts): OAuth route · Starts and manages connected Instagram accounts. · `app/api/instagram/connect/route.ts`
- [Dashboard stats API](https://github.com/diwenne/openreply/blob/main/app/api/dashboard/stats/route.ts): route handler · `app/api/dashboard/stats/route.ts`
- [Public tracking route](https://github.com/diwenne/openreply/blob/main/app/r/%5Bslug%5D/route.ts): route handler · `app/r/[slug]/route.ts`

### Automation pipeline

Webhook matching and durable delivery.

- [Meta webhook endpoint](https://github.com/diwenne/openreply/blob/main/app/api/webhook/route.ts): route handler · Validates events and creates matching delivery work. · `app/api/webhook/route.ts`
- [Webhook event processing](https://github.com/diwenne/openreply/blob/main/lib/meta/webhook.ts): Meta module · Resolves account, campaign, and valid comment events. · `lib/meta/webhook.ts`
- [Campaign matching](https://github.com/diwenne/openreply/blob/main/lib/utils/keyword-matcher.ts): matching logic · Matches active post/reel keyword triggers. · `lib/utils/keyword-matcher.ts`
- [DM delivery queue](https://github.com/diwenne/openreply/blob/main/lib/queue/dm-worker.ts): BullMQ · Durable Redis-backed jobs for sends and retries. · `lib/queue/dm-worker.ts`
- [DM worker](https://github.com/diwenne/openreply/blob/main/worker/dm-worker.ts): worker runtime · Applies limits and sends private/public replies and follow gates. · `worker/dm-worker.ts`

### Data and operations

Persistence, tracking, reporting, and maintenance.

- [PostgreSQL system of record](https://github.com/diwenne/openreply/blob/main/lib/db/client.ts): PostgreSQL + Prisma · Stores workspaces, accounts, campaigns, logs, links, reports, and follower history. · `lib/db/client.ts`
- [Redis](https://github.com/diwenne/openreply/blob/main/lib/queue/client.ts): queue backend · Supports BullMQ delivery and coordination. · `lib/queue/client.ts`
- [Tracked-link redirect](https://github.com/diwenne/openreply/blob/main/lib/tracking/server.ts): server tracking · Records clicks before redirecting visitors. · `lib/tracking/server.ts`
- [Reporting data](https://github.com/diwenne/openreply/blob/main/lib/reports/data.ts): reporting service · Aggregates campaign and link performance. · `lib/reports/data.ts`
- [Reconciliation and scheduled ops](https://github.com/diwenne/openreply/blob/main/lib/polling/comment-reconciler.ts): background maintenance · Refreshes tokens and recovers comment-processing gaps. · `lib/polling/comment-reconciler.ts`
- [Local service infrastructure](https://github.com/diwenne/openreply/blob/main/docker-compose.yml): Docker Compose · Defines local PostgreSQL and Redis services. · `docker-compose.yml`

### Other components

- [Meta Instagram APIs](https://github.com/diwenne/openreply/blob/main/lib/meta/client.ts): external platform · Instagram Login, Graph APIs, and webhook source. · `lib/meta/client.ts`
- **Resend**: email provider · Delivers Auth.js magic-link email.

## Connections

- Dashboard & campaign builder → Auth and workspace access: authorizes workspace access
- Auth endpoint → Auth and workspace access: serves authentication
- Auth and workspace access → Resend: sends magic links
- Dashboard & campaign builder → PostgreSQL system of record: manages campaigns and accounts
- Instagram connection → Meta Instagram APIs: OAuth/account lifecycle
- Instagram connection → PostgreSQL system of record: stores connections
- Dashboard stats API → Reporting data: reads performance
- Meta webhook endpoint → Webhook event processing: parses validated events
- Meta Instagram APIs → Meta webhook endpoint: comment and messaging events
- Webhook event processing → Campaign matching: checks active triggers
- Campaign matching → PostgreSQL system of record: reads campaigns and accounts
- Webhook event processing → DM delivery queue: enqueues matching work
- DM delivery queue → Redis: persists jobs
- DM delivery queue → DM worker: delivers jobs
- DM worker → Meta Instagram APIs: sends replies and follows
- DM worker → PostgreSQL system of record: records outcomes and limits
- Public tracking route → Tracked-link redirect: handles tracked links
- Tracked-link redirect → PostgreSQL system of record: records clicks
- Reporting data → PostgreSQL system of record: aggregates logs and clicks
- Reconciliation and scheduled ops → PostgreSQL system of record: reconciles and snapshots
- Reconciliation and scheduled ops → Meta Instagram APIs: refreshes and recovers
- Local service infrastructure → PostgreSQL system of record: runs locally
- Local service infrastructure → Redis: runs locally
