Facebook Messenger Connector
Send and receive messages through the Facebook Messenger Platform API using a Facebook Page.
Package
dotnet add package Ratatosk.Facebook
Required settings
| Parameter | Type | Description |
|---|---|---|
PageAccessToken | string | Long-lived Facebook Page access token (starts with EAA) |
PageId | string | Facebook Page ID (numeric) |
Optional settings
| Parameter | Type | Description |
|---|---|---|
WebhookUrl | string | URL for receiving Messenger webhook events |
VerifyToken | string | Token used for webhook verification challenge |
The PageAccessToken must be a long-lived token (valid ~60 days). Generate it from Facebook Developer Console → Your App → Messenger → Settings → Page Access Token.
Schema
| Property | Value |
|---|---|
| Provider | Facebook |
| Type | Messenger |
| Version | 1.0.0 |
| Capabilities | SendMessages, ReceiveMessages, MediaAttachments, InteractiveContent |
| Content types | PlainText, Media, Json, Button, QuickReply, Carousel, ListPicker |
| Endpoints | UserId (PSID), Id (page-scoped ID) |
| Authentication | API Key (PageAccessToken) |
Send examples
Text message
var settings = new ConnectionSettings()
.SetParameter("PageAccessToken", "EAA...")
.SetParameter("PageId", "1234567890");
var connector = new FacebookMessengerConnector(settings);
await connector.InitializeAsync(ct);
var message = new MessageBuilder()
.WithId("fb-1")
.To(Endpoint.User("facebook-user-psid"))
.WithText("Hello from Facebook Messenger")
.WithMessagingType("RESPONSE")
.Build();
var result = await connector.SendMessageAsync(message, ct);
Message with media (image)
new MessageBuilder()
.To(Endpoint.User("psid-123"))
.WithContent(new MediaContent(MediaType.Image, "photo.jpg",
"https://example.com/photo.jpg"))
.Build();
Message with quick replies
new MessageBuilder()
.To(Endpoint.User("psid-123"))
.WithText("Choose an option:")
.WithQuickReplies("[{\"content_type\":\"text\",\"title\":\"Yes\",\"payload\":\"YES\"},{\"content_type\":\"text\",\"title\":\"No\",\"payload\":\"NO\"}]")
.Build();
Message with buttons
// Using the convenience method
new MessageBuilder()
.To(Endpoint.User("psid-123"))
.WithButton("Open Website", ButtonType.Url, "https://example.com")
.Build();
Message with quick reply options
// Single quick reply option
new MessageBuilder()
.To(Endpoint.User("psid-123"))
.WithQuickReply("Yes", "YES_PAYLOAD")
.Build();
Message with carousel (horizontal cards)
// Using the sub-builder API
new MessageBuilder()
.To(Endpoint.User("psid-123"))
.WithCarousel(carousel => carousel
.AddCard("https://example.com/img1.jpg", "Product A", "Amazing", card =>
card.WithButton("Buy", ButtonType.Postback, "BUY_A")
.WithButton("Details", ButtonType.Url, "https://example.com/a"))
.AddCard("https://example.com/img2.jpg", "Product B", "Even better", card =>
card.WithButton("Buy", ButtonType.Postback, "BUY_B")
.WithButton("Details", ButtonType.Url, "https://example.com/b")))
.Build();
Message with list picker
// Using the sub-builder API
new MessageBuilder()
.To(Endpoint.User("psid-123"))
.WithListPicker(list => list
.WithStyle(ListPickerStyle.Compact)
.AddItem("Pizza", "Delicious cheese pizza")
.AddItem("Burger", "Juicy beef burger"))
.Build();
Webhook setup
Facebook Messenger uses webhooks for inbound messages and delivery events.
1. Configure webhook in Facebook Developer Console
- Go to your App → Messenger → Settings
- Set the callback URL to your endpoint
- Set a verify token (must match the one in your settings)
2. Handle verification challenge
Facebook sends a GET request with hub.mode, hub.verify_token, and hub.challenge parameters. The connector handles this automatically via ReceiveMessagesAsync.
3. Process inbound messages
[HttpGet("/webhooks/facebook")]
[HttpPost("/webhooks/facebook")]
public async Task<IActionResult> FacebookWebhook(CancellationToken ct)
{
if (Request.Method == "GET")
{
// Verification challenge — handled by the connector
using var reader = new StreamReader(Request.Body);
var query = Request.QueryString.Value!;
var source = MessageSource.UrlPost(query);
var result = await _connector.ReceiveMessagesAsync(source, ct);
return Content(result.Data?.Messages.FirstOrDefault()?.Id ?? "error");
}
// Inbound message
using var postReader = new StreamReader(Request.Body);
var body = await postReader.ReadToEndAsync(ct);
var msgSource = MessageSource.Json(body);
// Verify signature
var signature = Request.Headers["X-Hub-Signature-256"].FirstOrDefault();
if (!IsValidSignature(body, signature))
return Unauthorized();
var result = await _connector.ReceiveMessagesAsync(msgSource, ct);
return Ok();
}
Validate X-Hub-Signature-256 header using your Facebook app secret before processing.
Error codes
Facebook-specific error codes are defined in FacebookErrorCodes with domain "Facebook".
| Code | Description |
|---|---|
INVALID_ACCESS_TOKEN | Page Access Token is invalid or expired |
MISSING_PAGE_ID | Page ID is required but not configured |
INVALID_PAGE_ID | Page ID does not exist or token lacks permission |
CONNECTION_TEST_FAILED | Connection test to Graph API failed |
GRAPH_API_ERROR | Facebook Graph API returned an error |
OPERATION_NOT_SUPPORTED | Operation not available for the current Page or API version |
Standard MessagingErrorCodes are also used — see the error codes reference.
Original provider codes
Facebook errors are assigned directly via ConnectorException without mapping from Graph API error codes. The Graph API returns its own error objects (e.g., {"error": {"message": "...", "type": "OAuthException", "code": 190}}) which are surfaced through the GRAPH_API_ERROR code.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
INVALID_CREDENTIALS | Token expired or invalid | Regenerate long-lived token (valid ~60 days) |
INVALID_RECIPIENT | PSID doesn't exist or page can't message user | User must have interacted with the page |
| Webhook verification fails | VerifyToken mismatch | Ensure VerifyToken matches Facebook Console |
| Message not delivered | User blocked the page | Remove user from your store |
RATE_LIMITED | Too many messages | Facebook limits: 250 messages per page per hour (standard) |