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.
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.
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.
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`.
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.
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 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.
/api/v1/healthConfirms the API is up and your key is valid. Handy as a first call to verify credentials and connectivity.
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"
}/api/v1/statsReturns the calling user together with API-usage counters (requests today / this week / this month, error rate, API-key count).
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" }
}/api/v1/usersLists users. A regular key returns only its own user record; an admin key returns all users with pagination.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| limit | query | integer | no | Page size, 1–100 (default 10). Admin only; ignored for non-admins. |
| offset | query | integer | no | Rows 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" }
}/api/v1/usersAdmin-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.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| body | string | yes | New user email. | |
| name | body | string | yes | New user display name. |
| role | body | string | no | '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"
}/api/v1/filesUploads 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.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| file | form | file | yes | The 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_..."
}/api/v1/files/:idStreams 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.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| id | path | string | yes | File 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-fileResponse
Raw binary body with the stored `Content-Type` and `Content-Disposition: inline; filename="..."`. Returns `404 { "error": "File not found" }` if unknown./api/v1/files/:idDeletes a file owned by the calling key.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| id | path | string | yes | File 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/api/v1/tracetap/ingestWhere 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.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| X-TraceTap-Project | header | string | yes | The project key (pk_...). |
| messages | body | array | yes | Captured messages. Each may carry method, path, statusCode, durationMs, protocol, operation, requestHeaders, responseHeaders, requestBody, responseBody, error and rulesVersion. |
| connectionId | body | string | no | Pins these messages to one connection on the topology. |
| probeVersion | body | string | no | Version of the probe, recorded for support. |
| stats | body | object | no | Traffic 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
}/api/v1/tracetap/rulesReturns 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.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| X-TraceTap-Project | header | string | yes | The project key (pk_...). |
| since | query | integer | no | Rules 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"]
}/api/v1/tracetap/heartbeatMarks 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.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| X-TraceTap-Project | header | string | yes | The project key (pk_...). |
| version | body | string | no | Probe version string. |
| error | body | string | no | A local failure worth surfacing in the Pulse feed. |
| stats | body | object | no | The 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. |
| meta | body | object | no | Free-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
}