OAuth Connection Flow
Organizations connect Meta channels through a Facebook OAuth flow in Settings → Integrations → Meta. A single connection grants access to both Facebook Messenger and Instagram DM (if the Page has a linked Instagram Business account).
Flow Summary
Section titled “Flow Summary”- User clicks “Connect with Facebook” → app redirects to Facebook OAuth
- User authorizes → Facebook redirects to
/integrations/meta-messaging/oauth/callback - Server exchanges code for a long-lived token (60-day expiry)
- Server fetches the user’s Pages and linked Instagram accounts
- Single page → auto-saves. Multiple pages → user picks one via
MetaPagePicker
OAuth URL Generation
Section titled “OAuth URL Generation”GET /api/integrations/meta/oauth-urlReturns a Facebook OAuth URL with these scopes:
pages_messaging— Send and receive Messenger messagesinstagram_manage_messages— Send and receive Instagram DMspages_show_list— List user’s Pagesinstagram_basic— Access linked Instagram account info
The state parameter encodes org_id and user_id for the callback.
Callback Handler
Section titled “Callback Handler”GET /integrations/meta-messaging/oauth/callbackHandler: meta_messaging_oauth_callback in meta_oauth_callback_api.rs
The callback is a raw Axum handler (not a standard API endpoint) because it serves browser redirects.
Token Exchange
Section titled “Token Exchange”- Exchange authorization
codefor a short-lived token viaPOST /oauth/access_token - Exchange short-lived token for a 60-day long-lived token via
GET /oauth/access_token?grant_type=fb_exchange_token
Page Discovery
Section titled “Page Discovery”After token exchange, the handler calls fetch_pages_with_instagram():
GET /me/accounts— list user’s Facebook Pages- For each Page:
GET /{page_id}?fields=instagram_business_account - If IG account found:
GET /{ig_account_id}?fields=username
Redirect Logic
Section titled “Redirect Logic”| Scenario | Redirect |
|---|---|
| 1 Page found | Auto-save, subscribe webhooks → /settings/tab/integrations |
| Multiple Pages | Cache pages (5-min TTL) → /settings/tab/integrations?meta_select_page=true |
| 0 Pages | /settings/tab/integrations?meta_error=no_pages |
Page Selection
Section titled “Page Selection”When multiple Pages exist, the MetaPagePicker component renders a radio list:
GET /api/integrations/meta/pending-pages → returns cached pagesPOST /api/integrations/meta/select-page → saves selected page + subscribes webhooksEach page shows its name and linked Instagram username (or “No Instagram account”).
Webhook Subscription
Section titled “Webhook Subscription”After saving a page, the server subscribes to events:
subscribe_page_to_webhooks(page_id, token)— POST/{page_id}/subscribed_appswith fieldsmessages,message_deliveries,message_readssubscribe_app_to_page_webhooks(app_id, secret, verify_token, callback_url)— registers for Page eventssubscribe_app_to_instagram_webhooks(app_id, secret, verify_token, callback_url)— registers for Instagram events
Disconnect
Section titled “Disconnect”POST /api/integrations/meta/disconnectDeletes the organization_meta_settings row. Message history is preserved — only the connection is removed.
Stored Data
Section titled “Stored Data”The organization_meta_settings table holds:
| Column | Type | Description |
|---|---|---|
page_access_token | String | Long-lived Page access token |
facebook_page_id | String | Facebook Page ID |
facebook_page_name | String | Display name |
instagram_business_account_id | String? | Linked IG account (null if none) |
instagram_username | String? | IG username for display |