Skip to main content

Conversation Suggestions

POST /ai/suggestions/analyze takes an ordered list of conversation messages and returns the unresolved commitments, requests, deadlines and follow-ups it finds, each with a resolved due time and a recommended action. You can show these to users or turn them into jobs.

The analysis is deterministic (linguistic parsing, temporal extraction and rules); no language model is involved. The endpoint is stateless: obligations are reconciled only within the messages of a single request and nothing is persisted. It requires the execute scope and counts against the account's monthly classify quota (see quotas); it does not consume AI credits.

English only

options.locale defaults to en and accepts only en* values (en, en-US, en_CA). Anything else returns 400 with code UNSUPPORTED_LOCALE. Time zones are independent of locale.

Suggestion types​

TypeExample
COMMITMENT"I'll send it tomorrow."
REQUEST"Can you review this by Friday?"
CHECK_BACK"Let's revisit this next week."
DEADLINE"The report is due Friday."
FOLLOW_UP"I'm still waiting for the invoice."
REMINDER"Remind me to call John tomorrow."
DEPENDENCY"Once John approves it, I'll deploy."

Request​

Field names are snake_case.

{
"conversation_id": "conv_123",
"messages": [
{
"id": "msg_001",
"speaker": { "id": "user_123", "display_name": "Victor", "timezone": "America/Toronto" },
"timestamp": "2026-07-17T10:00:00-04:00",
"message": "I'll send you the proposal tomorrow."
},
{
"id": "msg_002",
"speaker": { "id": "user_456", "display_name": "John", "timezone": "America/Los_Angeles" },
"timestamp": "2026-07-17T10:01:00-04:00",
"message": "Sounds good."
}
],
"participants": [
{ "id": "user_123", "display_name": "Victor", "timezone": "America/Toronto" },
{ "id": "user_456", "display_name": "John", "timezone": "America/Los_Angeles" }
],
"options": {
"locale": "en_CA",
"default_timezone": "America/Toronto",
"minimum_confidence": 0.65
}
}
FieldDescription
messages[]Required. Each has timestamp (ISO 8601), message and optionally id and speaker.
messages[].speakerEither {id, display_name, timezone} or a bare display-name string.
participants[]Optional; inferred from speakers when omitted.
conversation_idOptional; echoed back.
options.localeEnglish only (see above).
options.default_timezoneIANA zone used when a speaker has none.
options.reference_time"Now" for relative phrases; defaults to the server clock.
options.minimum_confidenceSuggestions below this score are suppressed. Default 0.65.
options.include_low_confidenceReturn suppressed low-confidence suggestions too.
options.include_resolved_obligationsInclude COMPLETED/CANCELLED/SUPERSEDED obligations.
options.default_due_time, options.default_deadline_timeHH:MM applied to date-only due times and "by" deadlines. Defaults 09:00 and 17:00.

Minimal form:

curl -X POST "https://api.scheduler0.com/api/v1/ai/suggestions/analyze" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Secret-Key: YOUR_SECRET_KEY" \
-H "X-Account-ID: YOUR_ACCOUNT_ID" \
-d '{
"messages": [
{
"speaker": "Victor",
"timestamp": "2026-07-17T10:00:00-04:00",
"message": "I'\''ll send you the proposal tomorrow."
}
],
"options": { "default_timezone": "America/Toronto" }
}'

Response​

{
"success": true,
"data": {
"request_id": "req_9f1c2ab34d5e",
"conversation_id": "conv_123",
"analyzed_at": "2026-07-17T14:02:01+00:00",
"suggestions": [
{
"id": "sug_001",
"type": "COMMITMENT",
"status": "OPEN",
"actor": { "id": "user_123", "display_name": "Victor" },
"action": { "lemma": "send", "surface": "send" },
"object": { "text": "proposal", "normalized": "proposal", "resolved": true },
"temporal": {
"source_text": "tomorrow",
"due_at": "2026-07-18T09:00:00-04:00",
"timezone": "America/Toronto",
"precision": "DAY",
"is_inferred": false
},
"recommended_action": { "type": "CREATE_REMINDER", "remind_at": "2026-07-18T09:00:00-04:00" },
"confidence": 0.95,
"reason": "Victor committed to sending the proposal tomorrow.",
"evidence": [{ "message_id": "msg_001", "text": "I'll send you the proposal tomorrow.", "start": 0, "end": 36 }],
"rule_ids": ["commitment.first_person_future", "temporal.explicit_date"]
}
],
"obligations": [
{ "id": "obl_001", "status": "OPEN", "suggestion_id": "sug_001", "type": "COMMITMENT", "due_at": "2026-07-18T09:00:00-04:00" }
],
"warnings": [],
"engine": { "engine_version": "1.0.0", "rule_set_version": "2026-07-01" }
}
}

Each suggestion carries a reason, the evidence span, the rule_ids that matched and a confidence. The suggestion and obligation objects are produced by the analysis engine and may gain fields over time; treat unknown keys as additive.

Conversation state​

Later messages update earlier obligations: "Actually, make that Friday" moves the due date instead of creating a duplicate; "I sent the proposal" marks it COMPLETED; "Never mind" marks it CANCELLED. Resolved obligations are hidden unless options.include_resolved_obligations is true.

Per-message parse failures do not fail the request; they appear in warnings (for example {"message_id": "msg_002", "code": "TEMPORAL_PARSE_FAILED"}).

Errors​

StatusCause
400Non-English locale (UNSUPPORTED_LOCALE) or malformed body
429Monthly classify quota exhausted
503Analyzer not configured on the server

To create a job from a suggestion, use recommended_action.remind_at as the job's startDate with no spec (a one-time job). See Jobs. For recommending when to send a message rather than what to schedule, see send-time suggestions.