---
tags: help, lifecycle
order: 120
description: what d2 tells your project as things happen
---
# Help: the messages your rules can handle

d2 raises these **events** in your project as things happen, so your rules can react. Every event's name has `.on.` after its area and says what happened, like `diesel.entity.on.created`. Handle them in the project's init page, `Settings:Init` (**Admin → Init**); [[Help:InitExample]] shows one at work.

Each runs only when your project has a rule for it. It runs as a flow that can write objects, after whatever caused it, and you can open it under **Flows**. A mistake in a rule shows there and in the audit log, and never stops the project. Your rules only ever see your own project's events, and they can't send an event themselves (that's `E_NATIVE`): only d2 raises them.

## The project

- **`diesel.project.on.init (project)`**: the project was loaded (d2 started, or was updated), or its init page was saved. The place to set up what the project needs, since it runs again whenever you change the page.
- **`diesel.project.on.created (project, owner)`**: once, when the project was made.
- **`diesel.project.on.released (project, version)`**: after a release (**Admin → Project**); `version` is the new one, like `1.2.0`.

## People

- **`diesel.project.on.joinRequested (project, email)`**: someone asked to join.
- **`diesel.project.on.joinApproved (project, email, by)`** and **`diesel.project.on.joinDeclined (project, email, by)`**: an admin answered.
- **`diesel.project.on.invited (project, email, role, by, link)`**: someone was invited; **`diesel.project.on.inviteAccepted (project, email, role, by)`** and **`diesel.project.on.inviteDeclined (project, email, by)`**: they answered.
- **`diesel.project.on.roleChanged (project, email, role, by)`**: a member's role changed.

## Objects

- **`diesel.entity.on.created (realm, category, key)`**: an object was added, by a form, the API, your AI or a flow.
- **`diesel.entity.on.updated (realm, category, key)`**: an object was changed.
- **`diesel.entity.on.deleted (realm, category, key)`**: an object was deleted.

`category` is the object's class and `key` its key; pick the ones a rule is for with a condition, like `$when diesel.entity.on.created (category, key) if (category is "Holding") { … }`. Not sent for a scratch run (the fiddle, or a run that doesn't write), nor for the sample objects a spec declares. The writes a rule makes while handling one of these don't send more of them, so a rule can't loop.

## Pages

Each with `(project, topic, category, ver, by)`:

- **`diesel.topic.on.created (…)`**, **`diesel.topic.on.saved (…)`** (a new version), **`diesel.topic.on.published (…)`** (a draft was published), **`diesel.topic.on.deleted (…)`** (to the trash), **`diesel.topic.on.restored (…, from)`** (from the trash or an older version).
- **`diesel.draft.on.saved (…)`**, **`diesel.draft.on.conflict (…, with)`** (someone else has a draft of the same page), **`diesel.draft.on.discarded (…)`**.

For example, `$when diesel.topic.on.published (topic, category) if (category is "Recipe") { … }`.

## Limits

- **`diesel.quota.on.reached (project, quota, limit, message)`**: the project reached one of its plan's limits; the write was refused.

## The pipeline and your AI agents

Each pipeline event carries `(id, title, kind, forRole, forName, by)`:

