# External Integrations

**Analysis Date:** 2026-05-03

## APIs & External Services

**Autotask PSA:**
- Primary integration — syncs projects, tickets, time entries, contacts, configuration items to Postgres
- SDK/Client: `lib/services/autotask-factory.ts` → `getAutotaskClient()`, `lib/services/autotask-client.ts`
- Auth env vars: `AUTOTASK_API_URL`, `AUTOTASK_USERNAME`, `AUTOTASK_SECRET`, `AUTOTASK_API_INTEGRATION_CODE`
- Webhook secret: `AUTOTASK_WEBHOOK_SECRET` — HMAC-SHA1 verification in `lib/services/webhook-service.ts`
- Sync service: `lib/services/entity-sync.ts` (incremental via `lastTrackedModificationDateTime` when supported)
- Webhook handler: `/api/webhooks/autotask` (public endpoint)

**Microsoft Graph:**
- User/employee engagement data (calendar, presence, mail metrics)
- SDK/Client: `lib/services/msgraph-factory.ts` → `getMsgraphClient()`, `isMsgraphConfigured()`, `lib/services/msgraph-client.ts`
- Auth env vars: `MSGRAPH_CLIENT_ID`, `MSGRAPH_CLIENT_SECRET`, `MSGRAPH_TENANT_ID` (specific tenant, NOT 'common')
- Flow: client_credentials OAuth — no user interaction required
- Sync service: `lib/services/engagement-sync-service.ts`
- API: `/api/engagement/sync` (POST fire-and-forget, GET status), `/api/engagement/summary`, `/api/engagement/users`, `/api/engagement/user/[userId]`
- Scheduler: `engagement-daily` task (disabled by default, 6am UTC)

**Microsoft OAuth (Login):**
- SSO via Microsoft identity
- Auth env vars: `MICROSOFT_CLIENT_ID`, `MICROSOFT_CLIENT_SECRET` (same as above; tenant-aware)
- Integrated into Better Auth 1.4 — `lib/auth.ts` configures `microsoft` social provider
- Account linking enabled: Microsoft OAuth can link to existing accounts

**Datto RMM:**
- Remote device management — sites, devices, alerts
- SDK/Client: `lib/services/datto-rmm-factory.ts` → `getDattoRMMClient()`, `lib/services/datto-rmm-client.ts`
- Auth env vars: `DATTO_RMM_API_URL`, `DATTO_RMM_API_KEY`, `DATTO_RMM_API_SECRET`
- Sync service: `lib/services/datto-rmm-sync-service.ts` (on-demand or scheduled)
- API: `/api/datto-rmm/sync` (public POST, fire-and-forget)
- Alternate simpler client: `lib/services/datto-rmm-client-simple.ts` available

**Veeam Backup & Replication:**
- Backup infrastructure data — sites, repositories, jobs, backup chains
- SDK/Client: `lib/services/veeam-factory.ts` → `getVeeamClient()`, `isVeeamConfigured()`, `lib/services/veeam-client.ts`
- Auth env vars: `VEEAM_VSPC_URL`, `VEEAM_VSPC_API_KEY`
- Shadow mode: `VEEAM_RPO_SHADOW_MODE=true` (default)
- Sync service: `lib/services/veeam-sync-service.ts`
- API: `/api/veeam/sync` (public POST), `/api/veeam/rpo-check` (public POST, RPO health check)

**Auvik:**
- Network monitoring — devices, interfaces, metrics
- SDK/Client: `lib/services/auvik-factory.ts` → `getAuvikClient()`, `lib/services/auvik-client.ts`
- Auth env vars: `AUVIK_API_URL`, `AUVIK_API_USER`, `AUVIK_API_KEY`

**Addigy:**
- Apple device management
- SDK/Client: `lib/services/addigy-factory.ts` → `getAddigyClient()`, `clearAddigyClientCache()`, `lib/services/addigy-client.ts`
- Auth env vars: `ADDIGY_API_URL` (default: `https://api.addigy.com/api/v2`), `ADDIGY_API_TOKEN`, `ADDIGY_ORG_ID` (optional)

