Skip to main content

Schedules

Looking for the product view?
The Help Center explains this surface as users see it: Prompts on a schedule.

A chat schedule is a Job row of type chat that runs a stored prompt on a recurring cadence. The three verbs below all resolve the chat through chat.lookup, whose read_access scope limits the chat to the requester's own username. They do not check any ai.* permission; the ability to see the chat is the gate.

The schedule lives at /api/chats/{id}/schedule. A chat has at most one schedule, so there are no list or per-id schedule routes: PUT creates it if absent and updates it in place if present.

GET /api/chats/{id}/schedule

Retrieve the chat's schedule Job.

Authentication: Required (session)

Pre-blocks: chat.lookup

Response:

The Job for the chat (type: "chat"). nextRunAt is the computed next fire time and timezone is the stored IANA zone. The recurrence is the { interval, intervalType } pair and the prompt that runs lives under data.prompt.

Get a chat scheduleGET /api/chats/00000000-0000-4000-8000-000000000001/schedule
Returns the chat’s Job (type “chat”). nextRunAt is the computed next fire time; timezone is the stored IANA zone. 404 when the chat has no schedule.
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/chats/00000000-0000-4000-8000-000000000001/schedule"
},
"inf:job-action-order": {
"href": "https://informer.example.com/api/chats/00000000-0000-4000-8000-000000000001/schedule/_job-action-order"
}
},
"naturalId": "admin:daily-order-desk-summary",
"permissions": {
"assignTags": true,
"write": true,
"edit": true,
"run": true,
"delete": true,
"changeOwner": true,
"rename": true
},
"id": "00000000-0000-4000-8000-000000000002",
"type": "chat",
"tenant": "acme",
"slug": "daily-order-desk-summary",
"name": "Daily Order Desk Summary",
"description": null,
"nextRunAt": "2026-01-15T16:20:00.000Z",
"lockedAt": null,
"enabled": true,
"data": {
"alert": {
"enabled": true,
"condition": "anomalies detected"
},
"prompt": "Summarize yesterday’s Order Desk activity and flag anything unusual."
},
"interval": "0 9 1/1",
"startOn": "2026-01-15T16:20:00.000Z",
"intervalType": "Daily",
"endOn": null,
"notes": null,
"source": null,
"sourceId": null,
"timezone": "America/New_York",
"heartbeat": null,
"chatId": "00000000-0000-4000-8000-000000000001",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"ownerId": "admin",
"folderId": null,
"datasetId": null,
"reportId": null,
"queryId": null,
"datasourceId": null,
"editingId": null,
"actions": []
}
Captured from the API examples test suite; ids and timestamps are normalized.

A chat that has no schedule answers 404 (boom.notFound), which is distinct from the 404 returned for a chat the requester cannot see.

Get a schedule that does not existGET /api/chats/00000000-0000-4000-8000-000000000001/schedule
A chat with no schedule yields 404 (boom.notFound), distinct from a 404 for a chat the requester cannot see.
Response · 404
{
"statusCode": 404,
"error": "Not Found",
"message": "Not Found"
}
Captured from the API examples test suite; ids and timestamps are normalized.

PUT /api/chats/{id}/schedule

Create the schedule when the chat has none, or update the existing Job in place when it already has one.

Authentication: Required (session)

Pre-blocks: chat.lookup

Request Body:

FieldTypeRequiredDescription
namestringYesJob name.
intervalstringNoCron-ish recurrence expression (e.g. 0 9 1/1). Paired with intervalType.
intervalTypestringNoHuman-readable cadence label that accompanies interval (e.g. Daily, Weekly).
startOndateNoWhen the schedule begins firing.
endOndateNoWhen the schedule stops firing. null for indefinite.
enabledbooleanNoWhether the schedule is active.
prioritynumberNoJob priority.
timezonestringNoIANA zone the recurrence is evaluated in (e.g. America/New_York). Must be a valid IANA zone or the route answers 400. Omit (or send null) to inherit the requester's zone; on update, omit to keep the stored zone.
dataobjectNoDriver payload. Holds prompt (the text the schedule runs) and alert.
data.promptstringYes*The prompt the schedule runs. The chat-job driver asserts this in beforeValidate, so a usable schedule must carry it.
data.alertobjectNoAlert configuration: { enabled, condition }.

There is no top-level alert field and no single interval: "daily" string; the recurrence is the { interval, intervalType } pair, and alerts live under data.alert. Anything outside the schema is stripped (stripUnknown).

Example Request:

{
"name": "Daily Order Desk Summary",
"interval": "0 9 1/1",
"intervalType": "Daily",
"startOn": "2026-01-15T09:00:00.000Z",
"enabled": true,
"timezone": "America/New_York",
"data": {
"prompt": "Summarize yesterday's Order Desk activity and flag anything unusual.",
"alert": { "enabled": true, "condition": "anomalies detected" }
}
}

Response:

Responds 200 with the created or updated Job (there is no 201 and no Location header).

