Developers
API & webhooks
Use the Milestra API to pull goals into dashboards (Power BI, Looker Studio, Excel), push numbers in from other systems, or trigger automations in Zapier and Make. Workspace admins create keys in Settings → API & webhooks.
Authentication
Send your key in the Authorization header. Keys are shown once when created; we only store a hash.
curl https://milestra.app/api/v1/goals \
-H "Authorization: Bearer mst_live_…"- Scopes: every key can read; tick “allow updating key results” to add the write scope.
- Rate limit: 120 requests per minute per key. Over the limit you get HTTP 429 with a Retry-After header.
- Pagination:
limit(1–200, default 50) andoffset; responses includepagination.total. - Errors: JSON
{"error":{"code":"…","message":"…"}}with a matching HTTP status.
Endpoints
| Method | Path | What it does |
|---|---|---|
GET | /api/v1/me | Check your key and see the workspace it belongs to. |
GET | /api/v1/goals | List goals with progress and health. Filters: status, health (GREEN, AMBER, RED, GRAY), updated_since, limit, offset. |
GET | /api/v1/goals/{id} | One goal with its key results, actions and owner ids. |
GET | /api/v1/key-results | List key results. Filter: goal_id. |
GET | /api/v1/key-results/{id} | One key result. |
PATCH | /api/v1/key-results/{id} | Update current_value (needs a key with the write scope). Goal progress and health recalculate automatically. |
GET | /api/v1/actions | List action steps. Filters: status (open, completed, overdue), goal_id. |
Example: update a key result
curl -X PATCH https://milestra.app/api/v1/key-results/KR_ID \
-H "Authorization: Bearer mst_live_…" \
-H "Content-Type: application/json" \
-d '{"current_value": 48}'Webhooks
Add a webhook URL (https only) and choose events. Milestra sends a JSON POST within seconds of the change. Each delivery is logged; a webhook is paused automatically after 20 failed deliveries in a row.
| Event | When |
|---|---|
goal.created | A goal is created in the app. |
key_result.updated | A key result's current value changes (app or API). |
check_in.submitted | Someone submits or updates their weekly check-in. |
action.completed | An action step is marked done. |
{
"id": "5f0c…", // unique delivery id (also in X-Milestra-Delivery)
"event": "key_result.updated",
"created_at": "2026-10-05T09:12:44.120Z",
"organization_id": "…",
"data": { "key_result": { "id": "…", "title": "Learners at grade level", "current_value": 48, "target_value": 60 } }
}Verifying signatures
Every request carries X-Milestra-Signature: t=<unix seconds>,v1=<hex>, where v1 is the HMAC-SHA256 of <t>.<raw body> using your webhook's signing secret. Reject requests whose signature doesn't match or whose timestamp is more than 5 minutes old.
import crypto from "node:crypto";
export function verify(secret, rawBody, header) {
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(v1, "hex"));
}Zapier and Make
- Trigger: use “Webhooks by Zapier → Catch Hook” (or Make's Custom webhook) and paste its URL as a Milestra webhook.
- Action: use “Webhooks by Zapier → Custom Request” with your API key to update a key result.