Issues

Session API for issue list, status, merge, snooze, and bulk actions.

All routes below are under /api/v1 and require a dashboard session. Org scope comes from the session; PostgreSQL RLS enforces isolation.

Calling the Issues API

Login returns 204 plus a cookie, then GET /api/v1/issues. Unauthenticated calls return 401 { "error": "unauthenticated" }.

Ingest is separate: Ingest. Occurrences: Events.

First call with the seed user

Login returns 204 and Set-Cookie (Epure.sid on local HTTP).

curl -sS -c cookies.txt -D - -o /dev/null -X POST \
  "http://localhost:8080/api/v1/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"email":"dev@epure.local","password":"devpassword"}'
HTTP/1.1 204 No Content
Set-Cookie: Epure.sid=...; HttpOnly; Path=/; SameSite=Lax
curl -sS -b cookies.txt \
  "http://localhost:8080/api/v1/issues?q=is:unresolved&window=7d"

DSN headers on this path → 401. Use the cookie.


How do I list issues?

GET /api/v1/issues?project_id={uuid}&q={query}&window={window}&sort={sort}
QueryDefaultValues
project_idall org projectsUUID
qemptyLinear-style tokens + free text
window7d24h, 7d, 14d, 30d, 90d
sortlast_seen_desclast_seen_asc, events_desc, events_asc, first_seen_desc, first_seen_asc, title_asc

Query tokens (q)

TokenEffect
is:unresolved / is:openstatus unresolved
is:resolvedstatus resolved
is:ignoredstatus ignored
is:regressionstatus regression
is:snoozedactive snooze hold
env:{name}environment
release:{version}release
user.email:{pattern}user email pattern
level:{level}level
other tokensfree-text substring

Response 200

{
  "issues": [
    {
      "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "org_id": "11111111-1111-4111-8111-111111111111",
      "project_id": "550e8400-e29b-41d4-a716-446655440000",
      "fingerprint": "a3f1…",
      "title": "TypeError: Cannot read property 'x'",
      "status": "unresolved",
      "level": "error",
      "environment": "local",
      "release": "my-app@0.0.0",
      "event_count": 42,
      "unique_user_count": 3,
      "last_seen_at": "2026-09-17T12:00:00Z",
      "first_seen_at": "2026-09-16T08:00:00Z",
      "resolved_in_release": null,
      "snoozed": false,
      "snooze_until": null,
      "snooze_until_count": null,
      "snooze_until_users": null
    }
  ]
}

Merged children are omitted from the default list (merge_parent_id IS NULL).


POST /api/v1/issues/trends
Content-Type: application/json
{ "issue_ids": ["7c9e6679-7425-40de-944b-e07fc1f90ae7"] }

Max 200 IDs. Empty list → { "trends": [] }. Over 200 → 400.

Response: { "trends": [ { "issue_id": "…", "buckets": [ { "start": "…", "count": 3 } ] } ] }.


How do I patch an issue?

PATCH /api/v1/issues/{id}
{ "status": "resolved", "resolved_in_release": "1.4.0" }
FieldNotes
statusunresolved | resolved | ignored | regression
resolved_in_releaseoptional string for “resolved in next release”

At least one field required. Invalid status → 400. Missing issue → 404. Success returns the updated IssueSummary object.

curl -sS -b cookies.txt -X PATCH \
  "http://localhost:8080/api/v1/issues/${ISSUE_ID}" \
  -H "Content-Type: application/json" \
  -d '{"status":"resolved"}'

How do I snooze an issue?

POST /api/v1/issues/{id}/snooze
{ "mode": "hours" }
modeMeaning
hours or 4hsnooze 4 hours
occurrences or 100until 100 occurrences
users or 10until 10 unique users

Unknown mode → 400. Success → updated issue JSON.


Bulk update or delete

PATCH /api/v1/issues/bulk
{ "ids": ["7c9e6679-7425-40de-944b-e07fc1f90ae7"], "action": "resolve" }
actionResulting status
resolveresolved
ignoreignored
reopenunresolved
or a valid status stringthat status

Response: { "updated": 1 }.


Merge or split issues

POST /api/v1/issues/merge
{
  "canonical_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "merge_ids": ["aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"]
}
POST /api/v1/issues/split
{
  "canonical_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "split_ids": ["aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"]
}

Empty merge_ids / split_ids400. Response: { "updated": <count> }.

GET /api/v1/issues/{id}/merged

Lists child issues merged into {id}, in the same { "issues": […] } shape as the list route.

Where are events for an issue?

Occurrence routes live on Events: GET /api/v1/issues/{id}/events, timeline, releases, and crash-dialog feedback. Those paths need a session cookie; a DSN cannot list occurrences. Spike-valve floods can leave fewer stored rows than event_count.

Can I list issues with the DSN?

No. The DSN is ingest only. Login first (POST /api/v1/auth/login204 plus cookie), then GET /api/v1/issues. DSN headers here return 401 { "error": "unauthenticated" }. Org scope comes from the session and RLS: Authentication.