REST API

A small, Bearer-authenticated JSON API for programmatic access to this app.

Pointing an AI agent at this API?

Hand it the LLM-ready Markdown version — self-contained instructions an agent can follow with just a URL and an API key.

Open /docs/llm/api.md

Getting started

Base URL

All endpoints live under `http://tracetap.net/api/v1`. `http://tracetap.net` is the origin you were given (scheme + host, e.g. `https://example.com`). Do not add a trailing slash.

Authentication

Every endpoint requires an API key sent as a Bearer token: `Authorization: Bearer sk_your_key_here`. Keys always start with `sk_`. A missing or invalid key returns `401 { "error": "Invalid or missing API key" }`. Create keys in the app under Profile → API Keys.

Content type

Responses are JSON unless noted (file download returns raw bytes). Request bodies are JSON (`Content-Type: application/json`) except file upload, which is `multipart/form-data`.

Probe endpoints

The `/api/v1/tracetap/*` endpoints are called by installed TraceTap probes, not by users, and use different credentials: `Authorization: Bearer pt_...` (the probe key) together with `X-TraceTap-Project: pk_...` (the project key). An `sk_` key does not work on them, and a probe key does not work anywhere else. Request bodies may additionally be sealed with the project communication key (`ck_...`) using AES-256-GCM.

Rate limiting

Requests are rate limited per API key. When you exceed a limit you get `429` (or `403` if the limit is configured to block) with an `error` message and, when applicable, a `Retry-After` header (seconds). Back off and retry.

Errors

Errors are JSON with an `error` string and a matching HTTP status (`400` bad input, `401` unauthenticated, `403` forbidden, `404` not found, `413` payload too large, `429` rate limited, `500` server error).

Your base URL is http://tracetap.net. Create API keys under Profile → API Keys.

Endpoints

GET
/api/v1/health

Health check

Confirms the API is up and your key is valid. Handy as a first call to verify credentials and connectivity.

Auth: Bearer tokenAccess: Any valid API key.

Request

curl http://tracetap.net/api/v1/health \
  -H "Authorization: Bearer sk_your_key_here"

Response

{
  "status": "healthy",
  "timestamp": "2026-07-19T12:00:00.000Z",
  "uptime": 1234.56,
  "version": "1.0.0",
  "apiKey": "My key",
  "userId": "usr_...",
  "message": "API is running successfully"
}
GET
/api/v1/stats

Account & API usage stats

Returns the calling user together with API-usage counters (requests today / this week / this month, error rate, API-key count).

Auth: Bearer tokenAccess: Any valid API key (scoped to the key owner).

Request

curl http://tracetap.net/api/v1/stats \
  -H "Authorization: Bearer sk_your_key_here"

Response

{
  "user": { "id": "usr_...", "email": "you@example.com", "name": "You", "role": "user", "createdAt": "..." },
  "apiStats": {
    "totalApiKeys": 2,
    "requestsToday": 14,
    "requestsThisWeek": 98,
    "requestsThisMonth": 412,
    "errorRate": "1.20%",
    "errorCount": 5
  },
  "meta": { "timestamp": "...", "apiKey": "My key" }
}
GET
/api/v1/users

List users

Lists users. A regular key returns only its own user record; an admin key returns all users with pagination.

Auth: Bearer tokenAccess: Any valid API key (admin keys see all users; others see themselves).
NameInTypeReq.Description
limitqueryintegernoPage size, 1–100 (default 10). Admin only; ignored for non-admins.
offsetqueryintegernoRows to skip (default 0). Admin only.

Request

curl "http://tracetap.net/api/v1/users?limit=20&offset=0" \
  -H "Authorization: Bearer sk_your_key_here"

Response

{
  "users": [
    { "id": "usr_...", "email": "you@example.com", "name": "You", "role": "user", "emailVerified": null, "createdAt": "..." }
  ],
  "meta": { "limit": 20, "offset": 0, "total": 1, "apiKey": "My key" }
}
POST
/api/v1/users

Create user (scaffold)

