A Next.js application that enables sending messages to Instagram users on behalf of a business account using the Instagram Graph API.
- ✅ Send direct messages via Instagram Graph API
- ✅ View received Instagram messages and conversations
- ✅ Receive real-time webhook notifications for new messages
- ✅ Interactive web interface for sending messages
- ✅ Real-time message display with refresh capability
- ✅ Clean API endpoints with proper error handling
- ✅ TypeScript support for type safety
- ✅ Production-ready code structure
Before you begin, ensure you have:
- Node.js (v18 or later) installed
- pnpm (v8 or later) - Install with:
npm install -g pnpm - An Instagram Business or Creator Account (NOT a personal account)
- A Facebook Page linked to your Instagram Business account
- Facebook Developer Account with an app configured for Instagram messaging
⚠️ Important: Your Instagram account MUST be converted to a Business or Creator account and linked to a Facebook Page. Personal Instagram accounts will NOT work. See INSTAGRAM_SETUP.md for detailed setup instructions.
pnpm installCreate a .env.local file in the root directory:
cp env.example .env.localEdit .env.local and add your credentials:
PAGE_ACCESS_TOKEN=your_page_access_token_here
IG_USER_ID=your_instagram_user_id_here
WEBHOOK_VERIFY_TOKEN=your_custom_verify_token_hereNote: The
WEBHOOK_VERIFY_TOKENcan be any string you choose. You'll need to use this same token when setting up the webhook in the Facebook Developer Console.
PAGE_ACCESS_TOKEN:
- Go to Facebook Graph API Explorer
- Select your app
- Select "Get User Access Token"
- Add permissions:
pages_show_list(required to see pages)pages_messaging(required for messaging)pages_read_engagement(required to read messages)instagram_basic(basic Instagram access)instagram_manage_messages(required to send and read messages)
- Generate token and copy it
- Exchange it for a long-lived token (recommended for production)
IG_USER_ID:
- Use the Graph API Explorer to call:
GET /me/accounts - Find your Facebook Page ID
- Call:
GET /{page-id}?fields=instagram_business_account - Copy the
instagram_business_account.idvalue (this is your Instagram Business Account ID, not your personal Instagram ID)
📘 Need Help? See INSTAGRAM_SETUP.md for detailed step-by-step instructions on converting your Instagram to a Business account and linking it to your Facebook Page.
pnpm devThe server will start at http://localhost:3000
Visit http://localhost:3000 to access the interactive web interface where you can:
- Send messages directly through a form
- View all received Instagram messages and conversations
- Auto-refresh messages every 10 seconds (or manually with the refresh button)
- See conversation history with each user
- Easily copy recipient IDs with one click
- See which conversations are within the 24-hour messaging window
Send a POST request to /api/send-message with the following JSON payload:
{
"recipient_id": "USER_INSTAGRAM_ID",
"message": "Hello there!"
}# Send a new message
curl -X POST http://localhost:3000/api/send-message \
-H "Content-Type: application/json" \
-d '{
"recipient_id": "123456789",
"message": "Hello from Instagram API!"
}'
# Reply to a specific message
curl -X POST http://localhost:3000/api/send-message \
-H "Content-Type: application/json" \
-d '{
"recipient_id": "123456789",
"message": "This is a reply!",
"reply_to": "mid.xxxxx"
}'- Create a new POST request
- URL:
http://localhost:3000/api/send-message - Headers:
Content-Type: application/json - Body (raw JSON):
{ "recipient_id": "123456789", "message": "Hello from Instagram API!" } - Click Send
Fetch received messages programmatically:
curl -X GET http://localhost:3000/api/get-messagesThis will return all recent conversations with their messages.
To receive real-time notifications when users send messages to your Instagram business account:
Your webhook endpoint needs to be publicly accessible. For local development, you can use tools like:
Example using ngrok:
# Start your dev server
pnpm dev
# In another terminal, expose it publicly
ngrok http 3000Copy the HTTPS URL provided by ngrok (e.g., https://abc123.ngrok.io).
- Go to your Facebook App Dashboard
- Select your app
- Navigate to Webhooks in the left sidebar (under Products)
- Click Add Subscription for your Page
- Enter your webhook URL:
https://your-domain.com/api/webhook - Enter your Verify Token (must match your
WEBHOOK_VERIFY_TOKENfrom.env.local) - Subscribe to the following fields:
messages(to receive new messages)messaging_postbacks(optional, for button interactions)message_deliveries(optional, for delivery confirmations)message_reads(optional, for read receipts)
- Click Verify and Save
Once configured, your webhook endpoint will:
- Receive a POST request whenever someone sends a message to your Instagram business account
- Log the message details to your console
- Return a 200 OK response to acknowledge receipt
The webhook logs will show:
New message from [sender_id]: {
messageId: 'mid.xxx',
messageText: 'Hello!',
attachments: 0,
timestamp: 1234567890
}
Edit /app/api/webhook/route.ts to add your custom logic for handling incoming messages. Examples:
- Store messages in a database
- Send automated responses
- Trigger notifications to your team
- Process messages with AI/NLP
- Forward messages to other systems
Success Response (200):
{
"success": true,
"data": {
"recipient_id": "123456789",
"message_id": "mid.xxx"
}
}Error Response (400/500):
{
"error": "Error description",
"details": {
"message": "Detailed error message",
"type": "OAuthException",
"code": 190
}
}- Initiated a conversation with your business account first, OR
- Sent a message to your business within the last 24 hours
Otherwise, the API will return an error. This is an Instagram platform restriction.
Be aware of Instagram API rate limits. Monitor your usage to avoid hitting limits.
For production:
- Use long-lived access tokens (60-day tokens)
- Implement token refresh logic
- Add proper logging and monitoring
- Consider implementing a queue for message sending
- Add authentication/authorization for your API endpoint
Sends a text message to an Instagram user.
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
recipient_id |
string | Yes | Instagram User ID of the recipient |
message |
string | Yes | Text message to send |
reply_to |
string | No | Message ID to reply to (creates a threaded reply). The API will automatically format this as {"message_id": "your_id"} |
Response (Success):
{
"success": true,
"data": {
"recipient_id": "123456789",
"message_id": "mid.xxx"
}
}Retrieves recent Instagram conversations and messages for your business account.
Webhook endpoint for receiving real-time Instagram message notifications.
GET (Webhook Verification): Instagram sends a GET request to verify your webhook during setup.
Query Parameters:
| Field | Type | Description |
|---|---|---|
hub.mode |
string | Will be "subscribe" |
hub.verify_token |
string | Your verify token |
hub.challenge |
string | Challenge string to echo back |
POST (Receiving Events): Instagram sends POST requests when new messages arrive.
Request Body:
{
"object": "instagram",
"entry": [
{
"id": "instagram_business_account_id",
"time": 1569262486134,
"changes": [
{
"field": "messages",
"value": {
"sender": {
"id": "sender_instagram_id"
},
"recipient": {
"id": "your_instagram_business_id"
},
"timestamp": "1527459824",
"message": {
"mid": "message_id",
"text": "Message text",
"attachments": []
}
}
}
]
}
]
}Query Parameters: None required.
Response (Success):
{
"success": true,
"data": [
{
"id": "conversation_id",
"updated_time": "2024-01-01T12:00:00+0000",
"participants": {
"data": [
{
"id": "user_id",
"username": "instagram_username"
}
]
},
"messages": {
"data": [
{
"id": "message_id",
"created_time": "2024-01-01T12:00:00+0000",
"from": {
"id": "sender_id",
"username": "sender_username"
},
"message": "Hello!"
}
]
}
}
]
}Environment Variables:
| Variable | Description |
|---|---|
PAGE_ACCESS_TOKEN |
Facebook Page Access Token |
IG_USER_ID |
Instagram Business Account ID |
WEBHOOK_VERIFY_TOKEN |
Custom token for webhook verification (you choose this) |
"Missing required environment variables"
- Ensure
.env.localexists and contains bothPAGE_ACCESS_TOKENandIG_USER_ID - Restart the dev server after adding environment variables
"Instagram API error"
- Verify your access token is valid and not expired
- Check that your app has the correct permissions
- Ensure the recipient has messaged your business recently
"OAuthException"
- Your access token may be invalid or expired
- Generate a new token with the correct permissions
"No conversations found" or empty messages
- Make sure you have the
instagram_manage_messagespermission - Users must have initiated a conversation with your business account
- Check that your Instagram account is a Business or Creator account
Error #100: "not linked to an Instagram account" or "not professional account"
- Your Instagram must be a Business or Creator account (not personal)
- Your Instagram must be linked to your Facebook Page
- Make sure
IG_USER_IDis the Instagram Business Account ID (not personal Instagram ID)
Error #10: "sent outside of allowed window"
- You can only message users within 24 hours of their last message to you
- Make sure you're using the correct Instagram-scoped ID from the conversation (not their username or public profile ID)
- Check the "Received Messages" section on the homepage to find active conversations with the correct recipient IDs
- Look for conversations marked with "CAN REPLY" badge - these are within the 24-hour window
- Click the "Use This ID" button to automatically populate the correct recipient ID
MIT
For Instagram Graph API documentation, visit:
Seems like the webhook gets a different sender id? was able to send with the app in prod, which is the pld PAT. Looked at the message from the webhook and voila "sender": { "id": "1122050090040890" },