**IT Glue:**
- Documentation platform — organizations, configurations, passwords, flexible assets
- SDK/Client: `lib/services/itglue-client.ts` (direct client, no factory)
- Auth env var: `ITGLUE_API_KEY` (x-api-key header)
- Base URL: `https://api.itglue.com`
- Format: JSON:API (application/vnd.api+json)
- Search function for analyzer: `lib/services/analyzer/itglue-search.ts` (redacted output before LLM prompts)
- Sync service: `lib/services/itglue-sync-service.ts`
- API: `/api/itglue/sync` (public POST, fire-and-forget)

**Mimecast:**
- Email security — threat/policy logs
- SDK/Client: `lib/services/mimecast-client.ts` (direct client, no factory)
- Auth env vars: `MIMECAST_CLIENT_ID`, `MIMECAST_CLIENT_SECRET`, `MIMECAST_ACCOUNT_CODE`, `MIMECAST_BASE_URL` (default: `https://api.services.mimecast.com`)

**SentinelOne:**
- EDR/XDR — agents, threats, sites, groups
- SDK/Client: `lib/services/sentinelone-client.ts` (direct client, no factory)
- Auth env var: `S1_API_KEY` or `SENTINELONE_API_KEY`
- Sync service: `lib/services/sentinelone-sync-service.ts`
- API: `/api/sentinelone/sync` (public POST, fire-and-forget)

**Duo Security:**
- 2FA/MFA monitoring — users, phones, auth logs, accounts
- SDK/Client: `lib/services/duo-client.ts` (direct client, no factory)
- Auth env vars: `DUO_IKEY`, `DUO_SKEY`, `DUO_HOST` — HMAC-SHA1 request signing
- Supports both Accounts API (parent) and Admin API (parent + child accounts)
- API: `/api/duo` (public POST; Duo-specific endpoints for sync, data retrieval)

**Zoom:**
- Videoconferencing — meeting analytics, users
- SDK/Client: `lib/services/zoom-factory.ts` → `getZoomClient()`, `isZoomConfigured()`, `lib/services/zoom-client.ts`
- Auth env vars: `ZOOM_ACCOUNT_ID`, `ZOOM_CLIENT_ID`, `ZOOM_CLIENT_SECRET` — Server-to-Server OAuth
- Sync service: `lib/services/zoom-sync-service.ts`
- API: `/api/zoom/sync` (public POST, fire-and-forget)

**QuickBooks Online:**
- Accounting — invoices, payments, deposits, purchases, journal entries
- SDK/Client: `lib/services/qbo-client.ts` (singleton, uses DATABASE_URL)
- Auth env vars: `QBO_CLIENT_ID`, `QBO_CLIENT_SECRET`, `QBO_REALM_ID`
- OAuth2 token management: tokens stored in `qbo_tokens` table (migration 014), auto-refreshed
- Sandbox mode: `QBO_SANDBOX=true` switches to sandbox URL
- Callback: `/api/qbo/auth` (public POST, OAuth redirect), `/api/qbo/disconnect` (public POST)
- Sync service: `lib/services/qbo-sync-service.ts`
- API: `/api/qbo/sync` (public POST, fire-and-forget)

**Zabbix:**
- Infrastructure monitoring — events, problems, hosts, metrics
- SDK/Client: `lib/services/zabbix-client.ts` (direct client, no factory)
- Auth env vars: `ZABBIX_API_URL`, `ZABBIX_API_TOKEN`
- Webhook handler: `/api/zabbix/webhook` (public POST)

**SalesBldr:**
- Sales engagement platform
- SDK/Client: `lib/services/salesbldr-client.ts` (direct client, no factory)
- Auth env vars: `SALESBLDR_API_URL`, `SALESBLDR_API_KEY`

**ipinfo.io:**
- IP geolocation (optional)
- Auth env var: `IPINFO_TOKEN` (optional, defaults to empty)

## Data Storage

**Databases:**
- PostgreSQL 16 — Primary data store
  - Connection: `POSTGRES_HOST`, `POSTGRES_PORT` (5432), `POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD` (or `DATABASE_URL`)
  - Client: `lib/services/postgres-client.ts` singleton via `postgresClient.query()`, `.transaction()`, `.upsert()`, `.bulkUpsert()`
  - Migrations: `migrations/*.sql` (numbered sequentially, applied in alphabetical order on Postgres init)
  - All columns: `snake_case`; API responses: `camelCase` (manual transformation)
  - Audit columns: `created_at`, `updated_at`, `synced_at`, `is_deleted`, `deleted_at`

