Reference · Planning API

Planning API

Goals, tasks and Inbox messages. A goal or task is yours when assigneeWorkerId is empty, or a worker’s when it’s set (its shifts consume it and account for it). Agents file tasks through the same API; createdVia records how. The model: The planner, Goals and pace, Assigned work.

Goals

GET/v1/goals?workspaceId=&status=&assigneeWorkerId=List goals; filter by status, horizon or assignee.
POST/v1/goalsCreate. A measured goal needs startsOn and endsOn; a worker’s goal needs a target.
GET/v1/goals/{goalId}One goal, with pace when it has a measure.
PATCH/v1/goals/{goalId}Update fields, the measure or status. Achieving by hand stamps achievedBy: "user".
DELETE/v1/goals/{goalId}Delete (linked tasks survive, unlinked).
GET/v1/goals/{goalId}/readingsOne reading a day, oldest first: the chart.
POST/v1/goals/{goalId}/readingsCheck in a value, with a note (manual measures).
POST/v1/goals/{goalId}:readRead the source now instead of waiting for the hourly sweep.
FieldTypeRequiredDescription
metric.source"newsletter.subscribers" | "stripe.sales" | "manual"requiredWhere the number comes from.
metric.unitstringrequiredWhat’s counted, in words.
metric.targetnumberrequiredThe value to reach by endsOn.
metric.startnumberoptionalThe value on startsOn. Left out, Esy fills it from the source.
metric.newsletterIduuidoptionalnewsletter.subscribers only: which newsletter.
parentGoalIduuidoptionalMakes it a milestone of a bigger goal.
your own goal, with a measurejsonc
POST /v1/goals
{
  "workspaceId": "…",
  "title": "1,000 newsletter subscribers",
  "horizon": "half",
  "startsOn": "2026-11-03",
  "endsOn": "2027-04-30",
  "metric": { "source": "newsletter.subscribers", "unit": "subscribers", "target": 1000 }
}

// The response carries pace: state, expected, projected,
// short, perWeekNeeded, perWeekNow, daysGone, daysLeft.
a worker’s goal, with a targetjsonc
POST /v1/goals
{
  "workspaceId": "…",
  "title": "Grow education clip art",
  "horizon": "quarter",
  "assigneeWorkerId": "worker-06997927",
  "target": { "count": 150, "categories": ["education"] }
}

// 422 without a target: a goal assigned to a worker
// needs a measurable target (count + categories).

Tasks

GET/v1/tasks?workspaceId=&status=&assigneeWorkerId=&dateFrom=&dateTo=List tasks; the date window matches dueOn or scheduledOn (one query paints a calendar).
POST/v1/tasksCreate. scheduledOn plans it for a day; scheduledTime puts it on the Timeline; goalId files it under a goal.
GET/v1/tasks/{taskId}One task.
PATCH/v1/tasks/{taskId}Update fields or status; entering done stamps completedAt. status: dropped keeps it in the record.
DELETE/v1/tasks/{taskId}Delete.
a task under a goal, at a timejson
POST /v1/tasks
{
  "workspaceId": "…",
  "projectId": "…",
  "goalId": "…",
  "title": "Write the LinkedIn post on this week's system",
  "scheduledOn": "2026-11-04",
  "scheduledTime": "10:00"
}

Messages (the Inbox)

GET/v1/messages?workspaceId=&kind=&unread=Your Inbox: reports, alerts and messages (senderType: system, user or worker).
GET/v1/messages/unread-countUnread, split by decision weight (messages vs alerts).
PATCH/v1/messages/{messageId}Read and archive state only; a message’s content never changes.

What must happen on a schedule, with proof, is a commitment, not a task: Commitments API.