# Your first verification

Authenticate, provision a tag, and run your first verification.


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](https://docs.guzzlephp.org)), and Elixir
([Req](https://hexdocs.pm/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](/dashboard/sign-in) and mint a key from the
dashboard. Its 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:

```bash
export TAGBASE_API_KEY="key_VfUttEQbh1ceW7jtwV66rF:superstrongrandomsecret"
```

See [Authentication](/docs/api/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.

<!-- tabs -->
```bash cURL
curl https://platform.tagbase.io/api/v1/tags \
  -X POST \
  -H "Authorization: Bearer $TAGBASE_API_KEY" \
  -H "Content-Type: application/vnd.api+json" \
  -H "Accept: application/vnd.api+json" \
  -d '{ "data": [ { "type": "tags", "attributes": { "protocol": "ntag_424_dna", "url": "https://zanna.example/verify/lonafen/8a3f9c2b" } } ] }'
```
```js
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",
    "Accept": "application/vnd.api+json",
  },
  body: JSON.stringify({
    data: [{ type: "tags", attributes: { protocol: "ntag_424_dna", url: "https://zanna.example/verify/lonafen/8a3f9c2b" } }],
  }),
});
const tag = (await res.json()).data[0];
```
```php
$response = $client->post("https://platform.tagbase.io/api/v1/tags", [
    "headers" => [
        "Authorization" => "Bearer " . getenv("TAGBASE_API_KEY"),
        "Content-Type"  => "application/vnd.api+json",
        "Accept"        => "application/vnd.api+json",
    ],
    "json" => [
        "data" => [["type" => "tags", "attributes" => ["protocol" => "ntag_424_dna", "url" => "https://zanna.example/verify/lonafen/8a3f9c2b"]]],
    ],
]);
$tag = json_decode((string) $response->getBody(), true)["data"][0];
```
```elixir
%{"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"},
      {"accept", "application/vnd.api+json"}
    ],
    json: %{data: [%{type: "tags", attributes: %{protocol: "ntag_424_dna", url: "https://zanna.example/verify/lonafen/8a3f9c2b"}}]}
  ).body
```
<!-- /tabs -->

```json
{
  "data": [
    {
      "type": "tags",
      "id": "tag_XPg3Zq7gEExNezDWWW7Zzo",
      "attributes": { "url": "https://zanna.example/verify/lonafen/8a3f9c2b" }
    }
  ]
}
```

`protocol` selects the chip type. See [Tags](/docs/api/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. On a sandbox team, you
[emulate the tap](/docs/getting-started/sandbox) from the dashboard instead.
Forward those parameters to the platform and read back the verdict:

<!-- tabs -->
```bash cURL
curl https://platform.tagbase.io/api/v1/tags/tag_XPg3Zq7gEExNezDWWW7Zzo/verifications \
  -X POST \
  -H "Authorization: Bearer $TAGBASE_API_KEY" \
  -H "Content-Type: application/vnd.api+json" \
  -H "Accept: application/vnd.api+json" \
  -d '{ "data": { "type": "verifications", "attributes": { "...": "...tap URL params..." } } }'
```
```js
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",
      "Accept": "application/vnd.api+json",
    },
    body: JSON.stringify({ data: { type: "verifications", attributes } }),
  },
);
const verification = await res.json();
```
```php
$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",
            "Accept"        => "application/vnd.api+json",
        ],
        "json" => ["data" => ["type" => "verifications", "attributes" => $attributes]],
    ],
);
$verification = json_decode((string) $response->getBody(), true);
```
```elixir
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"},
      {"accept", "application/vnd.api+json"}
    ],
    json: %{data: %{type: "verifications", attributes: attributes}}
  ).body
```
<!-- /tabs -->

```json
{
  "data": {
    "type": "verifications",
    "id": "vrf_AoNAiMHGpUb2fxAB93eM4R",
    "attributes": { "status": "pending", "inserted_at": "2026-06-08T12:34:56.123456Z" },
    "relationships": {
      "session": { "data": { "type": "sessions", "id": "ses_2cZuH8Cb7L5fseae6FzNDc" } },
      "tag":     { "data": { "type": "tags",     "id": "tag_XPg3Zq7gEExNezDWWW7Zzo" } }
    }
  }
}
```

The scan returns `pending` and opens a [session](/docs/api/sessions). To finish
the check, forward another tap carrying that session id as the `session`
relationship:

<!-- tabs -->
```bash cURL
curl https://platform.tagbase.io/api/v1/tags/tag_XPg3Zq7gEExNezDWWW7Zzo/verifications \
  -X POST \
  -H "Authorization: Bearer $TAGBASE_API_KEY" \
  -H "Content-Type: application/vnd.api+json" \
  -H "Accept: application/vnd.api+json" \
  -d '{ "data": { "type": "verifications", "attributes": { "...": "...tap URL params..." }, "relationships": { "session": { "data": { "type": "sessions", "id": "ses_2cZuH8Cb7L5fseae6FzNDc" } } } } }'
```
```js
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",
      "Accept": "application/vnd.api+json",
    },
    body: JSON.stringify({
      data: {
        type: "verifications",
        attributes: tapParams,
        relationships: { session: { data: { type: "sessions", id: sessionId } } },
      },
    }),
  },
);
const verification = await res.json();
```
```php
$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",
            "Accept"        => "application/vnd.api+json",
        ],
        "json" => ["data" => [
            "type" => "verifications",
            "attributes" => $tapParams,
            "relationships" => ["session" => ["data" => ["type" => "sessions", "id" => $sessionId]]],
        ]],
    ],
);
$verification = json_decode((string) $response->getBody(), true);
```
```elixir
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"},
      {"accept", "application/vnd.api+json"}
    ],
    json: %{
      data: %{
        type: "verifications",
        attributes: tap_params,
        relationships: %{session: %{data: %{type: "sessions", id: session_id}}}
      }
    }
  ).body
```
<!-- /tabs -->

```json
{
  "data": {
    "type": "verifications",
    "id": "vrf_XBUGqbnP8BzQ7yJAmyrtaw",
    "attributes": { "status": "valid", "inserted_at": "2026-06-08T12:34:58.654321Z" },
    "relationships": {
      "session": { "data": { "type": "sessions", "id": "ses_2cZuH8Cb7L5fseae6FzNDc" } },
      "tag":     { "data": { "type": "tags",     "id": "tag_XPg3Zq7gEExNezDWWW7Zzo" } }
    }
  }
}
```

`status: "valid"` means a genuine tag. That's a complete verification.

> **Testing.** The verification `attributes` are the inbound query string passed
> through, so you can unit-test your tap handler and session branching with
> synthetic parameters. For real `valid` and `invalid` verdicts, emulate scans
> on a [sandbox](/docs/getting-started/sandbox) tag.

## Next steps

- [Resource model](/docs/overview/resource-model): how teams, tags, sessions,
  and verifications relate.
- [Verifications](/docs/api/verifications): the full scan-to-verdict sequence.
- [Building a solution](/docs/guides/building-a-solution): an end-to-end
  walkthrough that puts it all together.
- [Best practices](/docs/guides/best-practices): the checks to run on the
  page a customer reloads.

