Goals

A goal defines target thresholds/triggers against a metric.

A goal defines target thresholds/triggers against a metric.

Full field reference (types, enums, required/read-only flags) is generated from components.schemas.Goal in the API Reference tab, and tabulated field by field — with what each one's read-only status means for a write — in Field Reference. Or fetch the live OpenAPI document directly, see Getting Started for the URL.

GET /goals · GET /goals/:goalId

{
  "id": "ee3ed006c5734ec6895c640607a5ebee",
  "name": "MRR target",
  "type": "THRESHOLD",
  "ownerId": "4274514cd27b485e8d4630dd611c01e5",
  "payload": { "value": 50000, "thresholdType": "ABOVE", "valueType": "VALUE" },
  "scopes": { "metrics": ["a5b92ef1de2b4a78b1fbab5ee0340113"] },
  "triggers": [{ "type": "event.updated.metric.data" }],
  "createdAt": "2026-02-01T00:00:00.000Z",
  "updatedAt": "2026-02-10T00:00:00.000Z",
  "version": "1.0"
}

type is one of THRESHOLD, RECURRING_TARGET, CUSTOM_TARGET, RANGE, and determines payload's shape. triggers[].type is one of event.updated.metric.data (fires on new metric data) or event.updated.schedule.period_end (fires on a recurring schedule) — not a threshold direction; payload.thresholdType (ABOVE/BELOW) is what carries that.

payload by type

notifyOptions, wherever it appears below, is an array of strings — even when it holds a single option.

THRESHOLD — a single value compared against a fixed threshold:

{ "value": 50000, "thresholdType": "ABOVE", "valueType": "VALUE" }

RANGE — a lower/upper bound instead of a scalar value:

{ "lowerValue": 40000, "upperValue": 60000, "notifyOptions": ["CROSSED_OUTSIDE"] }

RECURRING_TARGET — a target re-evaluated every period:

{ "value": 50000, "recurringPeriod": "M" }

recurringPeriod is D (daily), w (weekly) or M (monthly) — note that the weekly code is lower-case while the other two are upper-case.

CUSTOM_TARGET — an explicit target per period, rather than one recurring value:

{
  "customGranularity": "M",
  "values": [
    { "coordinate": "2026-01-01T00:00:00.000Z", "value": 45000 },
    { "coordinate": "2026-02-01T00:00:00.000Z", "value": 47000 }
  ]
}

POST /goals

name and type are required. Returns 201 with the meta/data envelope. A metric scope (scopes.metrics) is practically required too — creating a goal without one fails with 403 on the share-rights check. When a metric scope is present, its id is attached to each trigger internally.

triggers itself is optional, but each entry you do send must name its type — that is the whole of a public trigger, so an entry without one is rejected with 400.

PUT /goals/:goalId

Replace editable fields; returns the updated goal. To change a threshold, fetch the goal, edit the value under payload, and send it back — the read-only fields the GET returned (id, ownerId, createdAt, updatedAt) are ignored as long as id/ownerId still match the stored goal, so there is nothing to strip out first. Sending a different id or ownerId is rejected with 400:

{
  "name": "MRR target",
  "type": "THRESHOLD",
  "payload": { "value": 60000, "thresholdType": "ABOVE", "valueType": "VALUE" },
  "scopes": { "metrics": ["a5b92ef1de2b4a78b1fbab5ee0340113"] },
  "triggers": [{ "type": "event.updated.metric.data" }]
}

This is a replace, not a merge, at every level — send the goal back whole, with your edit applied, exactly as the GET returned it. In particular payload is replaced as a unit, so a body carrying only { "value": 60000 } drops thresholdType and valueType; and an omitted scopes or triggers is cleared rather than kept. name and type are required on every write.

DELETE /goals/:goalId

204 No Content.


Did this page help you?