Create a chat schedulePUT /api/chats/00000000-0000-4000-8000-000000000001/schedule
PUT creates the schedule when the chat has none. `name` is required; the recurrence is the { interval, intervalType } pair (cron-ish), and the prompt to run lives under data.prompt (the chat-job driver requires it). Omit `timezone` (or send null) to inherit the requester’s zone. Responds 200 with the created Job.
Request body
{
"name": "Daily Order Desk Summary",
"interval": "0 9 1/1",
"intervalType": "Daily",
"startOn": "2026-01-15T16:20:00.000Z",
"enabled": true,
"timezone": "America/New_York",
"data": {
"prompt": "Summarize yesterday’s Order Desk activity and flag anything unusual.",
"alert": {
"enabled": true,
"condition": "anomalies detected"
}
}
}
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/chats/00000000-0000-4000-8000-000000000001/schedule"
},
"inf:job-action-order": {
"href": "https://informer.example.com/api/chats/00000000-0000-4000-8000-000000000001/schedule/_job-action-order"
}
},
"naturalId": "admin:daily-order-desk-summary",
"permissions": {
"assignTags": true,
"write": true,
"edit": true,
"run": true,
"delete": true,
"changeOwner": true,
"rename": true
},
"id": "00000000-0000-4000-8000-000000000002",
"tenant": "acme",
"name": "Daily Order Desk Summary",
"interval": "0 9 1/1",
"intervalType": "Daily",
"startOn": "2026-01-15T16:20:00.000Z",
"enabled": true,
"timezone": "America/New_York",
"data": {
"alert": {
"enabled": true,
"condition": "anomalies detected"
},
"prompt": "Summarize yesterday’s Order Desk activity and flag anything unusual."
},
"type": "chat",
"chatId": "00000000-0000-4000-8000-000000000001",
"updatedAt": "2026-01-15T16:20:00.000Z",
"createdAt": "2026-01-15T16:20:00.000Z",
"ownerId": "admin",
"nextRunAt": "2026-01-15T16:20:00.000Z",
"slug": "daily-order-desk-summary",
"description": null,
"lockedAt": null,
"endOn": null,
"notes": null,
"source": null,
"sourceId": null,
"_altid": null,
"datasetId": null,
"reportId": null,
"queryId": null,
"datasourceId": null,
"editingId": null,
"heartbeat": null,
"folderId": null
}
Captured from the API examples test suite; ids and timestamps are normalized.

A second PUT updates the same schedule row in place. Omit timezone to keep the stored zone.

Update a chat schedulePUT /api/chats/00000000-0000-4000-8000-000000000001/schedule
A second PUT updates the existing Job in place (same chat, same schedule row). Omit `timezone` to keep the stored zone. Here we change the recurrence to weekly and disable alerts.
Request body
{
"name": "Weekly Order Desk Summary",
"interval": "0 9 * * MON",
"intervalType": "Weekly",
"enabled": true,
"data": {
"prompt": "Summarize last week’s Order Desk activity.",
"alert": {
"enabled": false
}
}
}
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/chats/00000000-0000-4000-8000-000000000001/schedule"
},
"inf:job-action-order": {
"href": "https://informer.example.com/api/chats/00000000-0000-4000-8000-000000000001/schedule/_job-action-order"
}
},
"naturalId": "admin:weekly-order-desk-summary",
"permissions": {
"assignTags": true,
"write": true,
"edit": true,
"run": true,
"delete": true,
"changeOwner": true,
"rename": true
},
"id": "00000000-0000-4000-8000-000000000002",
"type": "chat",
"tenant": "acme",
"slug": "weekly-order-desk-summary",
"name": "Weekly Order Desk Summary",
"description": null,
"nextRunAt": "2026-01-15T16:20:00.000Z",
"lockedAt": null,
"enabled": true,
"data": {
"prompt": "Summarize last week’s Order Desk activity.",
"alert": {
"enabled": false
}
},
"interval": "0 9 * * MON",
"startOn": "2026-01-15T16:20:00.000Z",
"intervalType": "Weekly",
"endOn": null,
"notes": null,
"source": null,
"sourceId": null,
"timezone": "America/New_York",
"heartbeat": null,
"chatId": "00000000-0000-4000-8000-000000000001",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"ownerId": "admin",
"folderId": null,
"datasetId": null,
"reportId": null,
"queryId": null,
"datasourceId": null,
"editingId": null,
"actions": []
}
Captured from the API examples test suite; ids and timestamps are normalized.
Driver requires a prompt

The chat-job driver asserts data.prompt during validation, so a schedule without a prompt is rejected even though data is optional at the route level.


DELETE /api/chats/{id}/schedule

Remove the chat's schedule Job.

Authentication: Required (session)

Pre-blocks: chat.lookup

Response:

Responds 200 with an empty body (Informer convention), not 204. The verb is idempotent: deleting when no schedule exists still answers 200.

Delete a chat scheduleDELETE /api/chats/00000000-0000-4000-8000-000000000001/schedule
Removes the chat’s Job. Responds 200 with an EMPTY body (Informer convention), NOT 204 as older docs claimed. Idempotent: deleting when none exists still answers 200.
Response · 200 · no body
Captured from the API examples test suite; ids and timestamps are normalized.
Use cases

Scheduled chats are useful for daily report generation, periodic data analysis, automated monitoring alerts, and recurring summarization tasks.