Guide
What changed before an incident
When an alert fires, the first question is what changed. Logthread lists the deploys, config changes and feature flags from the hour before each incident, scored against the service that broke, with the reasons shown. They are possibilities to check, not causes.
Find it in two places:
- Alerts, Changes: every deploy and change from the last 30 days, and Record a change.
- An incident's page: What changed before this.
Set it up#
Deploys show up on their own when a service sends service.version. To add the rest, and to link each deploy to its pull request, report changes from CI.
- Under Keys, API keys, create a key with the Create annotations permission and store it in your CI secrets as
LOGTHREAD_API_KEY. See Ingest keys and API keys. - Add a step after your deploy. In GitHub Actions:
- name: Report the deploy to Logthread
env:
LOGTHREAD_API_KEY: ${{ secrets.LOGTHREAD_API_KEY }}
run: |
curl -fsS -X POST https://logthread.com/api/changes \
-H "Authorization: Bearer $LOGTHREAD_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type":"deploy","title":"checkout ${{ github.sha }}","service":"checkout","version":"${{ github.sha }}","url":"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}","id":"${{ github.run_id }}"}'- Open Alerts, Changes: the deploy is listed, and it is on your charts.
The id makes a retried pipeline update the same change: it changes only the fields it sends, so a re-run does not move the change later unless it sends a new time. Use type config, flag, migration or infra for the other kinds of change, and leave out service for one that touches everything.
To record one by hand, select Record a change on the Changes tab.
How the list is scored#
Every change from the hour before the incident gets a score from three things:
| Part | Weight | Value |
|---|---|---|
| How close it is | 40% | The service itself 1.0. A service it calls: 0.7, 0.5 or 0.35 at one, two or three hops. A workspace-wide change 0.4. Anything else is not listed. |
| How recent it is | 30% | Halves every 15 minutes. |
| Whether errors rose | 30% | Errors in the 15 minutes before against the 15 after: 1.0 if the rate at least doubled, with 3 or more errors after. 0.0 if not. 0.5 if it could not be measured. |
From 0.75 a change is labelled Likely worth checking first, from 0.55 Worth checking, and from 0.4 Weak link. Lower scores are not shown. Each row says which of these applied, so you can judge it yourself.
When a deploy is both reported and detected from service.version, the list counts it once and keeps the reported one. Charts show both markers.
API reference#
Report a change#
POST https://logthread.com/api/changes, with an API key that has the annotate permission as Authorization: Bearer <key>.
| Field | Meaning |
|---|---|
type | Required. deploy, config, flag, migration, infra or other. |
title | Required. 1 to 200 characters. |
service | The service it changed. Leave it out for a workspace-wide change. |
version | Up to 100 characters. |
url | An https:// link to the pull request or pipeline. |
text | A longer note, up to 2,000 characters. |
time | When it happened, RFC 3339. Default: now. Up to 30 days back and 1 day ahead. |
tags | Up to 10, each letters, digits and _ . : / -. |
id | Your own id, up to 120 of the same characters. A retry with the same id updates the change. |
It answers 201 with the change. 401 is a bad key, 403 a key without the permission, 409 annotation_limit the limit of 50,000 custom annotations and changes, and 422 a field that does not fit.
What changed around a moment#
GET https://logthread.com/api/changes/around?at=2026-10-31T09:00:00Z&service=checkout, with any API key.
at is required, RFC 3339. service is optional. It answers:
{
"at": "2026-10-31T09:00:00Z",
"service": "checkout",
"lookback_minutes": 60,
"graph": true,
"items": [{
"id": "an_01M6",
"time": "2026-10-31T08:55:00Z",
"source": "change",
"change_type": "deploy",
"title": "checkout 2.4",
"service": "checkout",
"version": "2.4",
"url": "https://github.com/acme/shop/pull/7",
"minutes_before": 5,
"score": 0.94,
"confidence": "high",
"relation": "same_service",
"hops": 0,
"errors_before": 2,
"errors_after": 40,
"errors_service": "checkout",
"reasons": ["Changed checkout itself.", "5 minutes before the problem.", "Errors in checkout rose: 2 in the 15 minutes before the change, 40 in the 15 after."]
}]
}graph is false when dependencies could not be read: only direct changes are listed then. errors_before and errors_after are null when they could not be measured. relation is same_service, dependency, workspace or unscoped.
To list changes without scoring them, use GET /api/annotations?kinds=change&from=...&to=.... See Chart annotations.
SDK reference#
Each SDK uses the same key as annotations: LOGTHREAD_API_KEY, and LOGTHREAD_API_URL for the address.
Node.js, from the Node.js SDK:
import { createChange } from '@logthread/node';
await createChange({ type: 'deploy', title: 'checkout 2.4', service: 'checkout', version: '2.4', url: 'https://github.com/acme/shop/pull/7', id: process.env.GITHUB_RUN_ID });Python, from the Python SDK:
import os
import logthread
logthread.create_change("deploy", "checkout 2.4", service="checkout", version="2.4", id=os.environ["GITHUB_RUN_ID"]).NET, from the .NET SDK:
await LogthreadAnnotations.ReportChangeAsync("deploy", "checkout 2.4", service: "checkout", version: "2.4", id: runId);Failures raise LogthreadApiError (LogthreadApiException in .NET) with code and message.
AI agents#
The MCP tool what_changed gives the same answer, and the investigate_incident prompt calls it. Pari uses it too; it sees the type, service and timing, never your titles, links or error counts.