Developer API
Developer APIResourcesTroubleshooting

Troubleshooting

Common symptoms, their causes, and how to fix them

This page collects the issues integrators hit most often, organized by symptom. Each entry names the likely cause and the concrete fix. For the full list of error codes, see Error Handling.

Authentication & Access

Every request returns 401 unauthorized

The key is missing or not being sent the way the API expects. Send it as a bearer token: Authorization: Bearer tl_live_xxx. A common mistake is sending the key as a raw header value without the Bearer prefix, or pasting a key that was truncated on copy. Confirm the key still exists and is enabled in Settings → API Keys.

A request returns 403 forbidden even though the key is valid

The key is authenticated but lacks the scope the endpoint requires. The message names the missing scope. For example, creating a geofence needs manage:geofences, not just read:geofences. Scopes are fixed at key creation—mint a new key with the scope you need rather than trying to edit an existing one. See Authentication.

403 api_disabled on the very first call

The API is not enabled for the workspace yet. Creating any API key from Settings → API Keys enables it automatically—create one and retry.

401 vs 403 vs 404 — which is which?

They are distinct on purpose. 401 means we could not authenticate the key at all. 403 means the key is valid but not allowed to do this (wrong scope, or API not enabled). 404 means the resource does not exist or is not visible to this key—which is also what a tag outside the key's allow-list looks like (see below).

Tags & Scoped Keys

A tag I can see in the dashboard returns 404 from the API

The key is probably restricted to a tag allow-list that does not include this tag. A scoped key only ever sees the tags it was granted; any other tag id resolves to not_found, by design, so a restricted key cannot probe for tags it shouldn't know about. Either use a key whose scope covers the tag, or add the tag to the key's allow-list when you create a replacement key.

GET /v1/tags returns fewer tags than I expect

If the key is scoped to an allow-list, the list endpoint only returns the tags in that list (and skips any that no longer exist). This is expected. A key scoped to all tags returns the full workspace set, page by page.

Pagination & Cursors

A page came back with fewer items than my limit, but there's a nextCursor

This is correct behavior, not a bug. Pages can be under-full when the window straddles gaps (for example, a scoped tag that was deleted). The rule is simple: keep paging until nextCursor is null. Never stop early just because a page looks short.

An empty page came back, but nextCursor is still set

Same rule applies. An empty page does not mean you have reached the end—only a null nextCursor means that. Pass the returned cursor to the next request and continue.

My pagination restarted from the beginning unexpectedly

Cursors are opaque. If you hand-build, truncate, or otherwise corrupt a cursor, it is treated as absent and the list starts over from the first page rather than erroring. Always pass back the exact nextCursor string you received, unmodified, and do not try to parse or construct cursors yourself.

Webhooks

POST /v1/webhooks rejects my URL with invalid_url

Delivery URLs must be public HTTPS endpoints. We reject anything that is not https://, that embeds credentials (https://user:pass@host), or that points at a private, loopback, or link-local address. Use a publicly reachable HTTPS URL. For local development, put a tunnel in front of your server and register the tunnel's public URL.

My webhook endpoint never receives events

Work through these in order:

  • Confirm the subscription is active.
  • Check the events filter. If you subscribed to specific event types, only those are delivered; omit events to receive all of them.
  • Make sure your endpoint answers with a 2xx quickly. Non-2xx responses are treated as failures and retried with backoff.
  • Inspect the delivery history endpoint to see attempts, status codes, and retry state.

My signature check always fails

Compute the HMAC-SHA256 over the exact raw request body bytes, using your subscription's signing secret, and compare against the hex digest in X-TagLogger-Signature (formatted sha256=<hex>). The two most common mistakes are hashing a re-serialized JSON object (which changes the bytes) instead of the raw body, and comparing against the whole header value without stripping the sha256= prefix. Use a constant-time comparison. See Webhook Delivery.

I received the same event more than once

Delivery is at-least-once, so retries can occasionally deliver a duplicate. Make your handler idempotent: dedupe on the event id and treat a repeat as a no-op.

Rate Limits & Share Links

I'm getting 429 rate_limited

You exceeded the per-key limit. Honor the Retry-After header and back off. If you hit it routinely, poll the fleet delta endpoint instead of looping per-tag, and widen your interval. The polling guide shows patterns that stay well under the limit.

A share link stopped working

Share links can carry an expiry. Once a link is past its expiration it no longer resolves. Create a fresh link with manage:share-links when you need continued access.

Still Stuck?

Capture the request method and path, the response status, and the error.code and message from the body, then reach out through your normal support channel. Never share an API key or signing secret in a support message—reference the key by its visible prefix instead.