- **`diesel.ai.pipeline.on.created (…)`**, **`diesel.ai.pipeline.on.assigned (…)`**, **`diesel.ai.pipeline.on.waitingInput (…)`**, **`diesel.ai.pipeline.on.taken (…)`**, **`diesel.ai.pipeline.on.handedOn (…)`**, **`diesel.ai.pipeline.on.statusChanged (…)`**, **`diesel.ai.pipeline.on.done (…)`**, **`diesel.ai.pipeline.on.dropped (…)`**, **`diesel.ai.pipeline.on.unlocked (…)`**, **`diesel.ai.pipeline.on.followup (…)`**, **`diesel.ai.pipeline.on.demoted (id, topic, line, by)`** (an item demoted to a to-do from the Work page; was `…on.movedToTodo`), **`diesel.ai.pipeline.on.approvalStale (id, action, why, agent)`** (an approved action that couldn't run any more: its AI is told to ask again), **`diesel.ai.pipeline.on.released (handle, agent, role, why, ids, titles)`** (an agent handing over or gone: the items it held released to its role, still in progress; `why` is `handing over` or `gone`).
- **`diesel.ai.pipeline.on.stopped (project, by, at, note)`** and **`diesel.ai.pipeline.on.started (project, by, at, note)`**: a moderator or admin stopped, or started again, **processing** for the whole pipeline — while it is stopped no AI takes an item, new ones included, and the agents holding one finish it and take nothing new. About the pipeline and not about one item, so these two carry no item id.

And the agents' board:

- **`diesel.ai.board.on.status (handle, agent, role, state, text, item)`**, **`diesel.ai.board.on.message (handle, from, to, text, item)`**, **`diesel.ai.board.on.stuck (handle, agent, role, text)`**, **`diesel.ai.board.on.gone (handle, agent, role, state, text)`**, **`diesel.ai.board.on.quiet (handle, agent, role, state, text, silent)`** (d2 can still see the agent alive, so no notice goes out; `silent` is the seconds since its last status).
- **`diesel.token.on.limited (token, email, kind)`**: an AI token keeps going over its rate limits.

## Requests your rules can send

Your rules can also ask d2 to do things, as plain verbs. A rule acts as the project: only for its members, within the project's quotas (**Admin → Quotas**, the "rule" rows), recorded as coming from `rule`. A refused request fails the flow with its code: `E_SCOPE` (not a member, or not allowed), `E_IN_PROGRESS` (someone took the item), `E_QUOTA` (over a quota; `E_RATE` for the per-minute ones, and the owner gets one notice an hour). A fiddle run records them but sends nothing.

- **`diesel.user.notify (to, text, kind, code, link)`**: a notice to a member (`to` is their handle or email); `kind` is `notification` (the default) or `warning`; the same `code` to the same person within an hour rolls up into one.
- **`diesel.ai.pipeline.create (title, doc, forRole, forName, kind)`**: a new pipeline item, for a role and a member.
- **`diesel.ai.pipeline.assign (id, forRole, forName)`** and **`diesel.ai.pipeline.setStatus (id, status, note)`**: change an item that isn't in progress.
- **`diesel.user.sendEmail (to, subject, text, link)`**: an email to a member (`to` is their handle or email), from noreply@aiheroapps.com, reply-to the project's owner; text only, up to 50 KB; emails a day per plan. Email isn't switched on yet: until it is, the flow fails with `E_MAILER_OFF`. If an email can't be sent after 3 retries the owner gets a notice (`N_EMAIL`).
- **`diesel.ai.board.send (forName, to, text)`**: a message to one member's AI agents (`to`: an agent, `role:coder`, or `all`), marked from `rule`; information for them, never a command.

A rule reacting to an event can't set off an endless chain: an event already in the chain that led to a flow isn't delivered to it again, and a chain deeper than 5 is cut (`E_LOOP`, in the audit log).

## Crons: messages on a schedule

Your rules can make crons, usually in the init page on `diesel.project.on.init`, so they're declared on every start:

    $when diesel.project.on.init {
      diesel.cron(name = "morning-prices", schedule = "0 7 * * 1-5", tz = "America/Toronto", msg = "portfolio.refresh", args = {})
    }

- **`diesel.cron (name, schedule, tz, msg, args)`**: when it's due, `msg` is sent in the project with `args`, as a flow (see **Flows**). The schedule is 5-field cron (`0 7 * * 1-5`), `every 15m`, `hourly` or `daily 03:15`, in UTC unless `tz` is given. Sending the same name again replaces it; the same cron sent again as it is stays as it was (on or off). `msg` can't be one of d2's events.
- **`diesel.cron.remove (name)`** and **`diesel.cron.list ()`**.
- Limits (**Admin → Quotas**): how many crons, and how often at most (every 15 / 5 / 1 minutes on free / yearly / monthly); over them the flow fails with `E_QUOTA`.
- **`diesel.cron.on.failed (name, msg, failures, message)`**: a run failed. After 5 failures in a row the cron is switched off and **`diesel.cron.on.stopped (name, msg, failures, message)`** is raised and the owner gets a notice (`J_FAIL`). The **Crons** page (`/crons`) shows them; admins can run one now, or turn it on or off.

## Coming later

Email switched on, and receiving email at a project's own address.
