Instagram
Instagram HIL Channel
Instagram messaging runs on the same Meta Messaging API as Facebook Messenger. The implementation is almost identical — the key difference is the access token scope and the fact that Instagram users are addressed by their Instagram-Scoped User ID (IGSID), not a Facebook PSID.
Instagram vs Facebook Messenger API
| Aspect | Facebook Messenger | |
|---|---|---|
| API endpoint | /me/messages with Page token | /me/messages with Page token connected to Instagram Business Account |
| User identifier | Page-Scoped ID (PSID) | Instagram-Scoped User ID (IGSID) |
| Button templates | Full Generic Template supported | Generic Template not supported — use quick replies or plain text + postback |
| Quick replies | Supported | Supported |
| 24-hour window | Yes | Yes |
| Webhook events | messages, messaging_postbacks | messages, messaging_postbacks |
No Generic Template on Instagram
The card-style Generic Template that works well in Messenger is not available in Instagram DMs.
Use Quick Replies for approve/reject flows, or send a text message followed
by a list of quick reply options.
Building the Message with Quick Replies
private object BuildInstagramMessage(
IReadOnlyList<ResolvedHilField> fields,
string resId,
string recipientIgsid)
{
var lines = new List<string> { "📋 *Approval Required*\n" };
foreach (var field in fields)
{
if (field.DisplayMode == HilDisplayMode.Concealed) continue;
string val = field.DisplayMode == HilDisplayMode.ReadableMasked
? "••••••••"
: field.CurrentValue?.ToString() ?? "—";
lines.Add($"• {field.Label}: {val}");
}
lines.Add("\nPlease select an action:");
return new {
recipient = new { id = recipientIgsid },
message = new {
text = string.Join("\n", lines),
quick_replies = new object[] {
new {
content_type = "text",
title = "Approve ✅",
payload = $"HIL_APPROVE|{resId}"
},
new {
content_type = "text",
title = "Reject ❌",
payload = $"HIL_REJECT|{resId}"
}
}
}
};
}
Sending the Message
// Identical endpoint to Messenger — same Graph API, different token scope
await _httpClient.PostAsJsonAsync(
$"https://graph.facebook.com/v19.0/me/messages?access_token={settings.PageAccessToken}",
message,
cancellationToken);
return NodeExecutionResult.Suspend("waiting");
Webhook Handler
Instagram quick reply responses arrive as regular messaging events with
message.quick_reply.payload. The handler is nearly identical to Messenger.
[HttpPost("webhooks/instagram")]
public async Task<IActionResult> Handle([FromBody] MessengerWebhookPayload payload)
{
foreach (var entry in payload.Entry)
foreach (var messaging in entry.Messaging)
{
// Quick reply response
var quickReplyPayload = messaging.Message?.QuickReply?.Payload;
if (quickReplyPayload != null && quickReplyPayload.StartsWith("HIL_"))
{
var parts = quickReplyPayload.Split('|', 2);
var action = parts[0];
var resId = parts[1];
var responseData = new Dictionary<string, object>
{
["approved"] = action == "HIL_APPROVE",
["respondedBy"] = messaging.Sender.Id
};
_ = Task.Run(() => _continuationOrchestrator.ContinueAsync(resId, responseData));
}
}
return Ok();
}
Sharing the Same Webhook
Meta allows a single webhook URL to receive events from multiple products (Messenger, Instagram, WhatsApp) by subscribing to different object types. You can route all three to the same controller and dispatch by the object type in the payload:
// payload.Object == "instagram" → Instagram handler
// payload.Object == "page" → Messenger handler
// payload.Object == "whatsapp_business_account" → WhatsApp handler
Required Permissions
| Permission | Purpose |
|---|---|
instagram_basic | Read Instagram Business Account info |
instagram_manage_messages | Send and receive DMs |
pages_messaging | Access to the connected Facebook Page for token |
pages_read_engagement | Required alongside pages_messaging |
Required Configuration
| Config Field | Value |
|---|---|
PageAccessToken | @{secret:InstagramPageAccessToken} — token for the Page connected to the Instagram Business Account |
WebhookVerifyToken | @{secret:InstagramVerifyToken} |
RecipientIgsid | @{input:instagramScopedUserId} |
Getting the IGSID
The Instagram-Scoped User ID (IGSID) is only available after the user has sent your account
a message first — it appears in the inbound webhook event's
sender.id. You cannot
initiate a first contact unless the user has already messaged you within the 24-hour window.