# Wake your agent

Maplith wakes your agent when something in your business moves. Instead of
your agent asking Maplith every few minutes whether there is work, Maplith
calls an address of yours the moment a signal your agent cares about is
raised, or a person decides one of its requests.

**On as soon as you set your agent up.** Wake-ups start for your workspace
the moment you set up its first wake endpoint, in Agents & access › Connections or by an agent
whose key may act on its own. They need your organisation to have accepted
the current Data Processing Agreement, under which Maplith's processing
description covers notices to an endpoint you set up; until it has, nothing is
sent, and setting up an endpoint tells you why. An admin can turn wake-ups off
for the workspace in **Agents & access › Connections › Wake-ups**; then nothing is
sent and no endpoint can be added until an admin turns them back on, and
Maplith does not switch them on over that choice. If Maplith switches a
workspace's wake-ups off, for misuse or an incident, setting up an endpoint
does not turn them on again until Maplith does. Your agent can always find its
work with `list_signals`: polling keeps working, and stays the fallback.

## How a wake-up works

1. A signal is raised that your agent's key may see and the wake-up's filter
   asks for — or a person approves or denies one of the key's pending
   requests.
2. Maplith sends one signed `POST` to your endpoint: your agent runtime, a
   scheduler, or a webhook that starts your agent.
3. Your agent starts and calls `get_task_context` (or `list_signals`)
   **with its own key**, so it is shown exactly what that key may see, as on
   any other call.

**A wake-up is a nudge, not the work.** It names signal ids and nothing else:
no title, no description, no object or field name, no figures and never a row.
What the signal is about is read through Maplith, with the key's scope and
your action policy applied, so a wake-up that reached the wrong address would
tell its reader nothing.

## Set one up

A wake-up belongs to one agent key; a key can have up to three.

- **In the app.** An admin who sees the whole map opens **Agents & access › Connections › Wake-ups**, chooses the key and adds a wake-up: the address to call
  (`https`, port 443), which kinds of signal, objects or domains, the least
  severe signal that counts, and whether a person's decision on one of the
  key's requests wakes it too. Choosing nothing in a list means all of it,
  within what the key sees. **Send a test** checks your endpoint receives and
  verifies a wake-up.
- **By the agent itself.** A key with the policy "can act" (`autonomous`)
  manages its own wake-ups over MCP: `set_wake_subscription` (which also
  sends a signed test wake-up to a new endpoint, a re-keyed one, or on
  request) and `remove_wake_subscription`. Every key reads its own wake-ups
  in `get_map_overview`'s `your_key.wake_ups`; an admin sets them up for a
  key that cannot, and sends a test from Agents & access › Connections.

The filter only ever narrows what the key already sees. An object or domain
outside the key's scope is refused when the wake-up is saved, and if the key's
scope is narrowed later, a wake-up names only the signals the key may see when
it is sent.

**The signing secret is shown once**, when the wake-up is made and when you
replace it. Store it in your runtime's secret store at once: Maplith cannot
show it again. After a replacement the old secret keeps signing beside the new
one for 24 hours, so your endpoint can switch over without missing a wake-up.

## What your endpoint receives

| Header | Value |
|---|---|
| `X-Maplith-Signature` | `t=<unix seconds>,v1=<hex>` — during the 24 hours after a secret is replaced, two `v1` values |
| `X-Maplith-Delivery` | `wd_<id>`, the same as `delivery_id` in the body; it stays the same when a wake-up is sent again |
| `Content-Type` | `application/json` |

```json
{
  "type": "signals.raised",
  "delivery_id": "wd_6f1c…",
  "workspace_id": "…",
  "subscription_id": "…",
  "sent_at": "2026-10-11T09:20:04Z",
  "signals": [{ "id": "…", "kind": "rule_fired", "severity": "attention", "episode": 3 }],
  "more": 0
}
```

- `type` is `signals.raised`, `approval.decided` or `test`.
- `signals` names at most 50; `more` counts the rest, which your agent reads
  with `list_signals`.
- `approval.decided` carries `"approval": { "request_id", "decision", "signal_id" }`
  instead: `decision` is `approved` or `denied`, and `request_id` is
  the `approval_id` your agent was handed when the request waited for a
  person. Your agent asks again with the same idempotency key to receive what
  was decided; nothing that grants anything travels in a wake-up.
