RPC error HTTP 401: Missing or unknown API key

Last updated:

At a glance

Error details
PropertyValue
Applies toEvery network, HTTP and WebSocket
HTTP status401
error valuesunauthorized, trial_expired, subscription_expired
Response headersX-Triport-Error, X-Triport-Error-Class: tier
Reaches a nodeNo: decided at the gateway
WebSocket equivalentClose 4001, or 4030 for an ended trial
RetryableNo, not until the key or the plan changes

Cause

Authentication runs before anything else, so a 401 says nothing about your method or the chain. The most common version we see is a request that carried no key at all: a deployment missing its environment variable, a client that dropped the header on a redirect, or a WebSocket library that does not send custom headers. A key that is present but not recognised gets the same unauthorized body, so the response alone cannot tell you which of the two you hit.

Solution

  1. Read the error field first. unauthorized is about the credential; trial_expired and subscription_expired are about the account, and no change to your request will fix them.
  2. Print what actually leaves the process: the header name, and the first few characters of the value. An empty environment variable sends a blank key, which fails like no key at all.
  3. Send the key once, in one place: the x-token header, Authorization: Bearer <key>, the ?api-key= query parameter, or the path form https://triport.io/r/<API_KEY>/sol.
  4. Copy the key again from the console at /app/keys. A revoked key stays unauthorized however it is sent.
  5. On trial_expired or subscription_expired, choose or renew a plan. Keys are kept, and requests work again once the plan is active.

Which 401 is this?

All three share the status, so branch on the error string. The message text may be reworded; the code is part of the contract (error envelope).

401 variants
errorMeaningExtra fieldsWhat fixes it
unauthorizedNo key, or the key is unknown or revokedkeys_urlSend a valid key
trial_expiredThe 7-day trial endedexpired_at, upgrade_urlChoose a plan
subscription_expiredA paid plan lapsedexpired_at, renew_urlRenew

401 or 403?

A 401 is about who you are; a 403 is about what your plan may call. If you upgraded and still see 401, the plan is not the problem: the request is not carrying that account's key. If you see 403 tier_insufficient, authentication already worked, and looking at the key will not help.

Handle it in code

Treat a 401 as fatal for the worker and raise an alert. Retry loops are the worst response: they turn a configuration mistake into a steady stream of refused requests, and hide it behind your backoff.

const res = await fetch("https://triport.io/sol", { method: "POST", headers, body });
if (res.status === 401) {
  const { error } = await res.json();
  // unauthorized | trial_expired | subscription_expired: no retry helps
  throw new Error(`Triport auth refused: ${error}`);
}

Example

{
  "error": "unauthorized",
  "message": "Missing or unknown API key. Pass your key as ?api-key=<key> or the header Authorization: Bearer <key>. Create or copy a key at https://triport.io/app/keys",
  "keys_url": "https://triport.io/app/keys"
}

FAQ

Does a 401 count against my rate limit?
Without a valid key there is no plan to count it against, so backing off does not help. Fix the key: retrying the same request cannot succeed.
Are my keys deleted when a trial ends?
No. The trial_expired and subscription_expired messages both say your API keys are kept, and requests work again once a plan is active.
Why do I get 401 on WebSocket but not on HTTP?
Browsers cannot set custom headers on a WebSocket upgrade. Use the ?api-key= query parameter or the /r/<API_KEY>/ path form for that connection.