---
title: defineEvent
description: "Define one workflow event: an event that an Agent or the caller of a Gate may send into your Machine."
---

# `defineEvent`

Function, in `src/define-event.ts` of `@jr2/agent-protocol`, a dependency of `@jr2/orchestrator`. It is an export of `@jr2/orchestrator`, on the [Authoring surface](../authoring-surface.md) page, in Machine and events.

```ts
function defineEvent<const Name extends string, Input extends z.ZodObject>(def: {
  name: Name;
  description?: string;
  input: Input;
  semantics?: EventSemantics;
  audience?: EventAudience;
}): EventDef<Name, Input>
```

Define one workflow event: an event that an Agent or the caller of a Gate may send into your
Machine. Give the result to `jr2Setup({ events })`, and write its name as an `on` key on the
states that take it. An Agent sees each event it may send as one tool on its Menu.

It gives back a frozen [`EventDef`](type-eventdef.md) and registers nothing. Each Machine whose `jr2Setup`
lists the event takes it. A Machine that does not list it does not take it.

Give an event only the params that the Machine reads. When a pick enters a state that invokes an
Effect, `createMachine` refuses each param of the pick that nothing reads: the input of the
Effect, an `assign`, or the `fill` of `fromPick`. The compiler does not find this error.
`jr2 check` and `jr2 up` find it when they load the module. Thus when you copy a `finish` event with a `summary`
and nothing uses the summary, drop `summary` from the `input`, or store it with `assign`.

It refuses:
- a name with a character other than a letter, a digit, `_` or `-`:
  `defineEvent: invalid name "<name>" — must match /^[a-zA-Z0-9_-]+$/ (it becomes an MCP tool
  name and an xstate event type)`
- an `input` with a `sender` field:
  ``defineEvent: "<name>" declares `sender` in its input — the Orchestrator writes that field
  on every delivered event (who sent it); name the payload field something else``
- an `output` field:
  ``defineEvent: "<name>" declares an `output`, which is retired — every pick is answered when
  the work it started is done, and the answer is the Machine's, not the def's``. With
  `semantics: "deferred"`, the text says ``declares `deferred` `` in place of
  ``declares an `output` ``.

**Parameters**

- `def`: The event: its `name`, its `input` schema, and optionally its `description`,
  `semantics` and `audience`.

  - `name`: `Name`

    The event's name: the `on` key that takes it, and the name of its tool on the Agent's Menu.
    Letters, digits, `_` and `-` only.
  - `description` (optional): `string`

    What the event means, and when to send it. An Agent's model reads it as the tool's
    description. Make it true at every state that takes the event.
  - `input`: `Input`

    The schema of the payload: a flat `z.object`. Use `z.object({})` for an event with no
    payload. Do not name a field `sender`: jr2 writes that field.
  - `semantics` (optional): [`EventSemantics`](type-eventsemantics.md)

    How the sender gets its answer. Default: `"ack"`, the only value `createMachine` accepts.
  - `audience` (optional): [`EventAudience`](type-eventaudience.md)

    Who may send the event: `"agent"`, `"external"` or `"any"`. Default: unset, and the state of
    each `on` key decides (see [`EventAudience`](type-eventaudience.md)).

**Returns**: The event, as plain data to list in `jr2Setup({ events })`.

**Example**

```ts
import { z } from "zod";
import { agent, defineEvent, fail, jr2Setup } from "@jr2/orchestrator";

const done = defineEvent({
  name: "done",
  description: "Call this when the summary is written.",
  input: z.object({ summary: z.string() }),
});

export const machine = jr2Setup({
  events: [done],
  actors: {
    writer: agent({
      model: "anthropic/claude-haiku-5-5",
      instructions: "You write short summaries.",
      workspace: "none",
    }),
    fail: fail(),
  },
}).createMachine({
  id: "summary",
  on: { "agent.fault": ".failed" },
  initial: "writing",
  states: {
    writing: {
      invoke: { src: "writer", input: { prompt: "Summarize the release notes in one line." } },
      on: { done: "finished" },
    },
    finished: { type: "final" },
    failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
  },
});
```

**See also**

- [Write a Workflow](../../guides/write-a-workflow.md)
- [The Menu](../../concepts/the-menu.md)
- [ADR-0011: Control events are workflow-defined and delivered by closure, not routed](../decisions/0011-workflow-defined-events.md)