**Cache:**
- Redis 7 (optional)
  - Connection: `REDIS_URL` (e.g., `redis://localhost:6380` in Docker)
  - Client: `lib/services/redis-client.ts` via `getRedisClient()`, `getCachedData()`, `setCachedData()`, `flushCache()`
  - TTL default: 300 seconds (5 minutes)
  - Graceful fallback: if REDIS_URL unset or connection fails, caching disabled

## Authentication & Identity

**Auth Provider:**
- Better Auth 1.4 — Magic link + TOTP 2FA + Microsoft OAuth
  - Implementation: `lib/auth.ts` configures plugins, session TTL, roles (user, admin, super-admin)
  - Database: Tables created in migration 012 (user, session, account, verification)
  - Default admin bootstrap: via `DEFAULT_ADMIN_EMAIL` env var, `lib/bootstrap.ts`
  - Session cookie handling: next-js plugin enabled
  - Account linking: Microsoft OAuth can link to existing accounts
  - Roles: `super-admin`, `admin`, `user`
  - RBAC: `lib/permissions.ts` defines `ac` (access-control) rules

**API Route Auth:**
- Helpers in `lib/auth-utils.ts`: `requireAuth()`, `requireAdmin()`, `requireSuperAdmin()`, `requirePermission(resource, action)`
- Middleware: `middleware.ts` checks session cookie existence; role verification happens in API routes
- Public routes: hardcoded in `middleware.ts` (webhooks, sync endpoints, health checks, auth callbacks, mobile, openclaw, kiosk, legal)

## Monitoring & Observability

**Error Tracking:**
- None (no Sentry/DataDog integration detected)

**Logs:**
- Console logging: `console.log()`, `console.error()`
- Slow query warnings: queries > 1000ms logged in `postgresClient.query()`

## AI & LLM

**Analyzer Pipeline:**
- Primary: Anthropic Claude API
  - SDK: `@anthropic-ai/sdk` 0.91.1
  - Auth: `ANTHROPIC_API_KEY` env var
  - Models: Haiku (stage 1, 6), Sonnet (stage 2, 5), Opus (stage 3, 4)
  - Worker: `lib/services/analyzer/worker.ts` (auto-starts in production, polled every 2s)
  - Pipeline: `lib/services/analyzer/pipeline.ts` (6 stages: triage, analysis, reasoning, fingerprint, aggregate, link-discovery)
  - Cost guard: `lib/services/analyzer/cost-guard.ts` (skips Opus above $2.00 estimated cost, flags for review)
  - Idempotency: per-request provider-scoped (`anthropic` | `openrouter`); same ticket can have both

**Alternate Provider:**
- OpenRouter (opt-in per request)
  - SDK: HTTP client, OpenAI-compatible format
  - Auth: `OPENROUTER_API_KEY` env var
  - Models: DeepSeek V4 Flash (fast), DeepSeek V4 Pro (standard), DeepSeek R1 (reasoning)
  - Call layer: `lib/services/llm/openrouter-call.ts`
  - Provider hints: `data_collection: 'deny'` (privacy floor), `sort: 'throughput'`, `allow_fallbacks: true`

**Model Constants:**
- `lib/services/llm/models.ts` — canonical model IDs and stage-model mappings
  - Anthropic: `claude-haiku-4-5`, `claude-sonnet-4-6`, `claude-opus-4-7`
  - OpenRouter: `deepseek/deepseek-v4-flash`, `deepseek/deepseek-v4-pro`, `deepseek/deepseek-r1-0528`

**Pricing & Token Tracking:**
- `lib/services/llm/pricing.ts` — per-token cost calculation
- `lib/services/analyzer/cost-guard.ts` — estimated cost ceiling enforcement

## File Storage