Admin-only endpoint scaffold for creating a user. Ships as a stub in this starter — it validates input and echoes it back rather than persisting. Fill in real creation logic before relying on it.

Auth: Bearer tokenAccess: Admin API keys only (others get 403).
NameInTypeReq.Description
emailbodystringyesNew user email.
namebodystringyesNew user display name.
rolebodystringno'user' (default) or 'admin'.

Request

curl -X POST http://tracetap.net/api/v1/users \
  -H "Authorization: Bearer sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"email":"new@example.com","name":"New User","role":"user"}'

Response

{
  "message": "User creation endpoint - implementation needed",
  "requestedData": { "email": "new@example.com", "name": "New User", "role": "user" },
  "apiKey": "My key"
}
  • This is a template stub — no user is actually created yet.
POST
/api/v1/files

Upload a file

Uploads a file and stores its raw bytes. Use this instead of a form/Server Action for any real upload (Server Actions cap the body at ~1MB; this endpoint does not). Send `multipart/form-data` with a single `file` field.

Auth: Bearer tokenAccess: Any valid API key (the file is owned by the key owner).
NameInTypeReq.Description
fileformfileyesThe file to upload (multipart field name must be "file").

Request

curl -X POST http://tracetap.net/api/v1/files \
  -H "Authorization: Bearer sk_your_key_here" \
  -F "file=@./photo.png"

Response

{
  "id": "fil_...",
  "filename": "photo.png",
  "url": "/api/v1/files/fil_..."
}
  • Default max size is 100MB (configurable via MAX_FILE_SIZE). Oversized uploads return 413.
  • The returned `url` is the Bearer-gated download endpoint below.
GET
/api/v1/files/:id

Download / preview a file

Streams the raw file bytes with the stored Content-Type. Because it is Bearer-gated you cannot put it directly in an `<img src>`; fetch it with the token and build an object URL client-side.

Auth: Bearer tokenAccess: Any valid API key.
NameInTypeReq.Description
idpathstringyesFile id returned by the upload endpoint.

Request

curl http://tracetap.net/api/v1/files/fil_your_file_id \
  -H "Authorization: Bearer sk_your_key_here" \
  --output downloaded-file

Response

Raw binary body with the stored `Content-Type` and `Content-Disposition: inline; filename="..."`. Returns `404 { "error": "File not found" }` if unknown.
DELETE
/api/v1/files/:id

Delete a file

Deletes a file owned by the calling key.

Auth: Bearer tokenAccess: Any valid API key (only the owner may delete).
NameInTypeReq.Description
idpathstringyesFile id to delete.

Request

curl -X DELETE http://tracetap.net/api/v1/files/fil_your_file_id \
  -H "Authorization: Bearer sk_your_key_here"

Response

{ "deleted": true }   // { "deleted": false } with status 404 if not found / not owned
POST
/api/v1/tracetap/ingest

Submit sampled traffic (probe)

Where an installed probe posts the messages it sampled. Bodies have already been transformed by the project rules inside the probe, so nothing arrives here that the rules did not allow through. The body is either plain JSON or a sealed envelope encrypted with the project communication key.

Auth: Probe key + project headerAccess: A probe key belonging to the project named in X-TraceTap-Project.
NameInTypeReq.Description
X-TraceTap-ProjectheaderstringyesThe project key (pk_...).
messagesbodyarrayyesCaptured messages. Each may carry method, path, statusCode, durationMs, protocol, operation, requestHeaders, responseHeaders, requestBody, responseBody, error and rulesVersion.
connectionIdbodystringnoPins these messages to one connection on the topology.
probeVersionbodystringnoVersion of the probe, recorded for support.
statsbodyobjectnoTraffic counters since the last report: { observed, sampled, withheld, errors, bytesObserved, since }. This is the only way TraceTap can know real traffic volume — stored samples are 1 in N by design, so counting them measures the sampling rate, not the link.

Request

