401 Unauthorized, it is almost always one of the causes below. Work through them in order.
1. The apex → www redirect (most common)
https://hitheo.ai 307-redirects to https://www.hitheo.ai. Most HTTP clients — including Node 18’s built-in fetch, curl without --location-trusted, and several language runtimes — strip the Authorization header on 3xx responses as a security precaution. That leaves the redirected request unauthenticated and the server correctly returns 401 invalid_api_key.
Fix: use the canonical www host directly. The SDK defaults to https://www.hitheo.ai as of @hitheo/sdk@0.2.0; if you pinned baseUrl manually, update it:
path=null start=null
2. Missing or malformed API key
Every request (exceptGET /api/v1/health) needs an Authorization: Bearer theo_sk_... header. Check:
- The header name is exactly
Authorization, notauthorization:with a stray colon orX-API-Key. - The token is prefixed with
theo_sk_(there is notheo_sk_test_variant — all keys are live). - No leading/trailing whitespace in the token — a trailing newline from a
.envfile is a common cause. - The key has not been revoked. Check the API Keys dashboard.
3. Wrong scope on the key
API keys carry scopes (completions, skills, tools, connectors, billing). A key without the completions scope returns 403 missing_scope, not 401. If you see a 403 specifically: re-issue the key with the scopes you need, or ask the key’s creator to update it.
4. Key expired (grace period ended)
When you rotate a key, the old one enters a grace window (configurable, default 60 min) then gets revoked. Requests against an expired key return401 key_expired. Rotate on the dashboard or via POST /api/v1/keys/rotate and swap the new token into your secret store.
5. Clock skew breaking HMAC on webhooks
Unrelated to API key auth, but sometimes reported as “401 on webhooks”: webhook signature verification uses theTheo-Signature header which is HMAC-SHA256 over the request body. If your server’s clock is more than 5 minutes out of sync, the signature won’t match. Run ntpdate -q pool.ntp.org to check.
Still stuck?
- Call
theo verify(orawait theo.verify()in the SDK) and share the output with support. - Include the
X-Request-Idresponse header in your ticket — every v1 response carries one, and successful responses also expose it asrequest_idin the JSON body. Support can look the request up in logs. - Check status.hitheo.ai for active incidents.