- After a day's cap held wake-ups back, the next one says so:
  `"held_back_since": { "count": 7, "since": "…" }`.

## Verify every wake-up

The signature is your endpoint's only proof that a call came from Maplith:
Maplith holds no token or password for your endpoint, ever. `v1` is
HMAC-SHA256 over `<t>.` followed by the raw request body, keyed with your
signing secret. Check it over the bytes you received, **before** any JSON
parsing; compare in constant time; refuse a `t` more than five minutes from
your clock; accept when **any** `v1` value matches. Then drop a
`delivery_id` you have already handled.

Node, no dependencies:

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

// header: the X-Maplith-Signature header. rawBody: the request body exactly as
// received (a Buffer or a string), before any JSON parsing. secret: your
// mwk_… signing secret.
export function verifyWake(header, rawBody, secret, nowSeconds = Math.floor(Date.now() / 1000)) {
  const m = /^t=([0-9]{1,12})((?:,v1=[0-9a-f]{64}){1,2})$/.exec(String(header ?? '').trim());
  if (!m || Math.abs(nowSeconds - Number(m[1])) > 300) return false;
  const expected = createHmac('sha256', secret).update(`${m[1]}.`).update(rawBody).digest();
  let ok = false;
  for (const value of m[2].slice(1).split(',')) {
    ok = timingSafeEqual(Buffer.from(value.slice(3), 'hex'), expected) || ok;
  }
  return ok;
}
```

Python, standard library only:

```python
import hashlib
import hmac
import re
import time

_SIGNATURE = re.compile(r"t=([0-9]{1,12})((?:,v1=[0-9a-f]{64}){1,2})")


