Skip to content

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).

  1. User clicks “Connect with Facebook” → app redirects to Facebook OAuth
  2. User authorizes → Facebook redirects to /integrations/meta-messaging/oauth/callback
  3. Server exchanges code for a long-lived token (60-day expiry)
  4. Server fetches the user’s Pages and linked Instagram accounts
  5. Single page → auto-saves. Multiple pages → user picks one via MetaPagePicker
GET /api/integrations/meta/oauth-url

Returns a Facebook OAuth URL with these scopes:

  • pages_messaging — Send and receive Messenger messages
  • instagram_manage_messages — Send and receive Instagram DMs
  • pages_show_list — List user’s Pages
  • instagram_basic — Access linked Instagram account info

The state parameter encodes org_id and user_id for the callback.

GET /integrations/meta-messaging/oauth/callback

Handler: 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.

  1. Exchange authorization code for a short-lived token via POST /oauth/access_token
  2. Exchange short-lived token for a 60-day long-lived token via GET /oauth/access_token?grant_type=fb_exchange_token

After token exchange, the handler calls fetch_pages_with_instagram():

  1. GET /me/accounts — list user’s Facebook Pages
  2. For each Page: GET /{page_id}?fields=instagram_business_account
  3. If IG account found: GET /{ig_account_id}?fields=username
ScenarioRedirect
1 Page foundAuto-save, subscribe webhooks → /settings/tab/integrations
Multiple PagesCache pages (5-min TTL) → /settings/tab/integrations?meta_select_page=true
0 Pages/settings/tab/integrations?meta_error=no_pages

When multiple Pages exist, the MetaPagePicker component renders a radio list:

GET /api/integrations/meta/pending-pages → returns cached pages
POST /api/integrations/meta/select-page → saves selected page + subscribes webhooks

Each page shows its name and linked Instagram username (or “No Instagram account”).

After saving a page, the server subscribes to events:

  • subscribe_page_to_webhooks(page_id, token) — POST /{page_id}/subscribed_apps with fields messages,message_deliveries,message_reads
  • subscribe_app_to_page_webhooks(app_id, secret, verify_token, callback_url) — registers for Page events
  • subscribe_app_to_instagram_webhooks(app_id, secret, verify_token, callback_url) — registers for Instagram events
POST /api/integrations/meta/disconnect

Deletes the organization_meta_settings row. Message history is preserved — only the connection is removed.

The organization_meta_settings table holds:

ColumnTypeDescription
page_access_tokenStringLong-lived Page access token
facebook_page_idStringFacebook Page ID
facebook_page_nameStringDisplay name
instagram_business_account_idString?Linked IG account (null if none)
instagram_usernameString?IG username for display