IntegrationsYour own systems

Brand mentions as a signed webhook

The channel with nothing on the other end that we know about — which is the point. Every other provider renders a message from this same envelope, and n8n, Make, Activepieces and Pipedream take it as it is, so if you are building rather than configuring, start here.

On every plan, including Free.

[01] WHAT LANDS THERE/ YOUR ENDPOINT

A mention, as it arrives in Webhooks

Rendered by the code that sends it, from a sample GitHub post. Never the post body.
POST /fishout — mention.scored
{
  "version": 2,
  "id": "mention.scored:wrq_sample:github-sync-drops",
  "type": "mention.scored",
  "organizationId": "org_acme",
  "projectId": "prj_default",
  "occurredAt": "2026-10-19T18:30:00.000Z",
  "simulated": false,
  "data": {
    "resourceType": "mention",
    "resourceId": "mt_github-sync-drops",
    "mention": {
      "id": "mt_github-sync-drops",
      "platform": "github",
      "url": "https://github.com/okafor-dev/helpdesk-bridge/issues/214",
      "title": "Notchmail sync drops threads with more than 50 replies",
      "authorHandle": "okafor-dev",
      "postedAt": "2026-10-19T10:02:00.000Z",
      "relevance": 84,
      "relevanceReason": "relevant",
      "labels": [
        "has-a-problem",
        "complaint"
      ],
      "labelNames": [
        "Has a problem",
        "Complaint"
      ],
      "replyCall": null,
      "replyHint": null,
      "subject": "competitor",
      "sentiment": "negative",
      "status": "new"
    },
    "links": {
      "self": "https://app.fishout.ai/api/v1/orgs/org_acme/projects/prj_default/mentions/mt_github-sync-drops",
      "app": "https://app.fishout.ai/org/org_acme/project/prj_default/inbox?mt_id=mt_github-sync-drops"
    }
  }
}

Version 2 of the envelope: relevance, labels, the Reply call, subject and sentiment, with id as the stable dedupe key. The post body is deliberately absent — fetch links.self with your own API key when you need the text.

[02] CONNECT IT/ SETUP

Point FishOut at your endpoint

4 steps, a few minutes end to end.
  1. 1

    Add your endpoint

    Under Settings → Notifications, add a webhook channel and give it the URL you want the POST to reach. On a paid plan you can also attach one as the webhook action on an agent, when you only want a subset — say, posts labelled Has a problem — rather than everything clearing a floor.

    endpoint

    https://ingest.acmemail.com/fishout
  2. 2

    Subscribe to the events you need

    mention.created fires before the AI read, so relevance is still null; mention.scored fires when it is done and carries relevance, labels, the Reply call, subject and sentiment (the event name predates relevance and is kept for compatibility); reply.drafted fires when a reply draft is written. mention.status_changed is listed but not sent yet. Take mention.scored unless you have a reason not to. Webhooks are instant by default rather than batched.

  3. 3

    Verify the signature

    Every request carries an HMAC-SHA256 signature over the timestamp and the raw body. Compute it over {x-fishout-timestamp}.{raw body} with the channel’s signing secret — shown once, when you create or rotate it — compare in constant time against the v1 value, and reject anything older than five minutes so a captured request cannot be replayed.

    request headers

    x-fishout-timestamp: 1757001600
    x-fishout-signature: v1=5257a869e7...
  4. 4

    Acknowledge fast, work afterwards

    Return a 2xx quickly and do the real work off the request. A slow handler looks identical to a broken one from our side, and the retries will make it worse rather than better.

Then set the floor

This is the one channel where no floor is the right answer. Your systems can filter for themselves, and the whole point of receiving the raw stream is deciding what matters in code rather than in a settings screen. Set a floor here only when you are paying per event somewhere downstream.
Settings · Notifications

Webhooks

https://ingest.acmemail.com/…

Healthy

Relevance floor0

Eventsmention.scored
Batch burstsoff
[03] WHAT IS IN IT/ FIELDS

What Webhooks receives, field by field

Built from the event’s envelope and nothing else, so a retry renders exactly the message the first attempt would have.
Field How
x-fishout-signatureShownv1= HMAC-SHA256 over {timestamp}.{raw body} with the channel’s secret.
x-fishout-event-idShownThe envelope id: the key to deduplicate a retried delivery on.
mention.relevance, labels, replyCallShownNull on mention.created, which fires before the AI read.
mention.digestNeverOnly if you opt the channel into batching; instant by default.
links.selfShownFetch it with an API key when you need the text.
Post bodyNeverNever sent: the envelope does not carry it.
[04] EVERY EVENT/ INSTANT

One request per event, by default

A machine wants every event and can group them itself, so Webhooks channels send each one as it happens. The same event can arrive twice after a retry: key on the envelope id, which is also the x-fishout-event-id header. Opt into the digest if you would rather receive a mention.digest per window.
Request headers
content-type: application/json
x-fishout-event: mention.scored
x-fishout-event-id: mention.scored:wrq_sample:github-sync-drops
x-fishout-delivery: dlv_…
x-fishout-timestamp: 1760898600
x-fishout-signature: v1=…
[05] WHAT WILL BITE YOU/ LIMITS

What will bite you

These are the constraints of the mechanism itself rather than a vendor’s, and every one of them is documented in full. Failures are tracked per channel, so one broken destination never stops the others.

Verify against the raw request body, before any JSON parsing. Parsing and re-serialising changes whitespace and key order, and the signature will never match. This is the single most common integration bug. [source]

An unverified endpoint is a public write API. Anyone who learns the URL can post whatever they like into your systems, so signature verification is not optional hardening. [source]

Retries mean an event can arrive twice. Make the handler idempotent by keying on the envelope id, which is also in the x-fishout-event-id header — do not assume exactly-once delivery. [source]

A failed delivery is retried with a growing delay for several attempts, then recorded as failed in the channel’s delivery history. It will not quietly keep trying forever, and a signing secret that has gone missing fails the delivery rather than sending it unsigned. [source]

Reject a timestamp more than five minutes old. Without that check a captured request can be replayed against you indefinitely, signature and all. [source]

Delivery is per channel, so a webhook returning 500 marks only that channel failing. It never takes your Slack or email alerts down with it. [source]

In the channel’s delivery history
You seeIt meansDo this
destination_not_publicThe hostname resolved to a private or loopback address at send time.Expose the endpoint on a public HTTPS address.
signing_secret_missingThe channel has no signing secret, so nothing is sent unsigned.Rotate the secret in Settings → Notifications.
timeoutYour handler took too long to answer.Return a 2xx first and do the work afterwards.
redirect_not_followedThe endpoint answered 3xx; redirects are never followed.Use the final URL, including the trailing slash if it wants one.
[06] WHERE MENTIONS COME FROM/ ROUTES
[07] OPEN IN FISHOUT/ THE INBOX

From Webhooks to a drafted reply

“Notchmail sync drops threads with more than 50 replies”, open in the inbox. You send the reply; FishOut never posts.
GitHubokafor-dev
84

Notchmail sync drops threads with more than 50 replies

Has a problemComplaintCompetitor

Draft reply

Copy and open threadYou send it

Questions