Facebook Messenger HIL Channel
Facebook Messenger's Send API supports button templates and quick replies.
Button payloads can carry up to 1000 characters, making it easy to embed the
ExecutionResId directly without a correlation table.
Messenger Interactive Elements
| Element | Payload limit | Best for |
|---|---|---|
| Postback button (in Generic Template) | 1000 chars | Structured approval with field context shown as a card |
| Quick Reply | 1000 chars | Simple approve/reject after a text message |
| URL Button | n/a | Opening a web form — useful for editable fields |
Correlation Strategy: Direct Embed
Messenger button payload fields support 1000 characters — enough to embed
a prefixed GUID directly:
"payload": "HIL_APPROVE|3fa85f64-5717-4562-b3fc-2c963f66afa6"
"payload": "HIL_REJECT|3fa85f64-5717-4562-b3fc-2c963f66afa6"
Building the Message
Use a Generic Template to display the HIL fields and provide buttons:
private object BuildMessengerMessage(
IReadOnlyList<ResolvedHilField> fields,
string resId,
string recipientPsid)
{
var subtitleLines = new List<string>();
foreach (var field in fields)
{
if (field.DisplayMode == HilDisplayMode.Concealed) continue;
string val = field.DisplayMode == HilDisplayMode.ReadableMasked
? "••••••••"
: field.CurrentValue?.ToString() ?? "—";
subtitleLines.Add($"{field.Label}: {val}");
}
return new {
recipient = new { id = recipientPsid },
message = new {
attachment = new {
type = "template",
payload = new {
template_type = "generic",
elements = new[] {
new {
title = "Approval Required",
subtitle = string.Join(" | ", subtitleLines.Take(3)), // 80-char limit on subtitle
buttons = new object[] {
new {
type = "postback",
title = "Approve ✅",
payload = $"HIL_APPROVE|{resId}"
},
new {
type = "postback",
title = "Reject ❌",
payload = $"HIL_REJECT|{resId}"
}
}
}
}
}
}
}
};
}
Sending the Message
// POST to https://graph.facebook.com/v19.0/me/messages?access_token={token}
await _httpClient.PostAsJsonAsync(
$"https://graph.facebook.com/v19.0/me/messages?access_token={settings.PageAccessToken}",
message,
cancellationToken);
return NodeExecutionResult.Suspend("waiting");
Webhook Handler
Messenger webhooks deliver postback events with messaging[].postback.payload.
The webhook endpoint also receives a GET challenge request during setup — handle both.
// GET: webhook verification challenge
[HttpGet("webhooks/messenger")]
public IActionResult Verify([FromQuery] string hub_mode, [FromQuery] string hub_challenge,
[FromQuery] string hub_verify_token)
{
if (hub_mode == "subscribe" && hub_verify_token == _settings.VerifyToken)
return Content(hub_challenge);
return Forbid();
}
// POST: inbound events
[HttpPost("webhooks/messenger")]
public async Task<IActionResult> Handle([FromBody] MessengerWebhookPayload payload)
{
foreach (var entry in payload.Entry)
foreach (var messaging in entry.Messaging)
{
var postbackPayload = messaging.Postback?.Payload;
if (postbackPayload == null || !postbackPayload.StartsWith("HIL_")) continue;
var parts = postbackPayload.Split('|', 2);
var action = parts[0]; // "HIL_APPROVE" or "HIL_REJECT"
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();
}
Handling Editable Fields
For RequiredFromHuman fields, add a URL Button that opens
a hosted BizFirst web form pre-populated with the current field values. The form URL
includes a signed token carrying the ExecutionResId. On form submission,
the web endpoint calls ContinueAsync with the collected values.
{
"type": "web_url",
"url": "https://your-domain/hil/form?token={signedToken}",
"title": "Edit Values"
}
Required Configuration
| Config Field | Value |
|---|---|
PageAccessToken | @{secret:MessengerPageAccessToken} |
WebhookVerifyToken | @{secret:MessengerVerifyToken} |
RecipientPsid | @{input:messengerPsid} — the user's Page-Scoped ID |
AppSecret | @{secret:MessengerAppSecret} — for request signature validation |
ACCOUNT_UPDATE) or a template —
subject to Meta's policy approval.