On this page

Guides Agents and MCP

Webhooks

Send data into a radar with an inbound webhook, and have On My Radar call your server with signed outbound events when blips and comments change.

Updated October 30, 2026 · Checked against On My Radar build 2c4905d

Webhooks connect On My Radar to other systems in both directions.

  • Inbound webhooks let another service create blips on a radar by sending a request to a secret URL.
  • Outbound subscriptions make On My Radar call your server, with a signed message, when blips or comments change on a radar.

Both are set up in a radar's settings. You need to be able to manage the radar.

Inbound webhooks

An inbound webhook is a URL. Anything that can send an HTTP request, such as a form service, a monitoring tool or a script, can post to it, and each request becomes a blip.

Create one

  1. Open the radar, then Settings, then the Webhooks tab.
  2. Under Inbound webhooks, choose New webhook.
  3. Fill in What will be sending? (a name so you know what to revoke later) and, if you like, Tags on every blip it creates.
  4. Choose the body: Work it out from the payload or Write my own template.
  5. Choose Create and show me the URL. Copy the URL, then choose Done, I have saved it.

The URL looks like https://onmyradar.io/hooks/<id>/omr_hook_…. The part after the id is the secret. It is shown once. The URL is the only credential, so keep it out of public code. If it leaks, use Issue a new URL on the webhook, which stops the old one at once.

Other row actions are Pause and Resume, and Delete.

Note: The drop box and smart radars do not take inbound webhooks. Agents with the manage scope can also create and manage them with create_webhook, list_webhooks, update_webhook, rotate_webhook_secret and delete_webhook. See MCP reference.

Send a request

curl -X POST 'https://onmyradar.io/hooks/<id>/<token>' \
  -H 'content-type: application/json' \
  -d '{"name":"Ada Lovelace","email":"[email protected]","message":"Saw your work and would like to talk."}'

A successful call returns HTTP 202:

{ "ok": true, "blipId": "…" }

PUT works the same as POST, for senders that can only send a PUT. A GET to the URL creates nothing and answers {"ok":true,"message":"This webhook accepts POST. Nothing was captured."}, whether or not the token is right.

