---
title: "Wake"
description: "Schedule and send typed \"wake\" commands to your own Primitive Functions over DKIM-signed email. Cron-driven or one-shot, gated by a default-deny allowlist of which senders may wake which function."
canonical: "https://docs.primitive.dev/docs/wake"
last-updated: "2026-05-17T00:00:00.000Z"
---

# Wake

Wake lets you drive your own [Primitive Functions](/docs/functions) on a schedule, or on demand, by sending them a typed command over signed email. A wake is just a structured email your function receives like any other inbound message, except it carries a command (for example `process_inbox`) and an optional JSON `args` object, and it is authenticated end to end with DKIM.

Two things make wake more than "email yourself on a cron":

- Typed delivery. A wake is delivered to your function as a typed `interaction.wake.dispatched` event, not a raw message you have to parse. Your handler branches on the command and reads `args`.

- Default-deny authorization. A function is woken only by senders you have explicitly allowed. With no grants, nothing can wake it, not even your own other addresses.

Wake is built on the Primitive Interactions standard (`wake.dispatch/1`): every wake carries a DKIM-signed `X-Primitive-Interaction` classifier header, and inbound wakes run through the same trust engine as any other interaction.

**Availability:** Wake is enabled per organization. Until it is turned on for your org, the Wake API and dashboard surfaces return a `403`.

## The model

A wake moves through a small, typed lifecycle:

- dispatch sends a wake command to the function's address (on a schedule, or one-shot).

- awaiting_result means the function has received the command and is expected to act.

- result / reject / expire is the terminal state: the function reports a result, rejects the command, or the wake expires unanswered.

Each transition emits an event you can subscribe to:

EventWhen`interaction.wake.dispatched`A wake was authorized and delivered to the function`interaction.wake.completed`The function reported a result`interaction.wake.rejected`The function rejected the wake (a wake denied by the trust gate is dropped silently and emits no event)`interaction.wake.expired`The wake was not answered before it expired

The `interaction.wake.dispatched` event is the load-bearing one: it is emitted only after authorization passes, and it is what your function endpoint actually receives.

## Authorize who can wake a function

Wake is default-deny. Before a sender can wake a function, you grant it explicitly. A grant is scoped by:

- Sender domain (required): the domain the wake must come from, for example `agents.acme.dev`.

- Sender address (optional): narrow the grant to a single address, for example `scheduler@agents.acme.dev`. Omit to allow any address on the domain.

- Allowed commands (optional): restrict the grant to specific commands. Omit to allow any command. A command is matched exactly, so list each one you intend to allow.

Manage grants from the **Wake** tab on a function's page in the dashboard (which resolves the function's receiving endpoint for you), or with the Wake API: `POST /v1/wake/authorizations` (passing the receiving endpoint id, the sender domain, and optionally a single address and a list of allowed commands) and `GET /v1/wake/authorizations`.

To wake your own function from your own scheduler, grant the address your schedules send **from** (see below). A wake whose sender is not covered by an enabled grant is denied before delivery.

## Schedule a wake

A schedule sends a wake command to a target address on a cron expression, in a timezone you choose.

FieldMeaning`from_address`The address the wake is sent from (must be authorized to wake the target).`target_address`The function's inbound address that receives the wake.`command`The typed command, for example `process_inbox`.`args`Optional JSON object passed to the handler (capped at 16 KB).`cron_expr`Standard cron expression for when to fire.`timezone`IANA timezone the cron is evaluated in, for example `America/New_York`.

`from_address` and `target_address` must differ; a schedule cannot wake the address it sends from.

Create and manage schedules from **Settings → Wake** in the dashboard, or with the Wake API: `POST /v1/wake/schedules` (with the fields above), `GET /v1/wake/schedules`, and `POST /v1/wake/schedules/:id/run` to fire one once, off-schedule.

## Inspect dispatches

Every wake that fires is recorded. List recent dispatches and their outcomes from the dashboard's **Wake** surfaces — which show each schedule's next run and each dispatch's status (dispatched, completed, rejected, expired) — or with `GET /v1/wake/dispatches`.

## Handle a wake in your function

A woken function receives an `interaction.wake.dispatched` event. The event name arrives in the `X-Webhook-Event` header; branch on it and run the work the wake represents:


```
export default {
  async fetch(request) {
    if (request.headers.get("X-Webhook-Event") === "interaction.wake.dispatched") {
      // Run the scheduled work. The command and args you set on the schedule
      // (or one-shot dispatch) determine what this wake should do.
      await processInbox();
    }
    return new Response("ok");
  },
};
```


The wake is signed and trust-checked before it reaches you: if the event arrived, the sender was authorized for that function and command.

## API

Everything above is available over the [API](/docs/api) under `/v1/wake`:

- `GET/POST /v1/wake/schedules`, `GET/PATCH/DELETE /v1/wake/schedules/:id`, `POST /v1/wake/schedules/:id/run`

- `GET/POST /v1/wake/authorizations`, `PATCH/DELETE /v1/wake/authorizations/:id`

- `GET /v1/wake/dispatches`

Maintained by the [primitive.dev team](/about) · [Trust & security](/trust)Last updated: May 17, 2026
