```html

Integrating Instagram Graph API with Lambda: Connecting @sailjada Media to Guest Photo Pages

What Was Done

We implemented Instagram Graph API integration into the shipcaptaincrew Lambda function (us-east-1, account 782785212866) to automatically surface @sailjada Instagram posts alongside guest-uploaded charter photos on dynamic guest photo pages at shipcaptaincrew.queenofsandiego.com/g/{event_id}. The Lambda previously had dormant Instagram code that returned an empty array when environment variables were missing; this work activates that capability by properly provisioning Graph API credentials and establishing the token refresh cycle.

Technical Details: Why We Needed Graph API, Not Basic Display

The initial setup attempt used Instagram's Messaging use case, which is designed for handling direct messages—not media reads. This is a critical distinction in Facebook's product architecture. To read media metadata (captions, timestamps, media URLs) from a business/creator account, you must use the Instagram Graph API product, which requires the instagram_basic scope. The Messaging use case does not grant this scope, making it a blocker for media retrieval.

Here's why this architecture matters:

  • Graph API — Designed for reading business/creator account data (media, insights, follower counts). Requires a Facebook Page linked to the Instagram business account.
  • Basic Display — Read-only access to media but no timestamps or captions; insufficient for our use case.
  • Messaging — DM handling only; no media scope granted.

@sailjada's account must be a Business or Creator account (not a personal account) and linked to a Facebook Page for this to work. The token flow uses the page's credentials to access the linked Instagram business account.

Step-by-Step Credential Provisioning

1. Add the Instagram Graph API Product

In the Facebook App Dashboard for sailjada-social:

  • Navigate to developers.facebook.com/apps → select sailjada-social
  • In the left sidebar, locate Add Product (near the bottom)
  • Search for Instagram and click Set Up
  • When prompted for access type, select Instagram Graph API (not Basic Display)
  • It will appear in your sidebar as a new product tile

2. Connect the @sailjada Instagram Account

Within Instagram Graph API settings:

  • Click API setup with Instagram login
  • Click Add Instagram account
  • Authenticate as @sailjada (must be the business/creator account owner)
  • Authorize the app to access the account

3. Generate a Short-Lived Token (Valid for ~1 hour)

Using the Graph API Explorer:

  • Go to developers.facebook.com/tools/explorer
  • In the top-left dropdown, select sailjada-social
  • Click Generate Access Token
  • Select the Facebook Page linked to @sailjada
  • Ensure scopes include instagram_basic and pages_show_list
  • Copy the generated token for the next step

4. Retrieve IG_USER_ID from Page to Instagram Business Account

Execute two API calls to traverse from Facebook Page → Instagram Business Account ID:


# First call: Get the page's linked Instagram business account
curl "https://graph.instagram.com/v18.0/{PAGE_ID}?fields=instagram_business_account&access_token={SHORT_LIVED_TOKEN}"

# Response structure:
# {
#   "instagram_business_account": {
#     "id": "17841400000000000"
#   },
#   "id": "{PAGE_ID}"
# }

# Extract the "id" from instagram_business_account — this is your IG_USER_ID

Store this IG_USER_ID value; it's immutable and tied to the Instagram account.

5. Exchange Short-Lived Token for Long-Lived Token (60-day validity)

Short-lived tokens expire in ~1 hour and are impractical for Lambda environment variables. Exchange it for a long-lived token:


curl "https://graph.instagram.com/v18.0/oauth/access_token" \
  -d "grant_type=ig_refresh_token" \
  -d "access_token={SHORT_LIVED_TOKEN}" \
  -d "client_secret={APP_SECRET}"

# Response:
# {
#   "access_token": "long_lived_token_value",
#   "token_type": "bearer"
# }

The returned access_token is your IG_ACCESS_TOKEN. Store this securely in AWS Secrets Manager or Lambda environment variables.

Infrastructure: Lambda Environment Variables

The shipcaptaincrew Lambda function reads these environment variables (set via console or CLI):


aws lambda update-function-configuration \
  --function-name shipcaptaincrew \
  --region us-east-1 \
  --environment Variables="{IG_USER_ID=17841400000000000,IG_ACCESS_TOKEN=your_60_day_token}"

When both variables are present, the Lambda's dormant Instagram integration code activates automatically and includes @sailjada posts in responses to guest photo page requests.

Token Refresh Strategy

Long-lived tokens expire after 60 days. Rather than manual renewal, we recommend:

  • EventBridge rule triggered monthly (e.g., cron(0 0 1 * ? *) for the 1st of each month)
  • Lambda function that calls the same exchange endpoint with the current token, receives a refreshed token, and updates the shipcaptaincrew function's environment
  • Fallback: If a token expires, Instagram endpoints return a 401; the guest photo page gracefully omits Instagram posts and shows only guest-uploaded photos

This pattern avoids hardcoding token generation logic into the main application Lambda.

Key Decisions

  • Environment variables over Secrets Manager — For this use case, environment variables are sufficient because the token is rotated monthly and the exposure window is bounded. If rotation were manual/infrequent, Secrets Manager would be preferable.
  • 60-day tokens vs. indefinite — Facebook intentionally limits long-lived token lifespans to encourage refresh patterns, reducing the blast radius of a leaked token. We accepted this constraint rather than working around it.
  • Separate product (not Messaging) — Adding Messaging first was a process error; Graph API is the correct product for media reads and has different OAuth scopes.
  • Page-level authentication — By authenticating through the Facebook Page, we avoid storing @sailjada's personal credentials. The page acts as a proxy for permissions.