Goals and pace
A goal is an outcome with a horizon. Give it a number and a date and it becomes a score: Esy reads the number from its source every hour, keeps one reading a day, and says whether the line is heading for the target in time, and what a week needs from here.
Horizons
| Horizon | Ends |
|---|---|
day | Today |
week | This Sunday |
month | The month’s last day |
quarter | The quarter’s last day |
half | Six months on, less a day (Nov 1 → Apr 30) |
year | December 31 |
vision | No end date |
A measure
metric holds the number. A measured goal needs startsOn and endsOn: pace is progress against time.
| Field | Means |
|---|---|
source | Where the number comes from (below). |
unit | What’s counted, in words: “subscribers”, “sales”. |
start | The value on the start date. Left out, Esy fills it. |
target | The value to reach by the end date. |
newsletterId | For newsletter.subscribers: which newsletter. Left out, the organization’s main one. |
| Source | Counts | Start when left out |
|---|---|---|
newsletter.subscribers | Confirmed readers on a newsletter’s list: the ones an issue reaches | Today’s count |
stripe.sales | Stripe charges since the start date, in the goal’s brand (or the whole organization). Refunds don’t count | 0 |
manual | Values you check in, with a note | Required |
A source Esy can’t read refuses the goal when it’s created, rather than saving a goal that can never show a number.
POST /v1/goals
{
"workspaceId": "…",
"projectId": "…", // the brand; leave out for the organization
"title": "1,000 newsletter subscribers",
"horizon": "half",
"startsOn": "2026-11-03",
"endsOn": "2027-04-30",
"metric": {
"source": "newsletter.subscribers",
"unit": "subscribers",
"target": 1000 // start: left out, Esy fills today's count
}
}Readings
Esy keeps one reading per goal per day: the day’s latest. A sweep reads every measured goal hourly; you can ask for a read now, or check in a value yourself for a manual goal. The chart on the goal reads the readings, oldest first.
Pace
Every goal with a measure comes back with pace, worked out by Esy from the measure and the dates.
"pace": {
"state": "behind",
"expected": 240, // where a straight line from start to target says you'd be today
"projected": 667, // where today's rate ends up by the end date
"short": 333,
"perWeekNeeded": 44,
"perWeekNow": 26,
"daysGone": 42,
"daysLeft": 136
}| State | Means |
|---|---|
| early | Under a week in: one good day says more than the trend. |
| on_pace | The projection reaches at least 95% of the target. |
| behind | The projection falls short. |
| reached | The target is met. |
| ended | Past the end date, short. |
Milestones
A milestone is a smaller goal with parentGoalId set to the bigger one, with its own measure and end date. Dropping the big goal leaves its milestones.
Tasks under a goal
A task with a goalId is work toward that goal. That link is how the planner, and an agent, tell work that moves a goal from work that doesn’t.
metric is your own score. A worker’s goal uses target, a catalog count its shifts work toward (Assigned work). The two are separate fields.Reference: Planning API. Where goals sit: The planner and The Plan.
- A measure is a source, a unit and a target, over a start and end date.
- Esy reads it hourly and keeps one reading a day.
- pace says where you are against the line, where you’ll land, and what a week needs.