```html

Integrating Instagram Graph API with AWS Lambda: Guest Photo Gallery Enhancement

We recently completed the integration of Instagram's Graph API into our guest photo gallery system at shipcaptaincrew.queenofsandiego.com/g/{event_id}. This walkthrough documents the technical decisions, infrastructure changes, and implementation patterns used to surface @sailjada Instagram posts alongside guest-uploaded charter photos.

What Was Done

The guest photo page previously displayed only user-uploaded and approved images. The Instagram integration extends this to include verified @sailjada posts from matching date/time windows, creating a richer visual narrative of charter events. The Lambda function shipcaptaincrew (us-east-1, account 782785212866) already contained dormant Instagram integration code that returned empty arrays when credentials were missing. This project activated that functionality by establishing proper OAuth token flow and secure credential storage.

Technical Details: OAuth Token Flow

Instagram's Graph API requires a two-stage token exchange process for business accounts. Here's the exact flow implemented:

  • Short-lived token generation: Created via the Graph API Explorer at developers.facebook.com/tools/explorer with scopes instagram_basic and pages_show_list. This token is valid for approximately 1 hour and serves as the foundation for the exchange.
  • IG_USER_ID retrieval: Called the Facebook Graph API endpoint to fetch the business account ID linked to the Facebook Page associated with @sailjada. This ID is permanent and acts as the resource identifier for all subsequent media queries.
  • Long-lived token exchange: Exchanged the short-lived token for a long-lived access token (valid 60 days) using the app credentials (APP_ID and APP_SECRET stored securely in AWS Secrets Manager). The exchange endpoint is https://graph.instagram.com/v18.0/oauth/access_token with POST parameters: grant_type=ig_refresh_token, access_token={short_lived}, client_secret={APP_SECRET}.

Why this multi-step approach? Instagram intentionally requires business accounts to maintain tighter control over token lifecycle. The short-lived token validates user intent; the long-lived token enables scheduled refreshes without user interaction. This pattern prevents token stagnation and reduces security surface area.

Infrastructure: AWS Lambda Configuration

The Lambda function shipcaptaincrew was updated with two new environment variables:

  • IG_USER_ID — The numeric identifier for the @sailjada business account on Instagram (obtained during token setup)
  • IG_ACCESS_TOKEN — The 60-day long-lived access token

These were injected via the Lambda function configuration update:

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

The function code retrieves these at initialization time. When either variable is missing or empty, the Instagram integration gracefully degrades to return an empty array, maintaining backward compatibility with the previous behavior.

Why Lambda environment variables instead of Secrets Manager? These credentials are non-sensitive metadata (IG_USER_ID is public; the token grants read-only access to media metadata, not user accounts or direct messages). Using environment variables reduces latency by eliminating the Secrets Manager API call on every invocation and simplifies local development workflows.

API Query Pattern

Once credentials are in place, the Lambda retrieves media using the Instagram Graph API endpoint:

GET https://graph.instagram.com/v18.0/{IG_USER_ID}/media
  ?fields=id,caption,media_type,media_url,permalink,timestamp
  &access_token={IG_ACCESS_TOKEN}

The function filters results by timestamp to match the guest event date/time window (typically ±12 hours from the event). The media_url field contains the image or video URL; permalink links back to the Instagram post for engagement.

This read-only scope avoids any risk of accidental modification or deletion of Instagram content. The API returns only business account metadata, never personal user data.

Token Refresh Strategy

The 60-day token expiration window was deliberately chosen to balance security with operational overhead. A monthly refresh cadence was selected:

  • Manual refresh option: Re-run the token exchange command monthly before expiration, updating environment variables via the AWS CLI or Lambda console.
  • Automated refresh (optional future enhancement): An EventBridge rule triggering a dedicated Lambda at day 45 of the token lifecycle could automate this entirely. The refresh Lambda would call the exchange endpoint, update the function configuration, and log completion to CloudWatch.

Why not max out the token's lifespan without refreshing? Periodic refreshes allow us to rotate credentials if a token is compromised and provide natural points to audit the Instagram app integration's continued relevance.

Key Decisions & Rationale

Facebook App Setup: The critical first step was adding the correct product to the existing sailjada-social app. A "Manage messaging" use case had been added previously, but this grants direct messaging scopes, not media read access. The Instagram Graph API product was added separately, ensuring the app requested instagram_basic — the minimal scope needed to read media metadata.

Business Account Requirement: @sailjada is configured as a Business/Creator account and linked to a Facebook Page. This is required by Instagram's Graph API; personal accounts cannot be accessed. This decision trades some profile flexibility for robust, documented API access and better content analytics.

Graceful Degradation: If either environment variable is missing or the API call fails, the gallery page continues to display guest-uploaded photos without interruption. This resilience pattern prevents Instagram API issues from breaking the core product experience.

Verification & Testing

Post-deployment verification is straightforward: navigate to shipcaptaincrew.queenofsandiego.com/g/2026-04-29 (or any event date with corresponding Instagram posts from @sailjada). The page should display both guest-uploaded photos and Instagram media in chronological order. CloudWatch logs in the /aws/lambda/shipcaptaincrew log group reveal any API errors during token exchange or media retrieval.

What's Next

Near-term enhancements include automating token refresh via EventBridge to eliminate manual intervention. Medium-term, we may cache Instagram media in DynamoDB with a 24-hour TTL to reduce API calls and improve page load performance. Finally, if Instagram ever offers carousel or Reel support, the media_type filtering logic can be extended to include those content formats alongside static images.

```