The platform API is how your backend talks to TAGBASE. You provision tags for your items and submit the scans your users make. The API answers whether a tap was genuine, and the page or app your users see stays yours.
It is a JSON:API over HTTPS. A sandbox key lets you build against virtual tags and emulated scans before any hardware ships. A production key works the same way against physical tags. See Authentication for how to get either one.
Conventions
Every endpoint follows the same conventions, so once you have made one request the rest are predictable.
Base URL
https://platform.tagbase.io/api/v1
All paths in this reference are relative to that base.
Content type
Send Content-Type: application/vnd.api+json on any request with a body. The
API rejects a request that names a different media type. It tolerates a
request that omits the header, but sending it is best practice. Accept is
optional; responses always come back as application/vnd.api+json. If you do
send Accept, use the same media type.
Content-Type: application/vnd.api+json
Request bodies are a single JSON:API document, a data object carrying a
type and an attributes map:
{
"data": {
"type": "tags",
"attributes": { "...": "..." }
}
}
Identifiers
Every resource id is a URL-safe string with a type prefix (team_, key_,
tag_, ses_, vrf_). The prefix identifies the resource type; treat the full
string as opaque.
Errors
Errors come back as a JSON:API errors array. Each entry has an HTTP status
and a short title:
{
"errors": [
{ "status": "401", "title": "Unauthorized" }
]
}
The HTTP response status matches the status field. The statuses you’ll see:
| Status | When |
|---|---|
400 |
The request body is missing or malformed (no data.attributes, or an out-of-range value). |
401 |
Missing, invalid, or revoked API key. |
404 |
The resource doesn’t exist under your team. |
422 |
The request was well-formed but could not be processed (validation failed). |
A 422 from a tag write carries one error per failed attribute, with a
detail message and a source.pointer naming the field:
{
"errors": [
{
"status": "422",
"title": "Unprocessable Entity",
"detail": "should be at most 50 character(s)",
"source": { "pointer": "/data/attributes/comment" }
}
]
}
Other error bodies carry only status and title.
Idempotency
The API has no idempotency keys, and every POST creates a new resource each
time it’s called. Retrying a request that already succeeded (after a timeout or
a dropped connection) creates a duplicate: another batch of tags, another
subteam, or another verification.
So retry deliberately. When you get no response (a timeout or a dropped connection), the outcome is unknown: the first attempt may have landed. Before you retry a create, confirm on your side whether it did, or accept that a duplicate may exist and reconcile it afterwards.
A response you did receive tells you what to do:
400,401,404are definitive: the same request will fail the same way, so fix the request rather than retry it.422means nothing was recorded, so retrying the identical request is safe and won’t create a duplicate. A422that keeps recurring means the request itself is wrong, so escalate it rather than keep retrying.201succeeded. Don’t resend it, or you’ll create a duplicate.
Rate limiting
Requests are throttled per client IP at 600 requests per minute across the
API. Exceeding the limit returns a plain-text 429 Too Many Requests (not a
JSON:API error document), and no rate-limit headers are sent, so there’s
nothing to inspect ahead of time: back off and retry after a pause. The limit
is generous for interactive traffic; batch work (like registering hundreds of
tags) should pace itself rather than burst.
Pagination
Tags is the one collection with a list endpoint. It
takes page and page_size query parameters and reports where you are in a
meta object:
{
"data": ["..."],
"meta": { "total_count": 250, "page": 1, "page_size": 100 }
}
page is 1-based and defaults to 1. page_size defaults to 10, with a
maximum of 100; ask for more and you get 100 back. Walk the pages until you’ve
collected total_count records.
What the API does not have (yet)
Keeping this explicit so you don’t design around features that aren’t there:
- Tags are the only collection you can list. You fetch every other resource
one at a time by id:
GET /verifications/:id,GET /sessions/:id, and so on (see the “Retrieve a…” section on each resource’s page). Teams, verifications, sessions, keys, and webhooks have no list, search, or pagination. Lose one of those ids and you can’t find the resource again. - Webhooks push event notifications (e.g. when a tag is written) to a URL you register. They’re the one place the platform calls you. See Webhooks. Scan verdicts still come back in the response to the verification request you made, not via a callback.
- No sandbox or test mode for verifications. A verification only succeeds
against a tag that’s been written to a physical chip, one whose lifecycle has
reached
configured(see Tags). A freshly provisioned tag returns404until then, and there’s no API to simulate a scan or mint a pre-configured test tag. Exercising the verification path end to end means using real written hardware.
So store each id at the moment you receive it. You can re-fetch the rest from the retrieve endpoints and list tag ids back, but verdicts and session ids are yours to keep when they arrive.