Agent API & MCP Server
Give any AI agent the ability to set reminders that actually reach you. NudgeBell calls your phone, texts you, and emails you in an escalating chain until you respond — and it runs on NudgeBell's servers, so it fires whether or not your agent, laptop, or session is still alive.
Your agent can't get you? NudgeBell won't stop.
Quick start
- Go to Settings → API Keys and create a key. It is shown once.
- Paste the MCP config block below into Claude Code or Cursor.
- Say: "Remind me tomorrow at 8am to call my dentist — call my phone, and text me ten minutes later if I don't pick up."
You need credits in your wallet (every account starts with 25) and a verified phone number for call, SMS and WhatsApp steps. Email works with any balance.
Authentication
Every request — REST and MCP — carries your key in the Authorization header. Keys in the URL are not accepted.
Authorization: Bearer nb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxPlain HTTP clients may use X-API-Key instead. A key is shown once, at creation, and cannot be retrieved later. Revoking a key in Settings takes effect immediately; reminders it already created keep running.
Who gets contacted
Only you. Every reminder is delivered to the account owner's own verified phone and email. There is no recipient parameter, and any request containing one — to, phone_number, recipient, at any depth — is rejected with recipient_not_allowed. An unverified number is rejected too, even if it is yours. Because reminders only ever reach your own verified contacts, a lost key cannot be used to contact anyone else.
Acknowledgment is human-only. Your agent can read whether you pressed 1 or tapped the link; it can never mark a reminder as done on your behalf.
MCP server
Hosted at https://nudgebell.app/api/v1/mcp over Streamable HTTP. Nothing to install and nothing to run locally, so cloud agents work too.
Claude Code
claude mcp add --transport http nudgebell https://nudgebell.app/api/v1/mcp \
--header "Authorization: Bearer nb_live_YOUR_KEY"Or in your project's .mcp.json:
{
"mcpServers": {
"nudgebell": {
"type": "http",
"url": "https://nudgebell.app/api/v1/mcp",
"headers": {
"Authorization": "Bearer ${NUDGEBELL_API_KEY}"
}
}
}
}Cursor
Add to ~/.cursor/mcp.json (global) or .cursor/mcp.json (per project):
{
"mcpServers": {
"nudgebell": {
"url": "https://nudgebell.app/api/v1/mcp",
"headers": {
"Authorization": "Bearer ${env:NUDGEBELL_API_KEY}"
}
}
}
}Tools
create_reminder— schedule an escalating chainlist_reminders— what's scheduled, and how far each chain gotget_reminder— full detail of oneget_reminder_status— delivered? acknowledged? when?pause_reminder— stop it, keep itcancel_reminder— stop it and permanently delete it, along with its delivery historyget_account— contacts, credits, limits, timezone
REST API
| Method | Path | What it does |
|---|---|---|
| POST | /api/v1/reminders | Create a reminder (1–5 escalation steps). |
| GET | /api/v1/reminders | List reminders. Optional ?status=active|paused|completed&limit=N. |
| GET | /api/v1/reminders/{id} | Full detail of one reminder. |
| GET | /api/v1/reminders/{id}/status | Per-step delivery + acknowledgment status. Optional ?step=N. |
| POST | /api/v1/reminders/{id}/pause | Pause a reminder. Reversible from the dashboard. |
| POST | /api/v1/reminders/{id}/cancel | Delete permanently. Terminal, no undo. |
| GET | /api/v1/account | Verified contacts, plan, credits, limits, timezone. |
| POST | /api/v1/mcp | Remote MCP endpoint (Streamable HTTP). |
Pause, don't cancel. Cancelling permanently deletes the reminder and its delivery history — there is no undo, from the API or the dashboard. Pause stops the chain, keeps everything, and is resumable from the dashboard. The MCP tool descriptions tell your agent to prefer pause unless you explicitly say delete or stop permanently.
curl
curl -X POST https://nudgebell.app/api/v1/reminders \
-H "Authorization: Bearer $NUDGEBELL_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: dentist-2026-09-05" \
-d '{
"title": "Call the dentist",
"trigger_at": "2026-09-05T08:00",
"recurrence": "none",
"steps": [
{ "channel": "call", "message": "Time to call the dentist about your root canal.", "delay_minutes": 0, "call_repeat_count": 3 },
{ "channel": "sms", "message": "Still need to call the dentist.", "delay_minutes": 10 },
{ "channel": "email", "message": "Third reminder: call the dentist.", "delay_minutes": 30 }
]
}'trigger_atwithout an offset is read as local time in your account's timezone. A full ISO-8601 instant works too. If you send a timezonethat conflicts with your account's, yours wins and the response carries a warning. recurrence is optional and defaults to none; see Recurrence.
Node
const res = await fetch("https://nudgebell.app/api/v1/reminders", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.NUDGEBELL_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "dentist-2026-09-05",
},
body: JSON.stringify({
title: "Call the dentist",
trigger_at: "2026-09-05T08:00",
steps: [
{ channel: "call", message: "Time to call the dentist.", delay_minutes: 0 },
{ channel: "sms", message: "Still need to call the dentist.", delay_minutes: 10 },
],
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error.message);
console.log(data.reminder.id, data.estimated_credits_per_run);Python
import os, requests
res = requests.post(
"https://nudgebell.app/api/v1/reminders",
headers={
"Authorization": f"Bearer {os.environ['NUDGEBELL_API_KEY']}",
"Idempotency-Key": "dentist-2026-09-05",
},
json={
"title": "Call the dentist",
"trigger_at": "2026-09-05T08:00",
"steps": [
{"channel": "call", "message": "Time to call the dentist.", "delay_minutes": 0},
{"channel": "sms", "message": "Still need to call the dentist.", "delay_minutes": 10},
],
},
)
if not res.ok:
raise RuntimeError(res.json()["error"]["message"])
print(res.json()["reminder"]["id"])Checking status
curl https://nudgebell.app/api/v1/reminders/$ID/status \
-H "Authorization: Bearer $NUDGEBELL_API_KEY"
# => { "acknowledged": false, "chain_position": 1, "chain_length": 3,
# "steps": [ { "step_order": 1, "channel": "call", "status": "sent",
# "sent_at": "2026-09-05T02:30:11Z", "confirmed_at": null }, … ] }Recurrence
Set recurrence to daily, weekly, monthly or yearly and the whole escalation chain repeats on that cadence, starting at trigger_at. Each occurrence fires at the same local time in your account's timezone, across daylight-saving changes. Birthdays and renewals set once, years ahead, fire on the day.
next_trigger_atin every reminder response is the next occurrence. It advances after each occurrence runs.- Acknowledging a step, by pressing 1 on the call or tapping the link, ends that occurrence only. The next one still comes.
- Pause stops the series and keeps it; resume from the dashboard continues where it left off. Cancel deletes the series and its history.
- Monthly and yearly reminders set on the 29th, 30th or 31st move to the last day of the first shorter month and stay on that day afterwards. Use the 1st–28th for a fixed day every month.
Retries and idempotency
Send an Idempotency-Key header (or idempotency_key in the MCP tool call) and retries return the original reminder instead of creating a second one. The response carries Idempotent-Replay: true.
If you send no key, identical creates within two minutes are collapsed automatically, so a double-submitted tool call can't produce two reminders or two phone calls. That two-minute window applies only to this automatic mode: a key you send yourself is never recycled, so reusing it a week later still replays the original reminder rather than creating a second one. Keys are kept for 90 days and are at most 255 characters.
Limits
- 1–5 escalation steps per reminder. Step 1 fires at the trigger; each later step must be strictly later.
- Message length: 300 characters for calls, 500 for SMS, WhatsApp and email.
- Call repeat count: 1–3 (the message repeats within a single call).
- Active reminders: Starter 5, Pro 15, Power unlimited — the same limit as the web app.
- Step spacing:
delay_minutesis 0–525,600 (1 year). To remind yourself further out, set a latertrigger_at. - Scheduling horizon:
trigger_atmust be in the future and at most 5 years ahead. Anything inside that window fires on time, one-off or recurring; the cap only catches a mistyped year. Recurring reminders keep going past it. - Per key: 10 creates/minute and 60/hour; 30 cancel-or-pause calls/minute; 120 reads/minute.
- Up to 10 active API keys per account.
- MCP: up to 20 messages per JSON-RPC batch, and a 256 KB request body.
A 429 carries Retry-After and a reset_at timestamp. If a request is refused with service_unavailable, nothing was created; retry after a short pause.
These limits are per API key. Repeated failed authentication is also rate limited.
Credits
Agent-created reminders use your credits, at exactly the same per-channel and per-country rate as reminders you create in the app. There is no separate agent allowance and no separate billing. Credits are deducted when a step is actually sent, and refunded automatically when a send fails or a call goes to voicemail.
Create responses include estimated_credits_per_run and your current balance. A create is rejected with insufficient_credits if you can't afford even the first step — no reminder is created when that happens.
Errors
Every failure returns a machine-readable code and a sentence an agent can read aloud.
{
"error": {
"code": "insufficient_credits",
"message": "Not enough credits: the first step (call) needs 65 credits but the account has 12. Top up at nudgebell.app/settings and try again.",
"details": { "required": 65, "available": 12, "shortfall": 53, "channel": "call" },
"docs_url": "https://nudgebell.app/docs#errors"
}
}Over MCP the same object is returned as the tool result with isError: true, so the agent can read it and fix the call itself.
| Code | HTTP | When |
|---|---|---|
| unauthorized | 401 | The key is missing, malformed, or unknown. |
| key_revoked | 401 | The key was revoked in Settings. Create a new one. |
| no_active_subscription | 403 | No longer returned: creation is gated on credits, not on a subscription. Kept for clients that switch on it. |
| plan_limit_reached | 403 | At the plan's active-reminder limit. Details carry plan, active, limit. |
| phone_not_verified | 403 | A call/SMS/WhatsApp step was requested but no verified phone is on file. |
| recipient_not_allowed | 403 | The request tried to name a recipient. NudgeBell only contacts the account owner. |
| insufficient_credits | 402 | Not enough credits for the first step. Add credits or subscribe. Details carry required, available, shortfall. |
| invalid_trigger_time | 400 | trigger_at is unparseable, in the past, or more than 5 years ahead. |
| channel_unavailable_for_country | 400 | The verified number can't take that channel. |
| validation_error | 400 | A field is missing or malformed. Details carry the field path. |
| reminder_not_found | 404 | No such reminder on this account (or it was cancelled). |
| duplicate_request_in_progress | 409 | An identical create is already in progress. Wait a moment and read your reminders; the original is returned, not duplicated. |
| rate_limited | 429 | Per-key limit hit. Details carry limit, scope, reset_at; Retry-After is set. |
| internal_error | 500 | Something broke on our side. Nothing was created. |
| service_unavailable | 503 | Temporarily unable to accept the request. No reminder was created; retry shortly. |
Reminders fire when your agent is off
Once a reminder is created, NudgeBell schedules it on its own servers. From that moment the API call is finished and nothing about the delivery depends on your agent, your machine, your network, or the MCP connection. Every step — the call, the retry, the credit deduction, the acknowledgment check — runs on our infrastructure at its scheduled time. Close your laptop; the phone still rings.
Questions: hello@mail.nudgebell.app