Metric ingest
Send numbers that live in other systems — monthly MRR, daily deploy count — to a GoalTrack metric and see them against the target line. You need two things: the metric’s ingest URL and a key with the metrics:ingest scope.
1. Copy the ingest URL
Section titled “1. Copy the ingest URL”Each metric has its own ingest URL. You can copy it in two places:
- On the Metrics screen, open the metric and use the copy button on the Ingest URL card.
- In Settings › API, pick the metric on the Ingest URL card and copy it.
Copy the URL from the app instead of building it by hand — the app builds it with the same function the server uses to register the route. The URL is not a secret: every request without a key is rejected.
2. Create a key
Section titled “2. Create a key”In API keys, we recommend creating a key with only the Metric ingest (metrics:ingest) scope. Such a key can add values and nothing else — it cannot read or write issues. Members with the viewer role cannot pick this scope.
3. Send values
Section titled “3. Send values”One value
Section titled “One value”curl -X POST "$GOALTRACK_INGEST_URL" \ -H "Authorization: Bearer $GOALTRACK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "value": 12500000, "ts": "2026-10-01T00:00:00Z", "labels": { "plan": "pro" } }'| Field | Required | Description |
|---|---|---|
value |
Yes | A number, or a string holding a decimal ("1234.56"). Its absolute value must be below 10¹⁴. |
ts |
No | An ISO 8601 time with seconds and a time zone (Z or +09:00), e.g. 2026-10-01T00:00:00Z. Without seconds (2026-10-01T00:00Z) the request is rejected with 400. Defaults to now. Stored rounded down to the metric’s default unit (day, week, or month). |
labels |
No | { "key": "value" }. Keys must be dimensions defined on the metric — otherwise 400 INVALID_LABELS. Leading and trailing whitespace in values is trimmed before saving (" web" = "web"). A value that is empty after trimming returns 400 METRIC_LABEL_EMPTY. |
source |
No | When omitted it is recorded as webhook, so it stays distinct from values people typed in the app (manual). |
On success you get 201 and the stored value.
Many values (batch)
Section titled “Many values (batch)”Append /bulk to the ingest URL and send a rows array — 1 to 1000 rows per request, each shaped like a single value.
curl -X POST "$GOALTRACK_INGEST_URL/bulk" \ -H "Authorization: Bearer $GOALTRACK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "rows": [ { "value": 11800000, "ts": "2026-09-01T00:00:00Z" }, { "value": 12500000, "ts": "2026-10-01T00:00:00Z" } ] }'On success you get 201 and { "count": 2 }. Batches are limited to 20 per minute per key (single values: 120).
Hand it to an AI agent
Section titled “Hand it to an AI agent”Give the AI agent instruction from Send values automatically on the metric’s page to an agent that can run shell commands, and the agent records values with the one-value command above. Put the same metrics:ingest key in the GOALTRACK_API_KEY environment variable where the agent runs. An AI agent key (gtag_) cannot record metric values — agent keys only open issue, comment, and status requests.
Retrying is safe
Section titled “Retrying is safe”Sending again for the same metric, the same time (after rounding down), and the same labels overwrites the value. Retrying a failed request as is never stores a value twice.
Common rejections
Section titled “Common rejections”| Response | Why |
|---|---|
401 UNAUTHORIZED |
The key is missing or wrong. |
403 INSUFFICIENT_SCOPE |
The key does not have the metrics:ingest scope. |
403 WRITER_ROLE_REQUIRED |
The person who created the key is now a viewer. |
404 NOT_FOUND |
The metric does not exist or is not in this key’s workspace. |
409 METRIC_ARCHIVED |
The metric is archived. Unarchive it on the Metrics screen. |
The full list is in Error codes.