Endpoint Investigations
SQLite-backed investigation records with review status, tags, notes, and evidence.
When triage surfaces an endpoint worth tracking — an IDOR candidate, a rate-limited login route, a token mint endpoint — you can pin a durable investigation record to it. Each record is keyed to a catalog route, carries a review status, free-form tags, and a notes field for evidence and reproduction steps, and is stored in the same SQLite database as your imported captures so it survives restarts.
Where investigation records live
Records are edited from the route catalog in the API lab. Open the API workspace, expand a route row in the catalog, and the Investigation record section appears alongside the schema, GraphQL, and decode-pipeline panels for that route. The section header shows the current status as a badge once a record exists.
The record is bound to the catalog entry’s endpoint key, so one route has exactly one record. See /docs/en/api-re/catalog/ for how routes are grouped.
Create and edit a record
- Expand the target route in the catalog.
- In the Investigation record section, pick a review status from the dropdown (defaults to
unreviewed). - Enter comma-separated tags, e.g.
auth, BOLA, tier-1. - Write evidence and reproduction notes in the text area.
- Click Save. The status badge updates and the record is written to SQLite.
To discard a record, click the trash button next to Save. Deleting removes the row entirely; the route returns to having no record (it is not reset to unreviewed).
Review status states
The status is the investigation workflow state. The backend rejects any value outside this set with HTTP 400.
| Status | Meaning | Badge |
|---|---|---|
unreviewed | Default; seen but not yet assessed | neutral |
reviewed | Assessed, nothing actionable | green |
interesting | Worth deeper investigation | amber |
vulnerable | Confirmed finding on an authorized target | red |
false-positive | Signal disproven | neutral |
[!NOTE]
vulnerableis for findings confirmed against systems you are authorized to test. Record the authorization scope in the notes so the record stands on its own when reviewed later.
Tags
Tags are short labels for grouping and filtering. On save the backend normalizes them:
- Each tag is trimmed of surrounding whitespace.
- Empty tags are dropped.
- Duplicates are removed case-insensitively (
auth,Auth, andAUTHcollapse to the first spelling seen). - A tag longer than 64 characters, or a 31st tag, fails validation with HTTP 400.
In the editor, type tags as one comma-separated string; they are split and trimmed on save.
Notes and evidence
The notes field is a free-text area for the substance of the investigation: observed behavior, reproduction steps, replay outcomes, owners, and remediation. The editor placeholder prompts Evidence, reproduction notes, owner, remediation….
Notes are capped at 20,000 characters; longer input is rejected with HTTP 400. There is no separate evidence object — the status is the workflow state and the notes field is where evidence and reproduction detail live. Paste the concrete request/response excerpts and the steps that reproduce the behavior so the finding is self-contained.
[!TIP] When a finding depends on replay, note the replay variables and derivation expressions used so the reproduction is repeatable. See /docs/en/replay/variables/ and /docs/en/replay/workbench/.
How records persist
Records live in the endpoint_annotations table of the Traffic Jam SQLite database (default .traffic-jam/traffic-jam-v2.sqlite3, overridable with TRAFFIC_JAM_DB_PATH):
create table if not exists endpoint_annotations (
endpoint_key text primary key,
status text not null default 'unreviewed',
tags_json text not null default '[]',
notes text not null default '',
updated_at text not null
);
endpoint_key is the primary key, so saving is an upsert: a second save for the same route overwrites status, tags, and notes and bumps updated_at (stored as an RFC 3339 UTC timestamp). Tags are stored as a JSON array in tags_json. A secondary index on (status, updated_at desc) backs listing by status and recency. Because the data is in the capture database, records survive API restarts and travel with the database file.
How records connect to the catalog and replay
The join between an investigation record and the rest of the workspace is the endpoint key, formatted method:host:pathTemplate, for example:
GET:api.example.test:/users/{id}
This is the same key that identifies a route in the catalog and keys per-route decode pipelines and signature analysis. A record therefore annotates a catalog route directly: the route’s latency, error rate, and session count in the catalog are the context around your finding, and replay is launched from that same catalog entry. Use the notes field to tie a record back to specific replay runs and passive findings (see /docs/en/security/findings/).
REST API
The frontend calls a small JSON API, which you can also drive directly:
| Method | Path | Purpose |
|---|---|---|
GET | /api/endpoint-annotations | List all records, newest updated_at first |
POST | /api/endpoint-annotations | Create or update a record (upsert by endpointKey) |
DELETE | /api/endpoint-annotations/{endpointKey} | Delete one record |
The POST body is:
{
"endpointKey": "GET:api.example.test:/users/{id}",
"status": "interesting",
"tags": ["auth", "BOLA"],
"notes": "User A can read user B's profile by swapping the id."
}
Validation failures (missing endpointKey, unknown status, oversized tags or notes) return HTTP 400; deleting a nonexistent key returns HTTP 404. Requests must satisfy the same origin and capability-token rules as the rest of the API.
Workflow: from interesting endpoint to documented finding
- Spot a candidate in search or passive findings and open its route in the catalog.
- Set the record to
interestingand tag the suspected class (BOLA,auth-bypass,rate-limit). - Replay the route — varying credentials or path IDs as the hypothesis requires — and capture the confirming request/response in the notes. See /docs/en/replay/workbench/.
- On confirmation against an authorized target, move the status to
vulnerableand record scope, impact, and remediation. If the signal does not reproduce, mark itfalse-positive. - Leave the record in place as the durable audit trail; it persists in SQLite with the capture that produced it.