DOCS · REST API

API reference

Base URL: https://tilldev.dev (production) or http://localhost:8455 (local). Auth via httpOnly cookies (tp_access JWT, tp_refresh) or Bearer API key.

01Auth

Authentication

Every request requires a valid session — either via cookies set by POST /api/auth/login, or via a Bearer token from a personal API key.

bash
# Cookie session (web app default)
curl https://tilldev.dev/api/projects \
  --cookie 'tp_access=<jwt>; tp_refresh=<token>'

# API key (CLI / CI)
curl https://tilldev.dev/api/projects \
  -H 'Authorization: Bearer tp_9f3a...'
02Endpoints

Auth endpoints

POST /api/auth/signup — create user + org.

text
{ "email": "ada@example.com", "password": "min8chars", "name": "Ada Okafor", "org": "example" }
→ { "ok": true, "org": "example" }

POST /api/auth/login — email + password (+ TOTP).

text
{ "email": "...", "password": "...", "totpToken": "123456" }
→ { "ok": true, "org": "example", "role": "owner" }
→ { "totp_required": true }   // if 2FA enabled

POST /api/auth/logout — revoke refresh, clear cookies.

POST /api/auth/refresh — rotate refresh, mint new access.

POST /api/auth/forgot-password — body { email }; always returns 200 (no enumeration).

POST /api/auth/reset-password — body { token, password }.

POST /api/auth/change-password — body { currentPassword, newPassword }.

POST /api/auth/accept-invite — body { token, password }.

GET /api/auth/sso/start?org=example — redirect to OIDC IdP.

GET /api/auth/sso/callback — IdP redirects here; sets cookies.

GET / PUT /api/auth/sso/config — owner-only org SSO config.

032FA

Two-factor (TOTP)

POST /api/auth/2fa/setup{ otpauthUrl, backupCodes }

POST /api/auth/2fa/verify — body { token }

POST /api/auth/2fa/disable — body { password }

04Projects

Projects

GET /api/projects — list projects in org.

POST /api/projects — body { name, platform, region } (region: af-south-1 | eu-central-1 | us-east-1; platform: react-native | flutter | ios | android | web).

GET / PATCH /api/projects/:projectId/settings

POST /api/projects/:projectId/rotate-dsn — the old DSN stops accepting events within ~5 minutes.

GET /api/projects/:projectId/issues — project-scoped issue list.

POST /api/projects/:projectId/source-maps — multipart/form-data file + release + filename + symbolication_type (js_source_map | proguard).

GET /api/projects/:projectId/source-maps — list uploads.

DELETE /api/projects/:projectId/source-maps/:mapId

POST / GET /api/projects/:projectId/dsyms — iOS dSYM upload (UUID auto-extracted from Mach-O LC_UUID).

GET / POST /api/projects/:projectId/integrations/github — repo URL + webhook secret (regenerate on POST).

GET / POST / DELETE /api/projects/:projectId/integrations/tracker — Linear or Jira credentials (encrypted at rest).

05Issues

Issues

GET /api/issues — query: status, severity, projectId, limit, offset, sort.

GET /api/issues/:issueId — full detail (breadcrumbs, frames, AI analysis).

PATCH /api/issues/:issueId — body { status } (unresolved | resolved | ignored | regressed | escalated) or { assignee_id }.

DELETE /api/issues/:issueId

GET / POST /api/issues/:issueId/comments

DELETE /api/issues/:issueId/comments/:commentId (author or admin).

POST /api/issues/:issueId/tracker-ticket → creates Linear / Jira issue, links it.

GET /api/issues/search?q=...&projectId=... — full-text search across your issues.

POST /api/issues/nlq — body { project_id, query } → your question is translated to a safe, read-only query over your telemetry (only read-only queries are ever run; project filter injected server-side).

06Alerts

Alerts

GET / POST /api/alerts?projectId=...

json
{
  "projectId": "uuid",
  "name": "High severity spike",
  "trigger_type": "issue_spike",
  "conditions": { "threshold": 50, "window_minutes": 15, "severity": "high" },
  "actions": [{ "type": "slack", "webhook_url": "https://hooks.slack.com/..." }]
}

Trigger types: new_issue, issue_regression, issue_spike, security_event, pre_crash_state, battery_drain_threshold, performance_threshold, critical_path.

Action types: slack, webhook, email, pagerduty, msteams.

PATCH / DELETE /api/alerts/:ruleId

POST /api/alerts/:alertId/ack — acknowledge a fired alert.

GET /api/alerts/recent?limit=8 — used by the dashboard’s notification bell.

07Metrics

Metrics & performance

GET /api/metrics?projectId=&range=1h|24h|7d|30d&metric=crash_rate|anr_rate|event_count|user_count

GET /api/performance?projectId=&range=7d — P50/P75/P95 cold-start, ANR rate, screen-render times.

08Team

Team

GET /api/team — list members.

POST /api/team — body { email, role }; sends invite email.

PATCH /api/team/:memberId — body { role }.

DELETE /api/team/:memberId

POST /api/invites — bulk invite. Body { emails: [...], role }.

09API keys

API keys

GET /api/api-keys — list (no raw key — only prefix).

POST /api/api-keys — body { name, scopes }. Raw key returned once.

DELETE /api/api-keys/:keyId — immediate revoke.

10AI

Ask Pulse (AI)

POST /api/ask — body { messages: [{ role, content }] }; returns Server-Sent Events.

text
data: {"delta":"Computed from your telemetry..."}
data: {"delta":"```sql\nSELECT ...```"}
data: [DONE]

The model never executes queries. The dashboard renders the highlighted read-only query with a sparkline; results are computed from your telemetry.

11Audit

Audit log

GET /api/audit-log?limit=50&offset=0&action=user.password_changed — admin/owner only.

12Onboarding

Onboarding

GET /api/onboarding/status{ steps: { project_created, sdk_installed, first_event, team_invited }, completed }

13Releases

Releases

GET / POST /api/releases?projectId=...

14Notifications

Notifications

POST /api/notifications/devices — register Expo / FCM push token. Body { token, platform }.

15Webhooks

Webhooks (incoming)

POST /api/integrations/slack/interactive — Slack button payloads. Slack requests are signature-verified.

POST /api/integrations/github/webhook?project=... — GitHub requests are signature-verified against your per-project secret.

POST /api/integrations/tracker/webhook?provider=linear|jira — tracker webhooks are verified per provider.

16Errors

Error envelope

json
{ "error": "Human-readable message" }
StatusMeaning
400Validation error — check error
401Not authenticated — redirect to /auth/login
403Authenticated but insufficient role
404Resource not found
409Conflict (duplicate slug, already invited, etc.)
429Too Many Requests — you exceeded your rate limit
500Internal server error