Early access is open. Free plan during early access. Start free
Logthread

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.

  1. 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.
  2. 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 }}"}'
  1. 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:

PartWeightValue
How close it is40%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 is30%Halves every 15 minutes.
Whether errors rose30%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>.

FieldMeaning
typeRequired. deploy, config, flag, migration, infra or other.
titleRequired. 1 to 200 characters.
serviceThe service it changed. Leave it out for a workspace-wide change.
versionUp to 100 characters.
urlAn https:// link to the pull request or pipeline.
textA longer note, up to 2,000 characters.
timeWhen it happened, RFC 3339. Default: now. Up to 30 days back and 1 day ahead.
tagsUp to 10, each letters, digits and _ . : / -.
idYour 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.