This quickstart takes you from zero to a first verification using the live API. You’ll authenticate, provision a tag, and submit a scan. It covers the API surface and the verdicts you get back.
The whole public API is three POST endpoints under
https://platform.tagbase.io/api/v1. Every request carries your API key and the
JSON:API content type. Each call below is shown in curl, JavaScript (fetch),
PHP (Guzzle), and Elixir
(Req). Pick your language with the tabs.
1. Authenticate
You authenticate as a team with a bearer key. Don’t have one yet? Sign up for a free sandbox team and mint a key from the dashboard. Sandbox tags are virtual, so you can run this whole guide without any hardware.
Put the key in an environment variable so it never lands in your shell history:
export TAGBASE_API_KEY="key_abcdef0123456789:superstrongrandomsecret"
See Authentication for the key format and where keys come from.
2. Provision a tag
Create a tag under your team. You get back the tag’s id, which you’ll keep and reference when verifying scans.
curl https://platform.tagbase.io/api/v1/tags \
-X POST \
-H "Authorization: Bearer $TAGBASE_API_KEY" \
-H "Content-Type: application/vnd.api+json" \
-d '{ "data": [ { "type": "tags", "attributes": { "protocol": "ntag_424_dna", "url": "https://zannatherapeutics.com/verify/lonafen/8a3f9c2b" } } ] }'
const res = await fetch("https://platform.tagbase.io/api/v1/tags", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.TAGBASE_API_KEY}`,
"Content-Type": "application/vnd.api+json",
},
body: JSON.stringify({
data: [{ type: "tags", attributes: { protocol: "ntag_424_dna", url: "https://zannatherapeutics.com/verify/lonafen/8a3f9c2b" } }],
}),
});
const tag = (await res.json()).data[0];
$response = $client->post("https://platform.tagbase.io/api/v1/tags", [
"headers" => [
"Authorization" => "Bearer " . getenv("TAGBASE_API_KEY"),
"Content-Type" => "application/vnd.api+json",
],
"json" => [
"data" => [["type" => "tags", "attributes" => ["protocol" => "ntag_424_dna", "url" => "https://zannatherapeutics.com/verify/lonafen/8a3f9c2b"]]],
],
]);
$tag = json_decode((string) $response->getBody(), true)["data"][0];
%{"data" => [tag]} =
Req.post!("https://platform.tagbase.io/api/v1/tags",
headers: [
{"authorization", "Bearer #{System.fetch_env!("TAGBASE_API_KEY")}"},
{"content-type", "application/vnd.api+json"}
],
json: %{data: [%{type: "tags", attributes: %{protocol: "ntag_424_dna", url: "https://zannatherapeutics.com/verify/lonafen/8a3f9c2b"}}]}
).body
{
"data": [
{
"type": "tags",
"id": "tag_abcdef0123456789",
"attributes": { "url": "https://zannatherapeutics.com/verify/lonafen/8a3f9c2b" }
}
]
}
protocol selects the chip type. See Tags for the supported
values, the full request, and the tag lifecycle.
3. Run a verification
Once a tag has been written to a physical chip, a tap produces a URL with scan parameters in its query string. Forward those parameters to the platform and read back the verdict:
curl https://platform.tagbase.io/api/v1/tags/tag_abcdef0123456789/verifications \
-X POST \
-H "Authorization: Bearer $TAGBASE_API_KEY" \
-H "Content-Type: application/vnd.api+json" \
-d '{ "data": { "type": "verifications", "attributes": { "...": "...tap URL params..." } } }'
const res = await fetch(
`https://platform.tagbase.io/api/v1/tags/${tagId}/verifications`,
{
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.TAGBASE_API_KEY}`,
"Content-Type": "application/vnd.api+json",
},
body: JSON.stringify({ data: { type: "verifications", attributes } }),
},
);
const verification = await res.json();
$response = $client->post(
"https://platform.tagbase.io/api/v1/tags/{$tagId}/verifications",
[
"headers" => [
"Authorization" => "Bearer " . getenv("TAGBASE_API_KEY"),
"Content-Type" => "application/vnd.api+json",
],
"json" => ["data" => ["type" => "verifications", "attributes" => $attributes]],
],
);
$verification = json_decode((string) $response->getBody(), true);
verification =
Req.post!("https://platform.tagbase.io/api/v1/tags/#{tag_id}/verifications",
headers: [
{"authorization", "Bearer #{System.fetch_env!("TAGBASE_API_KEY")}"},
{"content-type", "application/vnd.api+json"}
],
json: %{data: %{type: "verifications", attributes: attributes}}
).body
{
"data": {
"type": "verifications",
"id": "vrf_abcdef0123456789",
"attributes": { "status": "pending", "inserted_at": "2026-06-08T12:34:56.123456Z" },
"relationships": {
"session": { "data": { "type": "sessions", "id": "ses_abcdef0123456789" } },
"tag": { "data": { "type": "tags", "id": "tag_abcdef0123456789" } }
}
}
}
The scan returns pending and opens a session. To finish
the check, forward another tap carrying that session id as the session
relationship:
curl https://platform.tagbase.io/api/v1/tags/tag_abcdef0123456789/verifications \
-X POST \
-H "Authorization: Bearer $TAGBASE_API_KEY" \
-H "Content-Type: application/vnd.api+json" \
-d '{ "data": { "type": "verifications", "attributes": { "...": "...tap URL params..." }, "relationships": { "session": { "data": { "type": "sessions", "id": "ses_abcdef0123456789" } } } } }'
const res = await fetch(
`https://platform.tagbase.io/api/v1/tags/${tagId}/verifications`,
{
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.TAGBASE_API_KEY}`,
"Content-Type": "application/vnd.api+json",
},
body: JSON.stringify({
data: {
type: "verifications",
attributes: tapParams,
relationships: { session: { data: { type: "sessions", id: sessionId } } },
},
}),
},
);
const verification = await res.json();
$response = $client->post(
"https://platform.tagbase.io/api/v1/tags/{$tagId}/verifications",
[
"headers" => [
"Authorization" => "Bearer " . getenv("TAGBASE_API_KEY"),
"Content-Type" => "application/vnd.api+json",
],
"json" => ["data" => [
"type" => "verifications",
"attributes" => $tapParams,
"relationships" => ["session" => ["data" => ["type" => "sessions", "id" => $sessionId]]],
]],
],
);
$verification = json_decode((string) $response->getBody(), true);
verification =
Req.post!("https://platform.tagbase.io/api/v1/tags/#{tag_id}/verifications",
headers: [
{"authorization", "Bearer #{System.fetch_env!("TAGBASE_API_KEY")}"},
{"content-type", "application/vnd.api+json"}
],
json: %{
data: %{
type: "verifications",
attributes: tap_params,
relationships: %{session: %{data: %{type: "sessions", id: session_id}}}
}
}
).body
{
"data": {
"type": "verifications",
"id": "vrf_MqiFaFwAs6U1pu5ALCxa5M",
"attributes": { "status": "valid", "inserted_at": "2026-06-08T12:34:58.654321Z" },
"relationships": {
"session": { "data": { "type": "sessions", "id": "ses_abcdef0123456789" } },
"tag": { "data": { "type": "tags", "id": "tag_abcdef0123456789" } }
}
}
}
status: "valid" means a genuine tag. That’s a complete verification.
Testing. Your own code is testable offline: since the verification
attributesare just the inbound query string passed through, you can unit-test your tap handler and session branching with any synthetic parameters. What you can’t fake is a realvalid/invalidverdict: that needs a tag TAGBASE has provisioned and written to a physical chip. There’s no public sandbox, so ask TAGBASE for test tags to exercise the flow end to end.
Next steps
- Resource model: how teams, tags, sessions, and verifications relate.
- Verifications: the full scan-to-verdict sequence.
- Building a solution: an end-to-end walkthrough that puts it all together.