Error catalog

Every error response carries a stable code in error.code. Your client should branch on that one: search by code, filter by domain, or open any row for the full explanation and how to fix it.

52 documented codes

401AUTH_REQUIREDNo Bearer token was sent, and every call requires one.

We did not see a Bearer token in your request. Every API v6 call needs an API key in the Authorization header: without it we cannot tell which organization is calling.

How to fix it

Send Authorization: Bearer hk_YOUR_API_KEY on every request. Bearer is the only accepted scheme: the old styles that sent the key in the URL or the body are no longer accepted.

v5 code 40002#auth-required
401AUTH_INVALID_KEYThe API key is not recognized: wrong format, revoked or expired.

The API key you sent is not recognized. Three situations produce this code: a key with the wrong format, a key that was rotated or revoked, or a key past its expiration date.

How to fix it

Check that your client is using exactly the value shown when the key was created: keys are shown only once. If you rotated keys recently, make sure you are using the new one. If the key expired, issue a new one from the customer portal.

v5 code 40003#auth-invalid-key
401AUTH_COST_CENTER_DISABLEDThe cost center bound to the key is disabled.

Every API key is bound to a cost center so usage can be billed and reported correctly. The cost center for this key is disabled.

How to fix it

Re-enable the cost center from the customer portal, or issue a new key bound to an active one.

403AUTH_SERVICE_NOT_ALLOWEDThe key is not allowed to use the service behind this endpoint.

This key is not allowed to call the service behind this endpoint. Keys can be restricted to specific services so that a leaked or misused key can only do what it was allowed to.

How to fix it

Issue a new key with the right service enabled, or switch to one that already has it. Diagnostic endpoints under /api/v6/utilities/ are always available regardless of the key scope.

403AUTH_CAPABILITY_NOT_ALLOWEDThe key has the service, but not the capability this endpoint requires.

Some endpoints require, besides the service, an explicitly granted capability. In the URL shortener, creating, editing, deleting and restoring links requires urlshortener.write: a key with the urlshortener service and without that capability lists and reads links, statistics, the overview, the domains and the pricing, and gets this code when it tries to write. The account and organization endpoints require account or directory, and there the message is a different one: «This API key lacks the capability required for this endpoint. Capabilities are set when a key is issued and cannot be added later: use a key that has it, or issue a new one that includes it from the customer portal (Developers -> API keys).»

How to fix it

Capabilities are set when the key is created and cannot be added later: use a key that has it, or issue a new one that includes it and revoke the old one. Only an owner or an administrator of your organization can grant urlshortener.write: if your role cannot, ask one of them for a key that can create and edit links. Anyone who can create keys can grant account and directory, from the customer portal (Developers → API keys).

403AUTH_IP_NOT_ALLOWEDThe key only works from certain networks and your IP is not one of them.

This key has a list of allowed networks (single IPs or CIDR blocks, IPv4 or IPv6) and the request came from an IP that is in none of them. The key is valid and your organization is active: the only thing that does not match is the origin. The IP we saw you calling from comes in meta.clientIp of the same response.

How to fix it

Compare meta.clientIp with the key networks: if you call from behind a NAT, a proxy or a cloud, the egress IP is often not the one you expect. Call from an allowed network, or use a key that allows that IP. The list is set when the key is created, so changing it means issuing a new key.

400AUTH_SOURCE_IP_UNKNOWNReserved: kept only as a bridge from v5.

Reserved code, kept as a bridge for callers migrating from v5. API v6 does not use the source IP override mechanism that produced this error, so you should not see it on v6 traffic.

403ACCOUNT_NOT_ACTIVEYour organization is suspended or closed.

Your organization is not in an active state, usually because it was suspended or closed. While that lasts, every endpoint rejects calls, including the diagnostic ones.

How to fix it

Contact support to review the situation and restore the organization. The state cannot be lifted through the API.

403ACCOUNT_BLOCKEDA full block is active: every call is rejected.

