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.
| Field | Type | Required | Description |
|---|---|---|---|
metric.source | "newsletter.subscribers" | "stripe.sales" | "manual" | required | Where the number comes from. |
metric.unit | string | required | What’s counted, in words. |
metric.target | number | required | The value to reach by endsOn. |
metric.start | number | optional | The value on startsOn. Left out, Esy fills it from the source. |
metric.newsletterId | uuid | optional | newsletter.subscribers only: which newsletter. |
parentGoalId | uuid | optional | Makes 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.