API Reference
REST API reference for Flowtag services.
The Flowtag REST API gives you direct HTTP access to Flowtag services. Use it to integrate with any language or runtime that can make HTTP requests.
Services
| Service | Base URL | Authentication |
|---|---|---|
| Flowtag API | https://api.flowtag.dev | API key, sent as a bearer token |
| Variables API | https://variables.flowtagservices.com | Variable token, sent as a query parameter |
The Flowtag API currently covers data subjects only — listing them, reading what is stored about one, and erasing it. Management endpoints for trackers, monitors, and status pages are not exposed yet; use the Dashboard for those in the meantime.
Versioning
Every Flowtag API path carries a version, then the service it belongs to:
https://api.flowtag.dev/v1/{service}/...Resources below that are nested normally, so a data subject on a tracker is
/v1/analytics/trackers/{trackerId}/subjects/{subjectId}. Breaking changes ship
under a new version prefix; /v1 keeps its shape.
Authentication
Send your API key as a bearer token:
curl https://api.flowtag.dev/v1/analytics/trackers/trk_123/subjects \
-H "Authorization: Bearer ft_yourPublicKey.yourSecret"Each endpoint lists the scope it requires. Grant scopes when you create the key under Settings -> API Keys — a key can only do what its scopes allow, so issue one key per integration with the narrowest set that works.
Response format
Successful responses wrap their payload in data, alongside a meta object:
{
"data": { },
"meta": {
"requestId": "0f8fad5b-d9cb-469f-a165-70867728950e",
"timestamp": "2026-08-07T12:34:56.789Z"
}
}Failures use the same envelope shape with an error object instead:
{
"error": {
"code": "forbidden",
"message": "API key is missing the required scope \"analytics:delete-data\""
},
"meta": {
"requestId": "0f8fad5b-d9cb-469f-a165-70867728950e",
"timestamp": "2026-08-07T12:34:56.789Z"
}
}error.details is present only when a failure has something per-field to report
— in practice validation_error. It is omitted entirely otherwise, so check for
it rather than expecting an empty array:
{
"error": {
"code": "validation_error",
"message": "Request validation failed",
"details": [{ "field": "limit", "message": "Too big: expected number to be <=200" }]
},
"meta": {
"requestId": "0f8fad5b-d9cb-469f-a165-70867728950e",
"timestamp": "2026-08-07T12:34:56.789Z"
}
}meta.requestId is also returned as the X-Request-Id header. Log it — quoting
it makes support requests much faster to resolve. If you send your own
X-Request-Id, Flowtag uses that value instead.
Rate limits
Requests are limited per API key, with a separate per-IP limit in front of authentication:
| Limit | Applies to | Budget |
|---|---|---|
| Per IP address | All requests | 60 per minute |
| Per API key | All requests | 120 per minute |
| Per API key | Erasure requests only | 60 per hour |
Responses from these endpoints carry the current budget, including error responses:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests allowed in the window. |
X-RateLimit-Remaining | Requests left in the window. |
X-RateLimit-Reset | Unix timestamp (seconds) when the window resets. |
Retry-After | Seconds to wait. Sent on 429 responses only. |
Treat the headers as advisory rather than guaranteed — they are absent from unmetered endpoints such as the OpenAPI document, and briefly absent if our rate limiter itself is unavailable.
Exceeding a limit returns 429 with the rate_limited error code. Back off for
the number of seconds in Retry-After before retrying — retrying sooner just
consumes more budget.
Erasure has its own hourly budget because it is irreversible. If you process
deletion requests in batches, spread them across the hour rather than firing them
all at once. Erasure is idempotent, so a retry after a timeout or 429 is safe:
a subject that holds no data returns zero counts and alreadyErased: true.
Error codes
| Code | Status | Meaning |
|---|---|---|
invalid_request | 400 | The request could not be processed as sent. |
unauthorized | 401 | The API key is missing, malformed, revoked, or expired. |
forbidden | 403 | The key is valid but lacks the scope the endpoint requires. |
not_found | 404 | The resource does not exist, or the key's organisation cannot see it. |
validation_error | 422 | A parameter failed validation; see error.details. |
rate_limited | 429 | A rate limit was exceeded; see Retry-After. |
internal_error | 500 | Something went wrong on our side. Retry, then contact support with the request id. |
A resource belonging to another organisation returns not_found, not
forbidden — a 403 would confirm that the id exists.
Data retention
Analytics data is not kept indefinitely. Sessions and events past your plan's
retention window are deleted automatically, so results from this API shrink over
time: a data subject that returned data last month can return not_found today,
and sessions and events counts fall as older activity ages out. Aggregated
data that carries no visitor link is not affected.
Retention windows depend on your plan — see the Analytics Data Retention Policy. If you need a point-in-time record of what was stored, read the subject and keep the response; do not assume you can re-read it later.