Your organization is under a full block. Every call is rejected, including read-only ones, until the block is lifted.

How to fix it

Open a support ticket so we can review the case. Blocks are lifted once the underlying reason is resolved.

403ACCOUNT_READ_ONLYRead-only mode: only GET, HEAD and OPTIONS pass.

Your organization is in read-only mode. Requests that only read information (GET, HEAD, OPTIONS) keep working, but any call that creates or modifies data is rejected.

How to fix it

Until the block is lifted, pause any code that modifies data. Contact support to find out why it is active and what is needed to remove it.

403ACCOUNT_CONFIG_NOT_FOUNDWe could not load the organization behind your key.

Your API key authenticated correctly, but we could not load the organization it points to. This is a data problem on our side, not in your request.

How to fix it

Contact support and include the meta.requestId from the response: with that id we can trace the exact query that failed.

429RATE_DDOS_EXCEEDEDUnusually high volume from your source IP.

The per-source-IP guard fired, for one of two reasons. First: your IP produced an unusually high volume of requests in the last minute; legitimate integrations rarely get close to the limit. Second: 60 different API keys that do not authenticate arrived from your IP within one minute (keys that do not exist, or were revoked or expired), which is the footprint of someone guessing keys; the IP is then cut off for 15 minutes, and during that time it gets this 429 even if the key you send is valid. Both count IPv6 addresses by their /64 block: every address in the same /64 shares the limit and the cut-off.

How to fix it

Wait the seconds reported in Retry-After before retrying: in the cut-off for keys that do not authenticate, they are what is left of the 15 minutes. If your integration rotates between several keys, check that none is miscopied or revoked: every distinct invalid key counts. If you are a legitimate caller behind a shared IP and hit this consistently, contact support so we can review your traffic pattern.

v5 code 40001#rate-ddos-exceeded
429RATE_TPS_EXCEEDEDYou exceeded your organization quota for this endpoint.

You sent more requests to this endpoint than your organization quota allows in the current window. Each endpoint has its own quota: hitting the wall on one does not affect the others. The URL shortener overview and statistics also have a cost quota: each query spends units according to the days and sections it asks for, and the overview is computed at most twice at once per organization. And URL shortener bulk creation has a per-link limit (2,100 per minute per organization): a batch that does not fit is rejected whole, without creating or charging anything.

How to fix it

Pause your client for the seconds reported in Retry-After and start again. To avoid hitting it at all, read the RateLimit-Remaining and RateLimit-Reset headers present on every response and pace yourself. The usage limits guide covers it in detail.

v5 code 40001#rate-tps-exceeded
400IDEMPOTENCY_KEY_INVALIDThe idempotency key does not match the format.

The Idempotency-Key is too long or contains characters outside the allowed set. It accepts 1 to 255 characters from A-Z a-z 0-9 _ -. Nothing was done: nothing was created or charged.

How to fix it

Generate the key with a UUID v4 or any random value within the allowed set. See the idempotency guide.

409IDEMPOTENCY_IN_PROGRESSThe first request with that key is still in progress.

You sent the same idempotency key while the original request is still being processed. It does not run twice: the second one waits.

How to fix it

Wait the time reported in Retry-After and retry with the same key.

422IDEMPOTENCY_KEY_REUSEDThe key already exists, but with a different body.

You used an idempotency key your organization already used, with a different body. Nothing new was created or charged. It is almost always a client bug: one key identifies one operation, not several.

How to fix it

Use a new key for the new operation. Do not retry in a loop: the result will not change.

400URL_INVALIDThe destination URL is malformed.

