Every endpoint lives under /api/v1, except /api/health. Responses use one envelope:
{
"data": {},
"error": null,
"meta": {}
}
All endpoints require a Bearer token except these: registration, login, the extension pairing poll (GET /api/v1/extension/pair), frontend error reporting, the health check, and the WhatsApp webhook (which is verified by HMAC signature instead). The OAuth URL endpoints for Google and Twitter need a logged-in user, because the provider account is attached to that user.
The tables below list every route in the FastAPI OpenAPI schema. backend/tests/test_api_doc.py runs backend/scripts/check_api_doc.py and fails when a route is missing from this page, so the backend test run (including the pre-push hook) catches drift. To check locally, run PYTHONPATH=. python3 scripts/check_api_doc.py from backend/.
Auth
| Method | Endpoint | Description |
|---|
| POST | /api/v1/auth/register | Register a new account with email and password |
| POST | /api/v1/auth/login | Login with email and password |
| GET | /api/v1/auth/me | Current authenticated user |
| PUT | /api/v1/auth/me | Update profile fields |
| DELETE | /api/v1/auth/me | Delete the authenticated account |
| POST | /api/v1/auth/change-password | Change account password |
| GET | /api/v1/auth/google/url | Google OAuth authorization URL (requires a logged-in user) |
| POST | /api/v1/auth/google/callback | Attach the Google account to the logged-in user and return a fresh access token. The caller must be the user the OAuth state was issued to, otherwise 403 |
| GET | /api/v1/auth/google/accounts | List connected Google accounts (Gmail, Calendar, and Contacts sync cover every linked account) |
| DELETE | /api/v1/auth/google/accounts/{account_id} | Remove a connected Google account |
| GET | /api/v1/auth/twitter/url | Twitter OAuth authorization URL (requires a logged-in user) |
| POST | /api/v1/auth/twitter/callback | Twitter OAuth callback handler |
| DELETE | /api/v1/auth/twitter/disconnect | Disconnect Twitter |
| POST | /api/v1/auth/telegram/connect | Send OTP code to Telegram |
| POST | /api/v1/auth/telegram/verify | Verify the Telegram OTP code |
| POST | /api/v1/auth/telegram/verify-2fa | Submit Telegram 2FA password |
| POST | /api/v1/auth/telegram/reset-session | Clear the stored Telegram session but keep the username for reconnecting |
| DELETE | /api/v1/auth/telegram/disconnect | Disconnect Telegram |
| POST | /api/v1/auth/whatsapp/connect | Start a WhatsApp link-device flow |
| GET | /api/v1/auth/whatsapp/qr | Fetch the current WhatsApp QR payload |
| GET | /api/v1/auth/whatsapp/status | WhatsApp session status |
| DELETE | /api/v1/auth/whatsapp/disconnect | Tear down the WhatsApp session |
CRUD and listing
| Method | Endpoint | Description |
|---|
| GET | /api/v1/contacts | List contacts (paginated, searchable, filterable) |
| POST | /api/v1/contacts | Create a new contact |
| GET | /api/v1/contacts/ids | Bulk contact IDs for select-all |
| GET | /api/v1/contacts/stats | Dashboard contact stats |
| GET | /api/v1/contacts/birthdays | Upcoming birthdays |
| GET | /api/v1/contacts/overdue | Overdue follow-up contacts |
| GET | /api/v1/contacts/map | Contacts within a viewport (bbox query) |
| POST | /api/v1/contacts/bulk-update | Bulk update tags, priority, and other fields |
| POST | /api/v1/contacts/reconcile-last-interaction | Recompute last_interaction_at for all contacts |
| POST | /api/v1/contacts/scores/recalculate | Trigger relationship-score recompute |
| GET | /api/v1/contacts/{contact_id} | Get a contact |
| PUT | /api/v1/contacts/{contact_id} | Update a contact |
| DELETE | /api/v1/contacts/{contact_id} | Delete a contact |
| GET | /api/v1/contacts/{contact_id}/activity | Activity feed for a contact |
| GET | /api/v1/contacts/{contact_id}/related | Related contacts (same org, etc.) |
| GET | /api/v1/contacts/{contact_id}/duplicates | Duplicate candidates for a contact |
2nd-tier (Telegram group members)
| Method | Endpoint | Description |
|---|
| GET | /api/v1/contacts/2nd-tier/count | Count of 2nd-tier contacts |
| DELETE | /api/v1/contacts/2nd-tier | Delete all 2nd-tier contacts |
| POST | /api/v1/contacts/{contact_id}/promote | Promote 2nd-tier contact to 1st-tier |
Enrichment, AI, messaging
| Method | Endpoint | Description |
|---|
| POST | /api/v1/contacts/{contact_id}/enrich | Enrich via Apollo |
| POST | /api/v1/contacts/{contact_id}/extract-bio | AI-extract structured fields from bios |
| POST | /api/v1/contacts/{contact_id}/auto-tag | Apply AI taxonomy tags to one contact |
| POST | /api/v1/contacts/{contact_id}/refresh-avatar | Re-download avatar from connected platforms |
| POST | /api/v1/contacts/{contact_id}/refresh-bios | Re-fetch Twitter/Telegram bios |
| POST | /api/v1/contacts/{contact_id}/compose | Generate an AI-drafted message (default channel email) |
| POST | /api/v1/contacts/{contact_id}/send-message | Send a Telegram message (optionally scheduled). Other channels return 400; the web app copies those drafts to the clipboard instead |
Identity / merging
| Method | Endpoint | Description |
|---|
| POST | /api/v1/contacts/{contact_id}/merge/{other_id} | Merge two contacts directly |
| POST | /api/v1/contacts/{contact_id}/dismiss-duplicate/{other_id} | Dismiss a duplicate hint |
Imports
| Method | Endpoint | Description |
|---|
| POST | /api/v1/contacts/import/csv | Import from a CSV file. Each row is saved on its own, and failed rows come back in the result |
| POST | /api/v1/contacts/import/linkedin | Import a LinkedIn Connections.csv export |
| POST | /api/v1/contacts/import/linkedin-messages | Import a LinkedIn message export |
| Method | Endpoint | Description |
|---|
| POST | /api/v1/contacts/{contact_id}/sync-emails | Sync Gmail threads for a single contact |
| POST | /api/v1/contacts/{contact_id}/sync-telegram | Sync Telegram DM for a single contact |
| POST | /api/v1/contacts/{contact_id}/sync-twitter | Sync Twitter DM for a single contact |
| GET | /api/v1/contacts/{contact_id}/telegram/common-groups | Telegram groups shared with a contact |
Account-wide background syncs
Each call returns immediately and the work runs in a Celery task. Google Contacts, Google Calendar, Telegram, and Twitter syncs post a notification when they finish, and a sync that fails after retries posts a failure notification.
| Method | Endpoint | Description |
|---|
| POST | /api/v1/contacts/sync/gmail | Sync Gmail email threads |
| POST | /api/v1/contacts/sync/google | Import from Google Contacts |
| POST | /api/v1/contacts/sync/google-calendar | Sync Google Calendar events |
| POST | /api/v1/contacts/sync/telegram | Sync Telegram chats, groups, and bios |
| POST | /api/v1/contacts/sync/twitter | Sync Twitter DMs and mentions |
| POST | /api/v1/contacts/sync/whatsapp | Backfill WhatsApp messages |
| GET | /api/v1/telegram/sync-progress | Poll Telegram sync progress |
Tag taxonomy
| Method | Endpoint | Description |
|---|
| GET | /api/v1/contacts/tags | Flat list of in-use tags |
| GET | /api/v1/contacts/tags/taxonomy | Tag taxonomy structure |
| PUT | /api/v1/contacts/tags/taxonomy | Update taxonomy |
| POST | /api/v1/contacts/tags/discover | LLM-discover a draft taxonomy |
| POST | /api/v1/contacts/tags/apply | Apply taxonomy to contacts |
Interactions (timeline)
| Method | Endpoint | Description |
|---|
| GET | /api/v1/contacts/{contact_id}/interactions | List interactions |
| POST | /api/v1/contacts/{contact_id}/interactions | Add a manual note |
| PATCH | /api/v1/contacts/{contact_id}/interactions/{interaction_id} | Update a note |
| DELETE | /api/v1/contacts/{contact_id}/interactions/{interaction_id} | Delete a note |
Suggestions
| Method | Endpoint | Description |
|---|
| GET | /api/v1/suggestions | List follow-up suggestions |
| GET | /api/v1/suggestions/digest | Weekly digest of suggestions |
| POST | /api/v1/suggestions/generate | Generate new follow-up suggestions |
| PUT | /api/v1/suggestions/{suggestion_id} | Update suggestion status (snooze, dismiss, send) |
| POST | /api/v1/suggestions/{suggestion_id}/regenerate | Regenerate the AI-drafted message |
Identity Resolution
| Method | Endpoint | Description |
|---|
| GET | /api/v1/identity/matches | List pending identity matches |
| POST | /api/v1/identity/scan | Trigger an identity resolution scan |
| POST | /api/v1/identity/matches/{match_id}/merge | Confirm merge for a pair |
| POST | /api/v1/identity/matches/{match_id}/reject | Reject a match |
Organizations
| Method | Endpoint | Description |
|---|
| GET | /api/v1/organizations | List organizations (excludes those with zero active contacts) |
| POST | /api/v1/organizations | Create an organization |
| POST | /api/v1/organizations/merge | Merge two or more organizations |
| POST | /api/v1/organizations/backfill-logos | Backfill favicon-derived logos for orgs missing one |
| POST | /api/v1/organizations/scan-duplicates | Run a dedup scan; auto-merges deterministic pairs, queues fuzzy ones |
| GET | /api/v1/organizations/duplicates | List pending_review duplicate pairs |
| POST | /api/v1/organizations/duplicates/{match_id}/merge | Merge a pending pair (target_id body) |
| POST | /api/v1/organizations/duplicates/{match_id}/dismiss | Mark a pending pair as 'not the same' |
| GET | /api/v1/organizations/{org_id} | Get organization detail |
| PATCH | /api/v1/organizations/{org_id} | Update an organization |
| DELETE | /api/v1/organizations/{org_id} | Delete an organization |
| GET | /api/v1/organizations/{org_id}/stats | Per-organization stats from the materialized view |
| POST | /api/v1/organizations/{org_id}/refresh-logo | Re-fetch favicon for the org |
Notifications
| Method | Endpoint | Description |
|---|
| GET | /api/v1/notifications | List notifications (page, page_size up to 100, optional link filter) |
| GET | /api/v1/notifications/unread-count | Unread count |
| PUT | /api/v1/notifications/{notification_id}/read | Mark a notification read |
| PUT | /api/v1/notifications/read-all | Mark all read |
Settings
| Method | Endpoint | Description |
|---|
| GET | /api/v1/settings/priority | Follow-up intervals (high, medium, low) |
| PUT | /api/v1/settings/priority | Update follow-up intervals (7-365 days) |
| GET | /api/v1/settings/sync | Per-platform auto-sync and schedule (manual, 6h, 12h, daily) |
| PUT | /api/v1/settings/sync | Update per-platform auto-sync and schedule |
| GET | /api/v1/settings/telegram | Telegram-specific settings (the 2nd-tier import toggle) |
| PUT | /api/v1/settings/telegram | Update Telegram settings |
| GET | /api/v1/settings/suggestions | Suggestion preferences: max_suggestions (5-20), include_dormant, birthday_reminders, preferred_channel, dormancy_threshold_days (30-3650) |
| PUT | /api/v1/settings/suggestions | Update suggestion preferences |
| GET | /api/v1/settings/mcp-key | MCP API key status |
| POST | /api/v1/settings/mcp-key | Generate a new MCP API key (shown once; only an HMAC-SHA256 hash is stored) |
| DELETE | /api/v1/settings/mcp-key | Revoke the MCP API key |
Activity
| Method | Endpoint | Description |
|---|
| GET | /api/v1/activity/recent | Recent interactions (last 7 days, deduped per contact) |
Sync History
| Method | Endpoint | Description |
|---|
| GET | /api/v1/sync-history | Recent sync events with record counts |
| GET | /api/v1/sync-history/stats | Aggregate stats per platform |
Map
| Method | Endpoint | Description |
|---|
| GET | /api/v1/map/config | Public Mapbox token for the browser |
See also GET /api/v1/contacts/map above for the viewport query.
LinkedIn (Chrome extension push)
| Method | Endpoint | Description |
|---|
| POST | /api/v1/linkedin/push | Push profiles and messages from the extension |
Cookies are stored per user. The Chrome extension pushes fresh cookies, and the Settings page reads the status and clears them. Both the web token and the extension token are accepted.
| Method | Endpoint | Description |
|---|
| GET | /api/v1/integrations/twitter/cookies | Status of stored cookies for the user |
| POST | /api/v1/integrations/twitter/cookies | Push fresh cookies (encrypted at rest) and verify them |
| DELETE | /api/v1/integrations/twitter/cookies | Clear stored cookies |
| Method | Endpoint | Description |
|---|
| POST | /api/v1/meta/push | Push Messenger / Instagram DM data from the extension |
Extension Pairing
The pairing poll is unauthenticated; everything else requires the extension JWT.
| Method | Endpoint | Description |
|---|
| POST | /api/v1/extension/pair | Submit a pairing code from the extension popup |
| GET | /api/v1/extension/pair | Poll for token (unauthenticated, used by extension) |
| DELETE | /api/v1/extension/pair | Disconnect the extension |
| POST | /api/v1/extension/refresh | Silent token refresh (extension exchanges expiring JWT for a fresh 30-day one) |
Webhooks
| Method | Endpoint | Description |
|---|
| POST | /api/v1/webhooks/whatsapp | WhatsApp sidecar webhook (HMAC-signed) |
Diagnostics
| Method | Endpoint | Description |
|---|
| GET | /api/health | Liveness probe |
| POST | /api/v1/errors | Frontend error reporting |
| GET | /api/v1/version | Current app version and latest GitHub release (for self-hoster update banner) |