def verify_wake(header, raw_body, secret, now_seconds=None):
    """header: the X-Maplith-Signature header. raw_body: the request body as
    bytes, exactly as received, before any JSON parsing. secret: your mwk_...
    signing secret."""
    m = _SIGNATURE.fullmatch((header or "").strip())
    now = int(time.time()) if now_seconds is None else now_seconds
    if not m or abs(now - int(m.group(1))) > 300:
        return False
    expected = hmac.new(secret.encode(), m.group(1).encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    ok = False
    for value in m.group(2)[1:].split(","):
        ok = hmac.compare_digest(value[3:], expected) or ok
    return ok
```

Both are tested against the code Maplith signs with. It is the same scheme
Maplith checks on the reports a live check sends in, so one helper serves both
directions.

## Bursts and daily limits

A wake-up usually costs you a model run, and Maplith cannot see that cost. The
number of wake-ups is what Maplith can count, so that is what you limit.

- **Bursts become one wake-up.** The first matching signal opens a window
  (60 seconds unless you change it, up to 15 minutes; 10 to 15 seconds suits
  new support conversations). Everything that matches inside it goes out
  together: a scan that raises forty findings wakes your agent once. A
  person's decision is sent at once.
- **A daily cap per wake-up** (24 unless you change it), and a ceiling for the
  whole workspace across all of them (100). Signal wake-ups and tests count
  against both. A person's decision has its own count of the same size per
  wake-up, so the day's signals never keep a decision from your agent, and
  it does not count toward the workspace ceiling. The day is the Copenhagen
  calendar day.
- **At a wake-up's cap nothing more is sent that day,** except a decision and
  a wake-up that names an urgent signal: urgent work is sent past the cap, and
  counted, until the wake-up has sent four times its cap that day.
- **At the workspace ceiling nothing more is sent that day but decisions,**
  urgent or not: that ceiling is yours, and nothing passes it. Nothing is
  lost: the signals are still in `list_signals`, and the next wake-up says
  how many were held back and since when. Agents & access › Connections and
  `get_map_overview`'s `your_key.wake_ups` show where each wake-up stands,
  such as "9 of 24 wake-ups today".
- **The limits are yours.** An admin sets each wake-up's most wake-ups a day
  and shortest window, and the workspace ceiling. An agent may lower its own
  cap or wait longer, never more than you allow. While you have paused one of
  a key's wake-ups or tightened its limits, the agent can neither remove it
  nor set up another one beside it.

## When your endpoint does not answer

- Your endpoint must answer with a `2xx` status within 10 seconds. Maplith
  does not follow redirects, and keeps only the status of your answer.
- **Retried**: a timeout, a network error, `408`, `429` and `5xx`, after
  about 30 seconds, 2 minutes, 10 minutes, 30 minutes, 2 hours and 6 hours —
  seven attempts over about nine hours. Any other status is not retried.
- A wake-up that is not delivered stays on record with its last status.
  **Three in a row** set the wake-up to failing: Maplith stops calling it and
  raises a `link_dead` signal with the steps to repair it. An admin presses
  **Resume** once the endpoint answers again.
- **410 Gone** ends the wake-up: your endpoint says it is not coming back.
- Wake-ups can arrive out of order, and the same one can arrive twice. Use
  `delivery_id` to drop repeats, and `take_signal` so two agents do not do
  the same work.
- While an admin has paused every agent in the workspace, nothing is sent.
  What matched in the meantime arrives as one wake-up when they resume.

## Start your own Claude agent

A wake-up reaches an address of yours, and something there has to start your
agent. Starting it takes a credential of yours, which Maplith never holds,
brokers or passes on. So you run a small **relay** in your own account: it
checks the signature and starts your agent with your own token or key. That
relay is **Maplith Wake**, open source (Apache 2.0) with no dependencies, in
the `wake-relay` folder of
[github.com/Maplith/agents](https://github.com/Maplith/agents/tree/main/wake-relay). Once
the relay's address is set up as your key's wake endpoint, wake-ups start, on
the conditions above: you do not ask Maplith to switch them on. It starts your agent in either of two ways:

- **A Claude Code routine.** The relay calls the routine's API trigger with the
  routine's own token, which is kept in the relay's settings and nowhere else.
  The text it sends is signal ids, one per line, and nothing else.
- **A Managed Agents session,** with your API key, your agent's environment and
  vault, a cost limit for each session and, if you want one, a daily limit that
  counts only the sessions the relay started.

What the relay does that a plain address would not:

- **It starts nothing twice.** Maplith sends a wake-up again when it hears no
  answer, and a routine's start has no idempotency key, so the relay remembers
  each delivery and each signal episode it has started your agent for.
- **It stays under the routine's limit of 30 starts an hour.** What would pass
  the limit waits and goes out in the next start, which can carry up to 50 ids.
- **It tells you when the routine cannot be started,** such as after a token was
  replaced or a routine deleted, in plain words, at an address you choose.

**The routine's prompt decides what the ids mean.** The template's prompt for
support takes each signal, reads the conversation in your helpdesk, leaves an
internal note or a draft reply, never sends, records what it did, and then
sweeps for any signal it was not given. Give the routine an hourly schedule as
well, so that a missed wake-up costs at most an hour, and connect only Maplith
and your helpdesk to it.

**Start with drafts only.** A routine runs its tools without asking and reads
text written by people outside your company. Use a helpdesk account that
cannot send, set the Maplith key to *Hand over only*, and let a person send
every reply.

For a company, use a Team or Enterprise seat for the routine, or the Managed
Agents way with API credits. Routines also run on Pro and Max plans, which are
plans for individuals.

For those who would rather not run a server, the repository also has a recipe
that does the same in n8n or Make.

**Where things are in the repository:** the README says how to deploy it (one
click on Render, or Docker anywhere) and lists every setting; the routine's
prompt and its setup guide are in `routine/` (`PROMPT.md`, `SETUP.md`); the
n8n and Make recipe is in `recipes/`.

## What Maplith keeps

The list of past wake-ups — when each was sent, its type, the status your
endpoint answered and the attempts — is kept for 30 days and shown in Agents & access › Connections
and to the agent's own key. Every change to a wake-up, by a person or an
agent, is in your workspace's audit log. The full address is shown only to
admins in Agents & access › Connections: an agent is told the scheme and host only, because a
webhook address is often a secret in itself.

Related: [Signals & rules](/docs/signals-and-rules) for what a signal can be,
[Agent keys & policies](/docs/agent-keys) for what a key may see and do, and
[MCP](/docs/mcp) for connecting your agent.