JSON, form-encoded, multipart (text fields only) and plain text bodies are all accepted. A body with no content type that starts with { or [ is read as JSON.

How a payload becomes a blip

With Work it out from the payload, On My Radar looks for familiar field names:

  • Title from the first of title, subject, name, event, headline.
  • Link from the first of url, link, permalink, html_url. Only http and https addresses are kept.
  • Body from the first of body, text, message, content, description, note, summary.
  • Tags from a tags field, as an array or a string.

If the payload has one obvious body field, it is used as written. Otherwise every field becomes a labelled line, such as **Email:** [email protected]. In the example above there is no title field, so the blip is titled "Ada Lovelace" from name, and its body lists the email and message as labelled lines.

With Write my own template, you write the body (and optionally a title) with {{field.path}} placeholders, for example {{customer.name}} wrote: {{message}}. A path that does not exist becomes empty. Array positions are written items.0.name or items[0].name.

Errors and limits

  • 202: a blip was created.
  • 400: the webhook is paused, the account behind it is not active, or the payload was empty.
  • 403 or 404: the person who created it can no longer write to the radar.
  • 404: the id or token is wrong. The two are not told apart.
  • 413: the request was over 64 KiB.
  • 429: more than 60 requests in a minute to this webhook.

A long body is cut at 20,000 characters.

Inbound webhooks have no signature. The secret URL is the credential. A webhook runs as the person who created it, so if they lose write access to the radar or are suspended, it stops working.

Outbound subscriptions

An outbound subscription is a URL on your server that On My Radar calls when something changes on a radar.

Events

  • blip.created
  • blip.updated
  • blip.deleted
  • comment.created
  • subscription.test, sent only when you choose Send test.

A subscription picks which of the first four it wants. Smart radars cannot have subscriptions, and a radar can have up to 20.

Create one

Only a signed-in person can create, pause, resume, rotate or test a subscription, in the app. A token or a connected agent cannot.

  1. Open the radar, then Settings, then Webhooks.
  2. Under Outbound webhooks, choose New subscription.
  3. In New outbound subscription, enter a Name, the Receiver URL, and tick Events to send.
  4. Choose Create subscription.

The receiver URL must be a public https address of at most 2,048 characters, with no username, password or fragment. Addresses that point to private networks are refused.

A panel then shows the Signing secret — shown once, which starts with omr_whsec_. Choose Copy secret, store it where your receiver can read it, then choose I have saved it.

Each subscription row has Pause or Resume, Send test, Delivery history, Rotate secret and Delete. Send test needs the subscription to be enabled, and is refused while any delivery for it is still waiting. After Rotate secret the old secret stops working, and you copy the new one.

What a delivery looks like

Each delivery is a POST with a JSON body and these headers:

  • Content-Type: application/json.
  • X-OMR-Event-Id: the event id, such as evt_…. The same event keeps the same id across retries.
  • X-OMR-Delivery-Attempt: 1 for the first try, 2 for the next, up to 6.
  • X-OMR-Signature: t=<unix seconds>,v1=<hex>.

The body:

{
  "id": "evt_6f1c…",
  "event": "blip.created",
  "occurredAt": "2026-10-30T14:02:11.000Z",
  "radar": { "id": "…", "name": "Product ideas" },
  "actor": { "kind": "user", "id": "…", "name": "Ada", "agentId": null },
  "blip": { "id": "…", "title": "…", "body": "…", "…": "the blip as it is now" },
  "changes": { "title": { "from": "Old", "to": "New" } }
}
  • blip is the whole blip at the time of the event, including its full body and email notes.
  • changes appears only on blip.updated, with from and to for each field that changed.
  • On comment.created the payload also carries a comment with id, blipId, body, parentId, visibility, createdAt and updatedAt.
  • actor.agentId is set when the change was written under an agent's name.

Note: Treat the payload as untrusted text, like any user content. Do not run it or paste it into commands.

Verify the signature

Check the signature on every delivery before you act on it. The signature is an HMAC-SHA256, in hex, of the string <t>.<raw body>, keyed with your whole secret including its omr_whsec_ prefix.

import { createHmac, timingSafeEqual } from 'node:crypto';

// rawBody must be the exact bytes received, before JSON.parse.
export function verify(secret, rawBody, header) {
  const m = /^t=(\d+),v1=([a-f0-9]{64})$/.exec(header ?? '');
  if (!m) return false;
  const [, t, v1] = m;
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // 5 minutes
  const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest();
  const given = Buffer.from(v1, 'hex');
  return given.length === expected.length && timingSafeEqual(given, expected);
}

Reply with any 2xx status within 10 seconds. The response body is not read.

Retries and pausing

A delivery counts as failed if your server does not reply with a 2xx, does not answer within 10 seconds, redirects, or cannot be reached. On failure it is tried again after 1 minute, 5 minutes, 30 minutes, 2 hours and 3 hours. That is six attempts in all, over about five and a half hours.

After the sixth failure On My Radar:

  1. Pauses the subscription.
  2. Discards the events still waiting for it.
  3. Notifies whoever created it: "Outbound delivery paused after six failed attempts. Check Webhooks in radar settings, then resume."

Fix your receiver, then choose Resume. Events that happened while it was paused are not replayed, so catch up as described below.

A delivery also stops if its creator is no longer an active person with write access to the radar. This is checked on every attempt.

Delivery history

Choose Delivery history on a subscription. The last 100 attempts are kept, each with its event, attempt number, result and time. Response bodies are not stored.

Catch up after an outage

Because paused and failed events are not replayed, a receiver should reconcile. Ask for everything changed since your last successful sync:

curl 'https://onmyradar.io/api/radars/<radarId>/blips?updatedSince=2026-10-30T00:00:00Z&sort=updated&includeArchived=true' \
  -H 'Authorization: Bearer omr_pat_…'

Use a token with at least the read scope. See Tokens, scopes and revocation.

Subscriptions from an agent

An agent with the manage scope can list a radar's subscriptions, delete them and read their delivery history (list_subscriptions, delete_subscription, list_subscription_deliveries). It cannot create, pause, resume, rotate or test one.

Next