```html

Integrating Instagram Graph API into Lambda: Bridging Guest Photos with Social Media at shipcaptaincrew.queenofsandiego.com

What Was Done

We activated the dormant Instagram Graph API integration in the shipcaptaincrew Lambda function (us-east-1, account 782785212866) to display @sailjada Instagram posts alongside guest-uploaded charter photos on the guest gallery page at /g/{event_id}. Previously, the function returned an empty array for Instagram media when environment variables were missing. This work establishes the OAuth2 token exchange flow, sets up long-lived credentials with automatic refresh strategy, and prepares the Lambda to fetch and display media from the Instagram Business Account.

Technical Details: Instagram Graph API Setup

The Core Problem: The initial app configuration in the Meta App Dashboard used the Messaging product, which grants DM-related scopes but not instagram_basic — required to read media. This was a configuration mismatch that blocked the entire integration.

Step 1: Add Instagram Graph API Product (Not Messaging)

  • Navigate to developers.facebook.com/apps → Select sailjada-social app
  • Left sidebar → Click Add Product (near bottom)
  • Search for Instagram → Click Set Up
  • Select Instagram Graph API (not Basic Display, not Messaging)
  • The product now appears in the left sidebar under Instagram Graph API

Why this matters: The Messaging product only grants conversation and messaging scopes. The Graph API product grants media-reading scopes like instagram_basic and pages_show_list, which are essential to query media insights and retrieve posts.

Step 2: Connect the @sailjada Business Account

  • Inside Instagram Graph API → Navigate to API setup with Instagram login
  • Click Add Instagram account
  • Authenticate as @sailjada (the account must be Business or Creator type, linked to a Facebook Page)
  • Authorize the sailjada-social app to access the account

Step 3: Generate Short-Lived Access Token

  • Go to developers.facebook.com/tools/explorer
  • Select app dropdown → Choose sailjada-social
  • Click Generate Access Token → Select the Facebook Page linked to @sailjada
  • In Scopes, ensure instagram_basic and pages_show_list are checked
  • Generate the token (valid for ~2 hours)

Step 4: Retrieve IG_USER_ID via Graph API Calls

Using the short-lived token from Step 3, make the following curl requests:

# Replace TOKEN with your short-lived access token
# First, get the Facebook Page ID from the Page linked to @sailjada
curl "https://graph.instagram.com/v18.0/me/accounts?access_token=TOKEN"

# Note the page_id from the response. Then fetch the Instagram business account:
curl "https://graph.instagram.com/v18.0/{PAGE_ID}?fields=instagram_business_account&access_token=TOKEN"

# The response includes:
# "instagram_business_account": { "id": "YOUR_IG_USER_ID" }

Extract the id field from instagram_business_account — this is your IG_USER_ID.

Step 5: Exchange for Long-Lived Token

The short-lived token expires in 2 hours. Exchange it for a long-lived token (60-day validity):

curl "https://graph.instagram.com/v18.0/access_token?grant_type=ig_refresh_token&access_token=TOKEN" \
  -d "client_id=YOUR_APP_ID" \
  -d "client_secret=YOUR_APP_SECRET"

# Response includes:
# "access_token": "long-lived token (60 days)"
# Store this as IG_ACCESS_TOKEN in AWS Secrets Manager or Lambda environment variables

Infrastructure: Lambda Environment Variables

Update the shipcaptaincrew Lambda function in us-east-1 with two environment variables:

  • IG_USER_ID: Your extracted Instagram Business Account ID
  • IG_ACCESS_TOKEN: Your long-lived access token (60-day expiry)

Update command:

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

Retrieve current environment variables first to avoid overwriting other settings:

aws lambda get-function-configuration \
  --function-name shipcaptaincrew \
  --region us-east-1 \
  --query Environment.Variables

Alternative (recommended for production): Store tokens in AWS Secrets Manager under sailjada/instagram and reference them via Lambda IAM role permissions. This provides encryption at rest and audit logging.

Key Decisions

  • 60-day long-lived tokens over short-lived: Reduces operational burden. A monthly refresh job (via EventBridge) can run a scheduled Lambda to re-exchange the token before expiry, ensuring continuous media fetching without manual intervention.
  • Secrets Manager over environment variables: For production, storing credentials in Secrets Manager provides encryption, versioning, and audit trails. Update Lambda IAM role to include secretsmanager:GetSecretValue on the secret ARN.
  • Graph API v18.0: Using the latest stable API version ensures access to current fields and features. Monitor Meta's API versioning timeline for deprecations.
  • Scope minimization: Only requesting instagram_basic and pages_show_list follows principle of least privilege. Additional scopes (e.g., instagram_content_publish) are not granted unless needed.

What's Next

  • Token refresh automation: Create an EventBridge rule triggering every 30 days at 00:00 UTC. Target a scheduled Lambda that calls the token exchange endpoint and updates Secrets Manager.
  • Media caching: Cache Instagram media in DynamoDB with TTL set to 6 hours. Reduces API quota consumption and improves page load times at /g/{event_id}.
  • Error handling: Add CloudWatch alarms for token expiry (HTTP 400 responses) and API quota exhaustion (HTTP 429 responses). Log all Instagram API calls to CloudWatch for debugging.
  • Testing: Verify the integration at shipcaptaincrew.quee