**Backblaze B2 (S3-compatible):**
- LogLift evidence upload/download
- Auth env vars: `B2_KEY_ID`, `B2_APP_KEY`
- Config env vars: `B2_BUCKET` (default: `wulf-audits`), `B2_REGION` (default: `us-west-002`), `B2_ENDPOINT` (default: `s3.us-west-002.backblazeb2.com`)
- SDK/Client: `lib/services/b2/client.ts` — presigned URLs (AWS SigV4), download/upload with object-key validation
- Max download: 25 MB
- Object key format validation: `{client_id_or_uuid}/{computer_name}/eventlogs_{timestamp}.json.gz` (path-traversal guard)

## CI/CD & Deployment

**Hosting:**
- Docker Compose (provided in `docker-compose.yml`)
- Traefik labels for reverse-proxy routing at `pulse.wulfconsulting.cloud`
- Standalone Next.js output for containerization

**CI Pipeline:**
- None detected; local testing only (vitest)
- Build: `npm run build` (turbopack)
- Type check: `npx tsc --noEmit --pretty`
- Lint: `npm run lint` (eslint)

## Webhooks & Callbacks

**Incoming Webhooks:**
- `/api/webhooks/autotask` — Autotask event notifications (public, HMAC-SHA1 verified)
- `/api/zabbix/webhook` — Zabbix problem notifications (public)
- `/api/rmm/loglift` — LogLift evidence uploads (public, verified via `x-openclaw-key` header)
- `/api/qbo/auth` — QuickBooks OAuth callback (public)
- `/api/qbo/disconnect` — QBO token revocation (public)

**Outgoing Webhooks:**
- Autotask write-back: workflow engine executes Autotask API calls (POST notes, status updates, custom fields)
- Workflow execution: `lib/services/workflow-engine.ts` chains actions after classification

**Fire-and-Forget Sync Endpoints:**
- `/api/datto-rmm/sync` (POST)
- `/api/veeam/sync` (POST)
- `/api/veeam/rpo-check` (POST)
- `/api/itglue/sync` (POST)
- `/api/sentinelone/sync` (POST)
- `/api/engagement/sync` (POST)
- `/api/zoom/sync` (POST)
- `/api/qbo/sync` (POST)
- All public, triggered by admin UI or cron schedule in sync-scheduler

## Environment Configuration

**Required env vars (production):**
- `BETTER_AUTH_URL` — must match deployed domain
- `BETTER_AUTH_SECRET` — session signing secret
- `DATABASE_URL` or `POSTGRES_*` — database connection
- `AUTOTASK_API_URL`, `AUTOTASK_USERNAME`, `AUTOTASK_SECRET`, `AUTOTASK_API_INTEGRATION_CODE` — Autotask API
- `MICROSOFT_CLIENT_ID`, `MICROSOFT_CLIENT_SECRET`, `MICROSOFT_TENANT_ID` — OAuth login
- `ANTHROPIC_API_KEY` — AI Analyzer (required if analyzer enabled)
- `DEFAULT_ADMIN_EMAIL` — initial super-admin account

**Optional env vars:**
- `REDIS_URL` — enables caching; graceful no-op if missing
- `MSGRAPH_*` — Microsoft Graph (engagement sync)
- `DATTO_RMM_*`, `VEEAM_VSPC_*`, `AUVIK_*`, `ADDIGY_*`, `ITGLUE_API_KEY`, `MIMECAST_*`, `S1_API_KEY`, `DUO_*`, `ZOOM_*`, `QBO_*`, `ZABBIX_*`, `SALESBLDR_*` — per-integration
- `OPENROUTER_API_KEY` — alternate LLM provider
- `B2_*` — LogLift evidence storage
- `IPINFO_TOKEN` — optional IP geolocation

**Secrets location:**
- `.env.local` (development, mounted read-only in Docker)
- Environment variables passed to container (production)
- *Note: `.env` file is committed to the repository; treat values as potentially real.*

**Integration disable mechanism:**
- Two sources (merged):
  1. `INTEGRATIONS_DISABLED` env var (legacy, comma/space-separated keys; aliases: `sentinelone` → `s1`, `datto` → `datto_rmm`, `it-glue` → `itglue`, `ms-graph` → `msgraph`)
  2. `integration_settings` table (migration 081) — admin-toggled at `/admin/integrations` without restart; cache invalidation immediate; audit columns: `disabled_by`, `disabled_at`, `disabled_reason`

---

*Integration audit: 2026-05-03*
