Triggers
Triggers automatically run an agent when something happens — no schedule required. Where a Job runs an agent on a fixed clock, a Trigger runs an agent in reaction to an event, letting your workspace respond the moment something changes.
ToothFairyAI supports two event sources:
- Native — ToothFairyAI's own platform events (a document is created, an agent completes, a planner is awaiting approval, etc.).
- External — Trigger events emitted by third-party apps (Google Drive, Slack, GitHub, Salesforce, Stripe, Notion, Jira, and many more). External triggers are acquired by polling the provider with a configured Authorisation.
When a trigger fires, it sends the bound prompt (or forced prompt) to the selected agent exactly as a manual chat run would.
Menu location
Triggers can be created and managed from the following menu:
Settings > Triggers > Create trigger
Create a trigger
Basic Information
- Click on the
Create triggerbutton. - Name — Assign a unique name to identify the trigger.
- Description (optional) — Describe what this trigger does and when it should fire.
Execution Configuration
- Agent — Select the agent that will run when the trigger fires. Voice-mode agents are excluded from the dropdown (triggers target text-capable agents).
- Prompt — Choose the prompt sent to the agent. Available prompts are your saved prompts (see Prompting). The dropdown only lists prompts made available to the selected agent. You can provide a Forced Prompt instead — free-text instructions (max 1024 characters) used directly as the agent's input. Either a prompt or a forced prompt is required.
If the selected agent has no prompts assigned to it — and you have not provided a forced prompt — the form shows a placeholder linking you straight to the Generation settings section to create one.
Event Configuration
- Event Source — Choose where the triggering event comes from:
Native (ToothFairyAI events)
React to events produced inside your ToothFairyAI workspace.
-
Native Event Type — The platform event to listen for (e.g.
document.created,agent_completed,planner_pending_approval). See the Native events table below. -
Data Filter (optional) — Narrow the trigger to events whose payload matches conditions you define. Each row is a
field / operator / valuetriple:Operator Meaning equalsField equals the value not equalsField does not equal the value includesField contains the value greater thanField is numerically greater than the value less thanField is numerically less than the value existsField is present in the payload Add as many rows as you need; an event must satisfy all rows to fire. Empty
fieldrows are ignored on save.
External (3rd-party app)
React to events emitted by an external provider.
- Provider — The third-party app to listen to (e.g. Google Drive, Slack, GitHub). Providers that are registered but not yet fully implemented are shown with a
(soon)badge and cannot be saved until their adapter lands. - Provider Event — The specific event from the provider (e.g.
drive.file.created,slack.message.mentioned,github.pr.merged). - Authorisation — The credential the trigger will use to poll the provider. The dropdown lists every Authorisation you have configured in the workspace; the selected authorisation's type is stored as the trigger's authorisation type on save.
- Acquisition Mode — How ToothFairyAI acquires the event:
- Polling (default) — Periodically polls the provider's API for new items.
- Webhook (soon) — Receive push events from the provider. Not yet available.
- Poll Interval (Polling only) — How often to poll. Options: 1, 2, 5, 10, or 30 minutes (default 5 minutes).
- Poll Endpoint (Generic REST provider only) — The full URL to poll, e.g.
https://api.example.com/v1/items. - JSONPath (Generic REST provider only) — The JSONPath expression used to extract the list of new items from each poll response, e.g.
$.items[*].
External triggers need a provider Authorisation to authenticate the poll. If no Authorisations exist when an external provider is selected, the form shows a hint — create an Authorisation first, then return to the trigger.
Status
-
Active — Toggle to arm or pause the trigger. When active, the trigger is saved with status ARMED and will fire on matching events. When inactive it is saved as PAUSED and will not fire until re-enabled.
-
Click on the
Createbutton to save the trigger.
Native events
These are ToothFairyAI's own platform events, organized by category:
| Event ID | Category | Label | Description |
|---|---|---|---|
document.created | documents | Document Created | A new document has been created in the workspace |
document.updated | documents | Document Updated | An existing document has been modified |
document.deleted | documents | Document Deleted | A document has been deleted from the workspace |
agent_completed | agent | Agent Completed | An agent has successfully completed processing a chat message |
agent_failed | agent | Agent Failed | An agent encountered an error while processing a chat message |
planner_completed | planner | Planner Completed | A planner has successfully completed all plan steps |
planner_failed | planner | Planner Failed | A planner encountered an error during execution |
planner_pending_approval | planner | Planner Pending Approval | A generated plan is awaiting user approval |
planner_stopped | planner | Planner Stopped | Plan execution was stopped by the user |
Native events are the same events ToothFairyAI emits over its webhooks. Pairing a trigger with a native event is the in-app way to react to those same lifecycle events.
Event payload shape
Every event — native or external — reaches the bound agent in a normalized payload envelope. Its fields are listed below:
| Field | Type | Description |
|---|---|---|
event_type | string | Normalized event id, e.g. "external.googleDrive.drive.file.created" or "document.updated". External events are always prefixed external.<provider>.<providerEvent>. |
event_category | `string | null` |
source | string | Where the event came from: "api" |
nativeEventId | string | Provider-canonical event name (e.g. googleDrive fileCreated, stripe payment_intent.succeeded). |
event_id | string | Unique event id — used to avoid processing the same event twice per trigger. |
timestamp | string (ISO-8601) | When the event occurred. |
data | object | Provider- or platform-specific payload (fields are detailed per event/provider in the reference below). This is the ONLY part the trigger data-filter rows evaluate against. |
Sample — native (document.created):
{
"event_type": "document.created",
"event_category": "documents",
"source": "api",
"nativeEventId": "document.created",
"event_id": "evt_document.created",
"timestamp": "2026-07-19T03:00:00.000Z",
"data": {
"document_id": 12345,
"external_path": null,
"topics": [],
"status": "draft"
}
}
Sample — external (googleDrive.drive.file.created):
{
"event_type": "external.googleDrive.drive.file.created",
"event_category": null,
"source": "poller",
"nativeEventId": "drive.fileCreated",
"event_id": "evt_googleDrive_drive.file.created",
"timestamp": "2026-07-19T03:00:00.000Z",
"data": {
"fileId": "1a2b3c",
"name": "Proposal.docx",
"mimeType": "application/vnd.google-apps.document",
"modifiedTime": "2026-07-19T03:00:00.000Z",
"eventType": "drive.fileCreated"
}
}
Documents
ToothFairyAI's lifecycle events. Each event below lists the payload the bound agent receives and example data-filter rows.
document.created
Fires when: A new document is created in the workspace — e.g. a file upload, a raw-text document, or a document generated by the API.
Sample payload the agent receives:
{
"event_type": "document.created",
"event_category": "documents",
"source": "api",
"nativeEventId": "document.created",
"event_id": "evt_document.created",
"timestamp": "2026-07-19T03:00:00.000Z",
"data": {
"document_id": 12345,
"external_path": null,
"topics": [],
"status": "draft"
}
}
Fields you can filter on (data):
| Field | Type | Meaning |
|---|---|---|
document_id | number | Internal id of the document |
external_path | `string | null` |
topics | string[] | Topic IDs (UUIDs) the document is assigned to — matching a topic means the trigger fires for documents on that topic |
status | string | Lifecycle status ("draft" |
Filter examples:
| Field | Operator | Value | Fires when |
|---|---|---|---|
status | equals | published | Only documents that are already published |
topics | includes | 5f9c2e4a-7c8d-4b3a-9e2f-1a2b3c4d5e6f | Only documents assigned to a specific topic (match its ID) |
To fire only when a document is assigned to a specific topic:
- Copy the topic's ID from
Settings > Topics(topics are identified by their UUID, e.g.5f9c2e4a-7c8d-4b3a-9e2f-1a2b3c4d5e6f). - Add a data-filter row — Field
topics, Operatorincludes, Value that topic ID. - The event's
data.topicsis an array of topic IDs the document carries, soincludesmatches when any ID in that array equals your value. - Add more rows to narrow further — every row must match. For example a second row
statusequalspublishedrestricts the trigger to published documents on that topic.
document.updated
Fires when: An existing document is modified — content changed, re-indexed or re-published.
Sample payload the agent receives:
{
"event_type": "document.updated",
"event_category": "documents",
"source": "api",
"nativeEventId": "document.updated",
"event_id": "evt_document.updated",
"timestamp": "2026-07-19T03:00:00.000Z",
"data": {
"document_id": 12345,
"external_path": null,
"topics": [],
"status": "published"
}
}
Fields you can filter on (data):
| Field | Type | Meaning |
|---|---|---|
document_id | number | Internal id of the document |
external_path | `string | null` |
topics | string[] | Topic IDs (UUIDs) the document is assigned to — matching a topic means the trigger fires for documents on that topic |
status | string | Lifecycle status ("draft" |
Filter examples:
| Field | Operator | Value | Fires when |
|---|---|---|---|
status | equals | published | Only edits to published documents |
topics | includes | 5f9c2e4a-7c8d-4b3a-9e2f-1a2b3c4d5e6f | Only edits to documents on a specific topic |
document_id | equals | 12345 | Watch one specific document |
document.deleted
Fires when: A document is deleted (archived) from the workspace.
Sample payload the agent receives:
{
"event_type": "document.deleted",
"event_category": "documents",
"source": "api",
"nativeEventId": "document.deleted",
"event_id": "evt_document.deleted",
"timestamp": "2026-07-19T03:00:00.000Z",
"data": {
"document_id": 12345,
"external_path": null,
"topics": [],
"status": "archived"
}
}
Fields you can filter on (data):
| Field | Type | Meaning |
|---|---|---|
document_id | number | Internal id of the document |
external_path | `string | null` |
topics | string[] | Topic IDs (UUIDs) the document is assigned to — matching a topic means the trigger fires for documents on that topic |
status | string | Lifecycle status ("archived") |
Filter examples:
| Field | Operator | Value | Fires when |
|---|---|---|---|
external_path | exists | — | Only documents that were deleted from an external source |
Agents
ToothFairyAI's execution outcomes. Each event below lists the payload the bound agent receives and example data-filter rows.
agent_completed
Fires when: An agent successfully finishes processing a chat message. Pair it with a downstream notification, logging or hand-off agent.
Sample payload the agent receives:
{
"event_type": "agent_completed",
"event_category": "agent",
"source": "system",
"nativeEventId": "agent_completed",
"event_id": "evt_agent_completed",
"timestamp": "2026-07-19T03:00:00.000Z",
"data": {
"status": "success",
"agent_id": null,
"is_from_planner": false,
"step_id": null
}
}
Fields you can filter on (data):
| Field | Type | Meaning |
|---|---|---|
status | string | "success" for this event |
agent_id | `string | null` |
is_from_planner | boolean | True when the run was a step of a planner plan |
step_id | `string | null` |
Filter examples:
| Field | Operator | Value | Fires when |
|---|---|---|---|
is_from_planner | equals | false | Only whole agent runs, not planner sub-steps |
agent_id | exists | — | Only runs attributable to an agent |
agent_failed
Fires when: An agent encounters an error while processing a chat message — useful to alert, log or re-route the work.
Sample payload the agent receives:
{
"event_type": "agent_failed",
"event_category": "agent",
"source": "system",
"nativeEventId": "agent_failed",
"event_id": "evt_agent_failed",
"timestamp": "2026-07-19T03:00:00.000Z",
"data": {
"status": "error",
"agent_id": null,
"is_from_planner": false,
"error": null
}
}
Fields you can filter on (data):
| Field | Type | Meaning |
|---|---|---|
status | string | "error" for this event |
agent_id | `string | null` |
is_from_planner | boolean | True when the run was a step of a planner plan |
error | `object | null` |
Filter examples:
| Field | Operator | Value | Fires when |
|---|---|---|---|
error | exists | — | Only failures that carry error details |
Planners
ToothFairyAI's plan lifecycles. Each event below lists the payload the bound agent receives and example data-filter rows.
planner_completed
Fires when: A planner successfully completes every step of a generated plan.
Sample payload the agent receives:
{
"event_type": "planner_completed",
"event_category": "planner",
"source": "system",
"nativeEventId": "planner_completed",
"event_id": "evt_planner_completed",
"timestamp": "2026-07-19T03:00:00.000Z",
"data": {
"status": "success",
"planner_agent_id": null,
"plan_status": "completed"
}
}
Fields you can filter on (data):
| Field | Type | Meaning |
|---|---|---|
status | string | "success" for this event |
planner_agent_id | `string | null` |
plan_status | string | "completed" for this event |
Filter examples:
| Field | Operator | Value | Fires when |
|---|---|---|---|
plan_status | equals | completed | Only fully completed plans |