← AgentSignal

Docs

Every endpoint is one HTTP call. Nothing here needs an SDK, though there is one.

Send a notification

Authenticate with an API key from your dashboard. Address a person by their recipient key.

curl -X POST https://api.agentsignal.net/v1/messages \
  -H "Authorization: Bearer as_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "u_8fk2…",
    "title": "Deploy finished",
    "body": "prod is green",
    "priority": 1
  }'
{
  "ok": true,
  "id": "b715…",
  "deliveries": { "total": 3, "sent": 3, "failed": 0, "queued": 0 }
}

deliveries.total is how many devices that person has registered. Each one counts against your plan.

Fields

toRecipient key or id. One of `to`, `group`, `broadcast`.
groupGroup slug or id — sends to everyone in it.
broadcasttrue — everyone in the label.
titleUp to 250 characters. Defaults to the label name.
bodyRequired. Up to 4096 characters.
priority-2 to 2. See below.
soundSound name, or `default`.
urlA link shown under the message.
url_titleLabel for that link.
ttl_secondsDrop the message if undelivered by then.
tagsUp to 16 strings, for your own filtering.
dataArbitrary JSON, delivered untouched.
collapse_keyReplaces an earlier undelivered message.
idempotency_keyReplay-safe. Also accepted as a header.

Priority

-2No alert. Badge only.
-1Alert with no sound.
0Default.
1Bypasses quiet hours.
2Repeats until a human acknowledges.

Priority 2 takes retry_seconds (minimum 30) and expire_seconds (maximum 10800), returns a receipt, and will POST to your callback_url when someone acknowledges.

curl -X POST https://api.agentsignal.net/v1/messages \
  -H "Authorization: Bearer as_live_…" \
  -d '{
    "to": "u_8fk2…",
    "body": "Disk at 98% on db-1",
    "priority": 2,
    "retry_seconds": 60,
    "expire_seconds": 3600,
    "callback_url": "https://example.com/acked"
  }'

Not sending twice

An agent that retries a failed request must not notify a human twice. Send an Idempotency-Key header and a replay returns the original message without dispatching anything.

curl -X POST https://api.agentsignal.net/v1/messages \
  -H "Authorization: Bearer as_live_…" \
  -H "Idempotency-Key: run-4821-step-7" \
  -d '{"to":"u_8fk2…","body":"done"}'

Errors

400The body failed validation. `details` says which field.
401Missing, malformed, revoked, or expired key.
402Monthly quota exhausted. Upgrade, or pay per send.
403The key lacks the required scope.
404No such recipient or group.
429Rate limited. `Retry-After` says how long.
{
  "ok": false,
  "error": { "code": "recipient_not_found", "message": "Could not send: recipient not found." }
}

Sandboxes

A sandbox label’s messages are swept clean on a schedule, so an agent can be pointed at one and the whole thing deleted afterwards with nothing retained. Sandbox deliveries count against your monthly quota like any other. Its keys are visibly different — as_test_… instead of as_live_… — so a test key cannot quietly end up in production.

Scheduling

A schedule sends the same message on a rule instead of when you call us. Describe it in plain language — every weekday at 09:00, the 1st of every month, every two hours — or give a cron expression if you already think that way. A timezone is required, so 09:00 stays 09:00 through a daylight saving change rather than drifting by an hour.

curl -X POST https://api.agentsignal.net/v1/schedules \
  -H "Authorization: Bearer $AGENTSIGNAL_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Standup nudge",
    "kind": "recurring",
    "timezone": "America/New_York",
    "freq": "weekly",
    "days_of_week": [1, 2, 3, 4, 5],
    "at_time": "09:00",
    "payload": { "to": "u_...", "body": "standup in 5" }
  }'

GET /v1/schedules/:id/preview reads the rule back as actual dates, which is the fastest way to notice you picked the wrong day. A rule that would never fire — a one-off already past, the 30th of February — is refused when you write it rather than silently never arriving. GET /v1/schedules/:id/runs shows what each run actually did, so a schedule that has been failing quietly is visible.

Each run carries an idempotency key derived from its slot, so a schedule cannot notify anyone twice for the same due time.

Paying per notification

An agent with a wallet and no AgentSignal account can send by paying for it, in USDC, over x402. No key, no signup. The label owner turns this on per label; while it is off, the endpoint answers 404 for their recipient keys.

Send without payment and you get a 402 describing exactly what it costs.

curl -X POST https://api.agentsignal.net/v1/x402/messages \
  -H "Content-Type: application/json" \
  -d '{"to":"u_8fk2…","body":"build finished"}'
HTTP/1.1 402 Payment Required

{
  "x402Version": 1,
  "error": "This send costs $0.0010.",
  "accepts": [{
    "scheme": "exact",
    "network": "base",
    "maxAmountRequired": "1000",
    "asset": "0x8335…2913",
    "payTo": "0x…"
  }]
}

Pay it and repeat the request with an X-PAYMENT header. Any x402 client does this for you — with x402-fetch the whole thing is one call:

import { wrapFetchWithPayment } from 'x402-fetch';
import { privateKeyToAccount } from 'viem/accounts';

const pay = wrapFetchWithPayment(fetch, privateKeyToAccount(KEY));

await pay('https://api.agentsignal.net/v1/x402/messages', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ to: 'u_8fk2…', body: 'build finished' }),
});

Notifications cost $0.0002 per device the person has registered. Payments below $0.001 cost more in gas than they collect, so that is the minimum a 402 will ask for — and the difference is not kept. It stays as credit on the label and pays for the next sends, which go through with no payment round trip at all.

Ask what a send costs, without committing to one:

curl https://api.agentsignal.net/v1/x402/quote/u_8fk2…
{ "ok": true, "devices": 3, "cost": "$0.0006", "credit_micros": 800 }

Paid sends address exactly one person. Groups and broadcasts are not payable, so being given one recipient key never buys the ability to reach a whole label. Idempotency-Key works here too, and a replay is free — a retrying agent is never charged twice for one notification.

Pairing a device

People do not need an account. Create a person in the dashboard, hit Pair a device, and they scan the QR or type the eight characters into the app. Codes are single-use and expire in fifteen minutes.

The QR encodes https://agentsignal.net/enroll/CODE. The system camera opens it straight into the app, and on a phone without the app it opens a page showing the code instead of failing.