The destination URL isn't a valid absolute URL (we couldn't parse a scheme and host), or it carries something that would make a reader see a destination other than the real one: a user or password before the domain (https://bank.com@other.com), a domain with accents, ñ or letters from other alphabets —also written as xn--—, or invisible characters. The message says which one it was.

How to fix it

Send an absolute http/https URL including the host, e.g. https://example.com/path, copied from the address bar, with no user before the domain and the domain written only with unaccented letters, digits and hyphens.

400URL_SCHEME_NOT_ALLOWEDOnly http/https destinations are allowed.

The destination uses a scheme we don't shorten (e.g. javascript:, data:, ftp:, file:).

How to fix it

Only http and https destinations are accepted.

400URL_TOO_LONGThe destination URL exceeds the maximum length.

The destination URL is longer than the 2048-character maximum.

How to fix it

Shorten the destination (drop unnecessary query parameters) so it fits within 2048 characters.

400URL_BLOCKED_HOSTThe destination host is private or reserved.

The destination points at a private/reserved address or an internal hostname (e.g. 127.0.0.1, 10.0.0.0/8, localhost), or it already is a short link: one of our domains or a shortener such as bit.ly, tinyurl.com or t.co. We block these so short links can't be used to reach internal endpoints or to hide the final destination behind a chain of hops.

How to fix it

Point the link at a publicly reachable host and at the final address you want to take people to.

400ALIAS_INVALIDThe custom alias doesn't match the allowed format.

The alias isn't 3-32 characters of lowercase letters, digits, hyphen or underscore.

How to fix it

Use a value matching ^[a-z0-9_-]{3,32}$, or omit alias to let the API generate a code.

409ALIAS_TAKENThe alias was already used on this domain.

Another link on the same domain already used that alias, whether it still exists or not: links deleted or expired 90 or more days ago are archived, and their code stays reserved forever. Codes are unique per domain and are never released.

How to fix it

Pick a different alias, use a different domain, or omit alias for an auto-generated code.

409ALIAS_RESERVEDThe alias is a reserved word.

The alias collides with a reserved word (e.g. api, stats, admin, docs), or —on a platform domain, which every organization shares— it looks like the name of a brand, a bank, a payment method or a public entity (bancolombia-seguro, pago-pse, dian). A single impersonation link on a shared domain makes security filters flag it, and then everyone’s links stop opening.

How to fix it

Choose a non-reserved alias. If the brand is yours, use your own verified domain.

400EXPIRES_AT_INVALIDexpiresAt must be a future ISO date-time.

expiresAt couldn't be parsed as an ISO-8601 date-time, or it is in the past.

How to fix it

Send a future ISO-8601 value, e.g. 2026-12-31T23:59:59Z, or omit it for a link that never expires.

400NOTHING_TO_UPDATENo updatable fields were sent.

The PATCH body had none of the updatable fields (longUrl, status, expiresAt).

How to fix it

Send at least one updatable field.

500CODE_GENERATION_FAILEDCould not allocate a unique code.

We couldn't allocate a unique random code after several attempts. This is rare.

How to fix it

Retry the request. If it persists, quote the meta.requestId in a support ticket.

400STATS_RANGE_INVALIDThe stats range or granularity is invalid.

The statistics window is not valid: from or to are not YYYY-MM-DD, from is after to, the window exceeds the cap (366 days with day, 31 days with hour) or the granularity is neither day nor hour.

How to fix it

Send from and to as YYYY-MM-DD days (Colombia time) with from on or before to, and keep hourly queries within 31 days.

400DOMAIN_NOT_ALLOWEDThe chosen domain isn't available to your account.

The domain you requested isn't a platform-global domain nor one owned by your account (or your account has no default domain configured).

How to fix it

Call GET /api/v6/urlshortener/domains to see the domains you can use, or omit domain to use your default.

409DOMAIN_NOT_VERIFIEDYour domain is not verified yet.

The domain is one of your organization’s own, but it is not verified yet. Until it is, it cannot be used to create links: it might not point to the platform yet, and the link would be born broken.

How to fix it

Use another domain from GET /api/v6/urlshortener/domains, or omit domain to use the default. To verify yours, contact us.

409ALIAS_RETIREDThe alias belongs to a link of yours that you deleted.

You already used that alias on a link you deleted. The code of a deleted link is never released —a short URL you already handed out cannot reappear with another destination—, so no other link can be created with it. But that link can be restored for 90 days after you deleted it; after that it is archived and the alias returns ALIAS_TAKEN.

How to fix it

Restore it with POST /api/v6/urlshortener/links/{domain}/{code}/restore and, if needed, change its destination. Or pick another alias.

400URLSHORTENER_BATCH_TOO_LARGEThe batch has more links than one request accepts.

Bulk creation accepts up to 1,000 links per request, and the one you sent has more. None was created and nothing was charged.

How to fix it

Split the batch into requests of 1,000 links or fewer, each with its own Idempotency-Key.

402URLSHORTENER_INSUFFICIENT_FUNDSYour organization has no balance to create the link.

Creating a link is charged on creation, and your organization has no available balance to cover it (or it reached a spend limit it configured itself). The link was not created. In bulk creation the price of all the valid links is reserved at once: without balance for all of them, none is created.

How to fix it

Top up your balance or review your spend limits in the customer portal and try again. Check the price with GET /api/v6/urlshortener/pricing.

402URLSHORTENER_SPEND_LIMIT_REACHEDA spend limit your organization configured was reached.

Your organization has balance, but creating this link would exceed a spend limit it configured itself: for the whole organization, for the URL shortener, for a cost center, for a user or for this API key. The link was not created.

How to fix it

Review your organization's spend limits in the customer portal and adjust the one that applies. Topping up does not fix it: the limit is yours, not your balance's.

409URLSHORTENER_NOT_PRICEDThe URL shortener has no price configured for your currency.

We found no URL shortener price for your organization’s currency, so we cannot charge the operation and we do not perform it. It is a configuration problem on our side, not in your request.

How to fix it

Do not retry: the response will not change on its own. Contact support and include the meta.requestId from the response.

409URLSHORTENER_CURRENCY_UNDEFINEDYour organization does not have a currency yet.

An organization’s currency is chosen once, when its setup is finished, and everything it consumes is charged in it. Until it has one, we cannot price the operation and we do not perform it.

How to fix it

Finish setting up the organization in the customer portal and try again.

404COUNTRY_NOT_FOUNDNo country exists with that code.

The code sent to GET /api/v6/tools/countries/{code} does not match any country in the catalog. The parameter expects a two-letter ISO 3166-1 alpha-2 code.

How to fix it

Check the code against GET /api/v6/tools/countries, which returns the full catalog.

503INFRA_DB_CONNECTION_ERRORAn internal service was momentarily unreachable.

One of the services we depend on was temporarily unreachable when your request arrived. It is a problem on our side, not with what you sent.

How to fix it

Retry after the seconds reported in Retry-After, ideally with a small random jitter. If it lasts more than a couple of minutes, check the status page or open a support ticket.

503INFRA_CACHE_UNAVAILABLEAn internal component is temporarily unavailable.

Same scenario as the previous one, with a different internal piece. The failure is transient and recovers on our side with no change on yours.

How to fix it

Retry after Retry-After with a small random jitter. If you keep seeing it, the status page usually has the bigger picture.

504INFRA_DB_QUERY_TIMEOUTAn internal query took too long and was cut off.

An internal query took longer than we allow before returning a response, so we cut it off instead of leaving your client waiting indefinitely. In the URL shortener (the link list, its statistics and the overview) the database itself cuts it, and it happens with very large queries for organizations with many links: a long range, many sections or a broad search.

How to fix it

If the query asks for a lot, narrow it —fewer days, fewer sections, more filters—: repeating it as is does not help. If it happens with small queries, open a support ticket quoting the meta.requestId.

400VALIDATION_INVALID_PARAMETERA parameter or a body field is not valid.

A query parameter has a value that does not exist, or the body carries an unknown field, a wrong type or is not JSON. The message says which one and what it accepts.

How to fix it

Fix the parameter or field named in the message, using the values documented in the endpoint reference.

400BAD_REQUESTWe could not parse your request.

We could not parse your request. The most common causes are malformed JSON in the body, a Content-Type header that does not match the payload, or a path with invalid characters.

How to fix it

Inspect the raw payload your client is sending. Validating the JSON locally before the call usually reveals the problem right away.

401UNAUTHORIZEDGeneric 401: prefer the `AUTH_*` codes when present.

Generic 401 returned before the authentication logic could give you a more specific code. When you see it, the request was rejected in a layer earlier than API key validation.

403FORBIDDENGeneric 403: fallback when no `ACCOUNT_*` applies.

The request authenticated correctly but is not allowed to access this resource. When the cause is a known organization state you get an ACCOUNT_* code instead; this generic one is the fallback.

404NOT_FOUNDThe path you requested does not exist in this API.

The path you requested does not exist in this API. We also return this code, deliberately indistinguishable from a normal 404, for requests whose Host header is not on our allowed list, so the API surface cannot be probed by guessing hostnames.

How to fix it

Compare the path against the reference. Watch out for trailing slashes and casing: paths are case-sensitive.

405METHOD_NOT_ALLOWEDThe path exists but does not accept this HTTP method.

The path exists but does not accept the HTTP method you used, for example a POST to a read-only endpoint. The response includes an Allow header with the valid methods.

429TOO_MANY_REQUESTSGeneric 429: prefer the `RATE_*` codes when present.

Generic 429. When the limit you hit is one of ours we return RATE_DDOS_EXCEEDED or RATE_TPS_EXCEEDED instead, which carry more context. Branch on the specific ones when present.

500INTERNAL_SERVER_ERRORSomething went wrong on our side.

Something went unexpectedly wrong while processing your request. The full trace is stored against the same meta.requestId we returned, so we can investigate without you having to reproduce the failure.

How to fix it

Retry once with a small backoff. If you keep seeing it, open a support ticket and include the requestId.

503SERVICE_UNAVAILABLEGeneric 503: a service we depend on is not responding.

A service we depend on is not responding, or is handling too many requests at once: each service has its own cap of simultaneous requests at the edge, and when it is full the request is rejected immediately with Retry-After instead of waiting. The INFRA_* codes are more specific when we can name the failing component; this is the fallback when we cannot.

How to fix it

Retry after Retry-After with a small random jitter. If it happens persistently, check the status page.

504GATEWAY_TIMEOUTGeneric 504: a service we depend on took too long.

A service we depend on took too long to respond. When the slow component was the database you will see INFRA_DB_QUERY_TIMEOUT instead, which means the same with more context.

How to fix it

Retry with exponential backoff. Several in a row usually indicate a wider degradation: open a support ticket with the requestId.

How the numeric code is structured

Every error.legacyCode is a six-digit number: the first two identify the error domain and the remaining four the specific condition. It only appears when a bridge from v5 exists; codes introduced in v6 omit it.

PrefixDomainCovers
01xxxxAUTHAPI key, tokens, source IP, identity.
02xxxxRATEPer-IP guard and per-endpoint quotas.
03xxxxVALIDATIONRequest shape, JSON, parameters, types.
04xxxxACCOUNTOrganization information, state, blocks.
05xxxxBILLINGBalance, payments, currency, exchange rate.
06xxxxSMSSMS service specific errors.
07xxxxWHATSAPPWhatsApp service.
08xxxxEMAILEmail service.
09xxxxCALLBLASTINGCalling service.
10xxxxURL_SHORTENERURL shortener.
11xxxxUTILITIESPing and other diagnostic endpoints.
12xxxxTOOLSCatalogs and utilities.
90xxxxINFRAInternal dependencies and availability.
99xxxxINTERNALUnhandled exceptions.

Prefixes 13xxxx through 89xxxx are reserved for future services, to avoid renumbering.