Every request is authenticated with an API key belonging to a team. The key both identifies the team and scopes what it can see.
Obtaining a key
There are two ways in, depending on what you’re after:
Just want to build and try things? Sign up for a free sandbox team and mint a key right from the dashboard. Sandbox tags are virtual: they’re provisioned instantly with everything a physical tag would carry, and every scan is emulated in the browser — no hardware, no shipping, no waiting. You can build your entire integration against the sandbox, from the first API request to a full verification flow.
Ready for production? Production teams and their physical dynamic NFC tags are provisioned by us — contact TAGBASE to get your root team and its API keys. Your integration carries over unchanged: sandbox and production speak exactly the same API.
From there you’re self-service for tenants: use your root team’s key to create a subteam for each customer or tenant, and the response hands back that subteam’s own key. So you get your root team and key to begin with, and mint the rest yourself.
Key format
A key is two parts joined by a colon — a public key id and a secret:
key_abcdef0123456789:superstrongrandomsecret
You receive the full string once, when the key is minted (see Teams). The platform stores only a hash of the secret and can never show it to you again. Treat the whole string as a credential: store it somewhere secret, never commit it, never put it in a URL.
Sending the key
Pass the full key_id:secret string as a bearer token:
curl https://platform.tagbase.io/api/v1/tags \
-X POST \
-H "Authorization: Bearer key_abcdef0123456789:superstrongrandomsecret" \
-H "Content-Type: application/vnd.api+json" \
-d '{ "data": [ { "type": "tags", "attributes": { "protocol": "ntag_424_dna", "url": "https://zannatherapeutics.com/verify/lonafen/8a3f9c2b" } } ] }'
await fetch("https://platform.tagbase.io/api/v1/tags", {
method: "POST",
headers: {
"Authorization": "Bearer key_abcdef0123456789:superstrongrandomsecret",
"Content-Type": "application/vnd.api+json",
},
body: JSON.stringify({
data: [{ type: "tags", attributes: { protocol: "ntag_424_dna", url: "https://zannatherapeutics.com/verify/lonafen/8a3f9c2b" } }],
}),
});
$client->post("https://platform.tagbase.io/api/v1/tags", [
"headers" => [
"Authorization" => "Bearer key_abcdef0123456789:superstrongrandomsecret",
"Content-Type" => "application/vnd.api+json",
],
"json" => [
"data" => [["type" => "tags", "attributes" => ["protocol" => "ntag_424_dna", "url" => "https://zannatherapeutics.com/verify/lonafen/8a3f9c2b"]]],
],
]);
Req.post!("https://platform.tagbase.io/api/v1/tags",
headers: [
{"authorization", "Bearer key_abcdef0123456789:superstrongrandomsecret"},
{"content-type", "application/vnd.api+json"}
],
json: %{data: [%{type: "tags", attributes: %{protocol: "ntag_424_dna", url: "https://zannatherapeutics.com/verify/lonafen/8a3f9c2b"}}]}
)
Scope
A key sees only its own team’s resources. This is the isolation boundary
for subteams: a subteam’s key can create
and verify tags under that subteam and nothing else. Presenting a key for a
tag owned by a different team returns 404 Not Found — the platform doesn’t
distinguish “not yours” from “doesn’t exist”.
Auth errors
A missing, malformed, revoked, or unrecognized key returns 401:
{
"errors": [
{ "status": "401", "title": "Unauthorized" }
]
}
This covers every failure mode — no Authorization header, a header that isn’t
Bearer <key_id>:<secret>, a secret that doesn’t match, or a key that has been revoked.
Rotation
Each team can hold more than one active key, so you can rotate without downtime: provision the replacement, move your traffic over, then retire the old one.
Self-service key management endpoints (create / list / revoke a key on an existing team) are not part of the public API yet — today a key is minted together with its team. Until they ship, rotation on an existing team is handled by TAGBASE. Plan key storage so swapping the value is a config change on your side.