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.
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
- Open the radar, then Settings, then the Webhooks tab.
- Under Inbound webhooks, choose New webhook.
- 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.
- Choose the body: Work it out from the payload or Write my own template.
- 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_secretanddelete_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. Onlyhttpandhttpsaddresses are kept. - Body from the first of
body,text,message,content,description,note,summary. - Tags from a
tagsfield, 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.createdblip.updatedblip.deletedcomment.createdsubscription.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.
- Open the radar, then Settings, then Webhooks.
- Under Outbound webhooks, choose New subscription.
- In New outbound subscription, enter a Name, the Receiver URL, and tick Events to send.
- 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 asevt_…. 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" } }
}blipis the whole blip at the time of the event, including its full body and email notes.changesappears only onblip.updated, withfromandtofor each field that changed.- On
comment.createdthe payload also carries acommentwithid,blipId,body,parentId,visibility,createdAtandupdatedAt. actor.agentIdis 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:
- Pauses the subscription.
- Discards the events still waiting for it.
- 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
- Limits: webhook and subscription limits with the rest.
- Tokens, scopes and revocation: the token to use for reconciling.
- MCP reference: the webhook and subscription tools.