Skip to main content

Listen for Comments

Comments reach your integration through the comment.received webhook event. There is no polling endpoint and no need for one — the event fires for every new comment on a connected Instagram or Facebook Page post, whether or not one of your automations answers it.

Subscribe

Add comment.received to a webhook endpoint's subscribed_events:

curl -X POST "https://api.sendseven.com/api/v1/webhooks" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"name": "Comment listener",
"url": "https://example.com/hooks/sendseven",
"subscribed_events": ["comment.received"]
}'

See Webhook Setup for endpoint management, retries and signature verification.

The event

comment.received uses the standard webhook envelope. Comments are not conversations — a comment has no conversation and no message — so data carries the post and the comment directly.

The full payload example and field-by-field reference live in Webhook Events → Comment Events. These are the fields you make decisions on:

FieldWhy it matters
event_idMeta's own comment id. Use it to de-duplicate — see below.
data.comment.external_idThe same comment id, and the value you pass to the private-reply endpoint.
data.comment.private_reply_window_expires_atWhen the 7-day private-reply window closes. null means the platform gave no reliable comment timestamp.
data.auto_repliedtrue when a SendSeven rule or flow already answered this comment. Skip it.
data.channel_idThe channel the post belongs to. Matches channel_id on message events.
author.username is null on Facebook

Instagram supplies the commenter's handle. Facebook does not expose handles at all — data.comment.author.name is a display name, not something you can address or link to. Do not build a mention out of it.

De-duplicate on event_id

Meta redelivers webhooks. event_id is set to Meta's comment id, which is stable, so the same comment always produces the same event_id. Store it and ignore repeats.

SendSeven already suppresses redeliveries it recognises, but your endpoint must still be idempotent — an HTTP retry after your own timeout will legitimately deliver the same event twice.

Scope events to specific channels

Comment events respect a webhook endpoint's channel source filter. With source_filter_mode: "selected", comment.received is matched on data.channel_id, exactly like message events.

What fires and what does not

Event on the platformcomment.received
New top-level comment on your postYes
Reply to another comment on your postYes — is_reply: true, with parent_external_comment_id set
Comment editedNo
Comment deleted or hiddenNo
Your own account's comment (including your public replies)Not delivered as an actionable comment — Meta echoes your own comments back, and answering them would DM yourself

Handling an event

The decision an integration actually has to make is short: has it already been answered, is the window still open, and do I want to answer it.

from datetime import datetime, timezone

def handle_comment_received(event):
# 1. Idempotency — Meta redelivers, and so do our retries.
if already_processed(event["event_id"]):
return

data = event["data"]
comment = data["comment"]

# 2. A SendSeven rule or flow may already own this comment.
# Meta permits exactly one private reply per comment, ever.
if data.get("auto_replied"):
record_seen(event["event_id"])
return

# 3. The 7-day window. `null` means we cannot prove it is open.
expires_at = comment.get("private_reply_window_expires_at")
if not expires_at:
return
if datetime.fromisoformat(expires_at.replace("Z", "+00:00")) <= datetime.now(timezone.utc):
return

# 4. Your own matching logic.
if "price" in (comment.get("text") or "").lower():
send_private_reply(
comment_id=comment["external_id"],
channel_id=data["channel_id"],
text="Hi! Our current pricing is at example.com/pricing",
)

record_seen(event["event_id"])
Check auto_replied before you answer

If you run your own tooling alongside SendSeven auto-reply rules, auto_replied is what stops you racing them for the single private reply Meta allows. The loser of that race gets a permanent 409.

Reacting inside SendSeven instead

You do not have to receive the webhook at all to act on comments:

  • Auto-reply rules match keywords and send a private reply or start a flow, with no code.
  • The comment_received flow trigger starts a flow directly from a matching comment.

The webhook and the automations are independent: comment.received fires either way, so you can log and analyse every comment while SendSeven handles the answering.

Next steps