TAGBASE

API Reference

Webhooks

Get notified when things change — signed, so you can trust them.

Most of the API is request/response: you call, you read the result. Webhooks are the exception — the one place the platform calls you. When something happens to one of your tags, we POST an event to a URL you’ve registered, so your integration learns about changes it didn’t initiate (most importantly, a tag being written to a chip out in the field).

Every delivery is signed. The signature is what makes a webhook trustworthy — so the rule is simple: if a request isn’t validly signed, reject it. Never act on an unsigned or mis-signed request.

Registering an endpoint

Webhook endpoints are configured per account in the back office. When you add one, a signing secret (whsec_…) is generated and shown once — store it; it’s the key you’ll verify deliveries with. An account can have several endpoints; each has its own secret and can be disabled or deleted.

An endpoint receives events only for its own account’s tags — register it on the account whose tags you want to hear about.

Events

Each event has a stable type. The catalog today:

Type When
tag.created A tag is provisioned under your account.
tag.configured A tag is written to a chip and reaches configured — ready to scan.
tag.configuration_failed A configuration attempt failed; the tag stays unconfigured.

More types may be added over time. Treat unknown types as a no-op rather than erroring, so new events never break your receiver.

Payload

Deliveries are POSTed as application/vnd.api+json, in the same JSON:API shape as the rest of the API. The affected resource is the top-level data; the event envelope (id, type, timestamp) lives in meta.event:

{
"data": {
"type": "tags",
"id": "tag_abcdef0123456789",
"attributes": {
"url": "https://zannatherapeutics.com/verify/lonafen/8a3f9c2b",
"status": "configured",
"protocol": "ntag_424_dna"
}
},
"meta": {
"event": {
"id": "evt_9f8c2b1a4d6e0f3a72",
"type": "tag.configured",
"created_at": "2026-06-28T12:30:00.000000Z"
}
}
}

The example above is a tag.configured delivery. Every event uses the same shape — only data.attributes.status and meta.event.type differ. A tag.created delivery looks like:

{
"data": {
"type": "tags",
"id": "tag_abcdef0123456789",
"attributes": {
"url": "https://zannatherapeutics.com/verify/lonafen/8a3f9c2b",
"status": "created",
"protocol": "ntag_424_dna"
}
},
"meta": {
"event": {
"id": "evt_1b2c3d4e5f6a7b8c90",
"type": "tag.created",
"created_at": "2026-06-28T12:00:00.000000Z"
}
}
}

Dispatch on meta.event.type. meta.event.id is the event’s unique id — use it to deduplicate (see Delivery & retries). For tag.configuration_failed, meta.event also carries a reason.

Verifying the signature

Every request carries a Tagbase-Hmac-SHA256 header — the Base64-encoded HMAC-SHA256 of the raw request body, keyed by your endpoint’s signing secret:

Tagbase-Hmac-SHA256: 5dPp1Lq8w4l8m2p0r3s5t7v9x1z3B5D7F9H1J3L5N7P=

To verify, recompute the HMAC over the raw request body (the exact bytes — don’t re-serialize the parsed JSON, or whitespace/key-order differences will break the check), Base64-encode it, and constant-time compare it to the header.

defmodule Receiver do
def valid?(raw_body, presented, secret) do
expected =
:hmac
|> :crypto.mac(:sha256, secret, raw_body)
|> Base.encode64()
Plug.Crypto.secure_compare(expected, presented)
end
end

The signature covers the body only; use the event id (next section) to ignore anything you’ve already processed.

Respond 2xx once you’ve accepted the event. Any other status (or a timeout) is treated as a failure and retried.

Delivery and retries

  • At-least-once. A delivery that doesn’t get a 2xx is retried with exponential backoff. The same event may therefore arrive more than once — dedupe on the event id and make handling idempotent.
  • Order isn’t guaranteed. Don’t assume events arrive in the order they occurred; use created_at if you need to reason about timing.
  • Disabling. Disable an endpoint in the back office to pause deliveries without losing its configuration; delete it to stop permanently.

Security checklist

  • Reject any request without a valid Tagbase-Hmac-SHA256 — this is the only proof the request came from us.
  • Compare signatures in constant time.
  • Dedupe on the event id so a replayed delivery is a no-op.
  • Keep your signing secret server-side; rotate it (delete + re-add the endpoint) if it’s ever exposed.
TAGBASE uses cookies to keep you signed in and protect against fraud. With your permission, we also measure how the site is used. Read our cookie policy for details.
Necessary
Analytics