curl -X POST http://tracetap.net/api/v1/tracetap/ingest \
  -H "X-TraceTap-Project: pk_your_project_key" \
  -H "Authorization: Bearer pt_your_probe_key" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [{
      "method": "POST",
      "path": "/api/customers",
      "statusCode": 201,
      "requestBody": "{\"email\":\"generated@example.com\"}",
      "responseBody": "{\"id\":42}"
    }]
  }'

Response

{
  "accepted": 1,
  "divergences": 1,
  "rulesVersion": 3
}
  • To encrypt, send { "v": 1, "iv": "...", "ct": "...", "tag": "..." } — AES-256-GCM under the project communication key, wrapping the JSON body above.
  • At most 100 messages are processed per request; the rest are ignored.
  • `rulesVersion` in the response tells the probe whether a newer rules document exists.
  • `stats` counters are deltas since the probe last reported, not running totals. Send them again if a request fails, or that window is lost.
  • The counters are plain numbers — no payload content — so reporting them does not weaken the guarantee that nothing sensitive leaves the probe.
GET
/api/v1/tracetap/rules

Fetch the deterministic generation rules (probe)

Returns the rules document a probe enforces locally: which fields are personal data or secrets, which generator replaces each one, and what to do with anything unclassified. Pass `since` with the version you already have to get 204 when nothing changed.

Auth: Probe key + project headerAccess: A probe key belonging to the project named in X-TraceTap-Project.
NameInTypeReq.Description
X-TraceTap-ProjectheaderstringyesThe project key (pk_...).
sincequeryintegernoRules version the probe already holds. Omit, or pass 0, to always receive the document.

Request

curl "http://tracetap.net/api/v1/tracetap/rules?since=0" \
  -H "X-TraceTap-Project: pk_your_project_key" \
  -H "Authorization: Bearer pt_your_probe_key"

Response

{
  "format": 1,
  "projectKey": "pk_...",
  "version": 3,
  "publishedAt": "2026-08-24T10:00:00.000Z",
  "sampleEveryN": 20,
  "unknownFieldPolicy": "mask",
  "generators": [
    { "id": "gen_...", "name": "email", "kind": "builtin", "builtinId": "email", "config": {} }
  ],
  "fields": [
    { "endpoint": "POST /api/customers", "direction": "request", "section": "body",
      "pointer": "$.email", "classification": "pii", "piiKind": "email",
      "action": "generate", "generatorId": "gen_..." }
  ],
  "redactHeaders": ["authorization", "cookie", "set-cookie", "x-api-key"]
}
  • Returns 204 with no body when `since` is 1 or higher and already matches the current version.
  • `unknownFieldPolicy` is what a probe applies to any field not listed: the default, `mask`, is why an unreviewed field never leaves in the clear.
POST
/api/v1/tracetap/heartbeat

Probe liveness and error reporting (probe)

Marks the probe as alive and, optionally, reports a local problem it cannot fix itself. The response states which rules version the probe should be running and how often it should sample.

Auth: Probe key + project headerAccess: A probe key belonging to the project named in X-TraceTap-Project.
NameInTypeReq.Description
X-TraceTap-ProjectheaderstringyesThe project key (pk_...).
versionbodystringnoProbe version string.
errorbodystringnoA local failure worth surfacing in the Pulse feed.
statsbodyobjectnoThe same traffic counters the ingest endpoint accepts. Sent here too because a probe sampling 1 in 1000 on a quiet link may go a long time without an ingest call, and its traffic volume still has to arrive.
metabodyobjectnoFree-form diagnostics stored with the event.

Request

curl -X POST http://tracetap.net/api/v1/tracetap/heartbeat \
  -H "X-TraceTap-Project: pk_your_project_key" \
  -H "Authorization: Bearer pt_your_probe_key" \
  -H "Content-Type: application/json" \
  -d '{
    "version": "1.0.0",
    "stats": {
      "observed": 4210, "sampled": 210, "withheld": 0,
      "errors": 7, "bytesObserved": 8402331,
      "since": "2026-08-24T12:00:00.000Z"
    }
  }'

Response

{
  "ok": true,
  "rulesVersion": 3,
  "sampleEveryN": 20
}
API Documentation · TraceTap