# Redirects

_Category: REST API reference_

A redirect sends visitors from a path on your help center to another address, with a `301`. Use redirects to keep the links of a help center you moved from working, or to send readers on from a page you removed. These endpoints manage the same redirects as **Custom redirects** in Settings (see [Redirect old links to the right page](https://self.helpcenter.io/content/custom-redirects)).

**API keys only.** The redirect endpoints accept an `apikey` header and nothing else. An OAuth access token gets `403 Forbidden` with `api_key_required`: `Use an API key of this help center: an AI agent's key, or one its Owners and Admins create in Settings > API keys. No OAuth scope covers setting up a help center.`

## Endpoints

| Method and path | What it does | Scope |
| --- | --- | --- |
| `GET /v1/redirects` | List redirects | `content.read` |
| `POST /v1/redirects` | Create or re-point a redirect | `content.write` |
| `POST /v1/redirects/bulk` | Create or re-point up to 200 | `content.write` |
| `DELETE /v1/redirects/{redirectId}` | Delete a redirect | `content.write` |

Any API key can list. Creating and deleting take a **Read & write** key: a Read only key gets `403` with `insufficient_scope`. Every change counts as one write toward the 120 a minute, a bulk request included (see [Rate limits](https://developers.helpcenter.io/content/rate-limits)).

## The redirect object

| Field | Type | Description |
| --- | --- | --- |
| `id` | integer | The redirect's id. |
| `from` | string | The path visitors come from, always with a leading `/`. |
| `to` | string | Where they go. A page of this help center is stored as its path, query and fragment kept, even when you sent a full URL on the help center's own address, so the redirect keeps working when the help center's address changes. Another site's URL, which only a key allowed to redirect to other sites can send, is stored as you sent it. |
| `created_at`, `updated_at` | string | ISO 8601, with an offset. |

One path has at most one redirect. The redirects HelpCenter.io keeps for you when an article's address changes are separate: they are not listed here and can't be deleted here.

## How redirects are served

- **Only where nothing else answers.** When a visitor asks for a path no page of the help center uses, HelpCenter.io looks for a redirect from that path and answers `301 Moved Permanently` to its `to`. A page that exists always wins over a redirect from its path.
- **On the visitor's host.** A `to` path is completed with the host the visitor came in on: the help center's helpcenter.io address, its custom domain, or its subfolder address.
- **Without the visitor's query string.** A query on the visitor's request is not carried over to `to`.
- **Any title after an id.** A redirect from an old page address by its id, such as `/hc/en-us/articles/360001`, also forwards that address with a title after the id, such as `/hc/en-us/articles/360001-Change-your-plan`. This works for the page paths Zendesk, Intercom, Front, Help Scout Docs and HelpDocs use: `articles`, `categories`, `sections`, `collections`, `article`, `category` and `collection`.
- **Only on a published help center.** A help center that isn't published yet answers every visitor but its team with a page saying so, and serves its redirects to its team only until it is published.
- **Once the old host points here.** Redirects work on every address that reaches the help center, so you can add them before a move. Old links reach them once the host name they use belongs to this help center: when the old help center's domain becomes its custom domain (see [Custom domain](https://developers.helpcenter.io/content/custom-domain-api)).

## List redirects

The help center's redirects, newest first.

GET`/v1/redirects`

List the help center's redirects, newest first, 50 to a page by default.

| Parameter | Description |
| --- | --- |
| `search` | Only redirects whose `from` or `to` contains this text, up to 255 characters. A leading `/` is ignored. A path with accented or non-Latin characters is stored percent-encoded, so search for it in that form. |
| `page` | The page, from 1. 1 by default. |
| `limit` | Redirects per page, 1 to 100. 50 by default. |

`meta` has `page`, `per_page`, `total_pages` (at least 1) and `items_count`, the redirects that match on every page. A page past the last has an empty `redirects`. A value out of range answers `422` with `validation_error`.

## Create or re-point a redirect

Send the old path and where it should lead. When the path already has a redirect, it is re-pointed: the answer is `200 OK` with `"action": "updated"` and the redirect's own `id`, instead of `201 Created` with `"action": "created"`. Sending the same redirect again is safe.

POST`/v1/redirects`

Redirect a path to a page. A path that already has a redirect is re-pointed instead.

| Field | Type | Description |
| --- | --- | --- |
| `from` | string | Required. The old path on this help center, such as `/hc/en-us/articles/360001234-Change-your-plan`, up to 191 characters. |
| `to` | string | Required. A path starting with `/`, such as `/content/change-your-plan`, or an `http` or `https` URL, up to 191 characters: a page of this help center that exists now, or, for a key allowed to redirect to other sites, another site's URL. |

### The from path

`from` is matched against the path of the address a visitor opens, so it is a path alone. A leading or trailing `/` makes no difference, and letter case is kept. These are refused with `422` and `validation_error`, with the message under `errors.from`:

| A from that | Message |
| --- | --- |
| Starts with a scheme or `//` | `The from must be a path on this help center, like /hc/en-us/articles/123, without a scheme or host.` |
| Holds a `?` or `#` | `The from must be a path alone: a redirect matches the path of the address, never its query or fragment.` |
| Is the home page, `/` | `The from cannot be the home page: it always answers, so a redirect from it would never be used.` |
| Is longer than 191 characters once percent-encoded, as with many accented characters | `The from must not be longer than 191 characters once percent-encoded.` |
| Is longer than 191 characters as sent | `The from field must not be greater than 191 characters.` |

A path the help center answers before it looks for a redirect would never be used, so it is refused with `422` and `source_not_redirectable`:

- A one-segment path, such as `/pricing`: the help center reads the first segment as a language and ends on its home page.
- A path whose first segment is not one of the help center's languages and goes on with `content`, `category`, `categories` or `contacts`, such as `/docs/contacts`.
- An article address in the form the help center doesn't use: `/en/content/...` while it has one language, `/content/...` once it has several. In its own form, an article address is refused while an article answers it, including an address an article had before it was renamed.
- An address the help center answers itself, such as `/categories`, `/search`, `/sitemap.xml`, or an existing category's own address.

A path no page uses, such as `/hc/en-us/articles/360001-Billing` or `/kb/billing`, is fine. The message:

> A redirect from this path would never be used: the help center answers it before it looks for a redirect. It reads a path's first segment as a language, so a one-segment path, like /pricing, always ends on its home page, as does a path like /xx/content/... whose first segment is not one of its languages; an article address (/content/... or /en/content/...) first moves to the form its languages use; and an address it answers itself, like /categories, /search or an article or category that exists, is never redirected.

### The to address

`to` is a path starting with `/`, or an `http` or `https` URL with a host. Anything else, a `//` start, or a space, line break or backslash anywhere, answers `422` with `validation_error` and `The to must be a path starting with /, like /en/content/billing, or an http(s) URL, with no spaces, line breaks or backslashes.`

A URL on the help center's own address, its helpcenter.io address, its custom domain or its subfolder address, is stored as a path. Such a `to` must be a page of the help center that exists when you save: its home page, the category list, the contact page when the help center has one, a category readers can see, or a published, public article in one of its languages. Anything else, such as a draft, a private article or a path no page has, answers `422` with `target_not_found` and `The target is not a page of this help center right now. Point it at the current address of its home page, category list, contact page, a category, or a published public article in one of its languages.` Publish an article before you point a redirect at it.

**Other sites need a setting on the key.** A `to` on another site answers `422` with `external_target_forbidden`, unless one of the help center's Owners or Admins has ticked **Allow redirects to other sites** for the key in Settings, under **API keys**: when they create the key, or later with **Edit access** on it. Only a **Read & write** key can have it. With it, the key may point a redirect at another site, and such a `to` is stored as you sent it. Without it, the message is:

> This key's redirects stay on this help center: point it at one of its pages, as a path like /en/content/billing or a URL on its own address. A redirect to another site needs a Read & write key that one of the help center's Owners or Admins has ticked "Allow redirects to other sites" for in Settings > API keys, or Settings > Redirects in the dashboard.

Custom redirects in the dashboard can point at any address. HelpCenter.io doesn't check a target again after the redirect is saved.

### Chains and loops

A redirect whose `to` leads to a path that has a redirect of its own makes a chain. A chain may hold at most 3 redirects in a row, counting the ones that already lead into the redirect you save, and it may not come back to where it started:

- `too_many_hops`: `This redirect would send visitors through more than 3 redirects in a row. Point it at the final page instead.`
- `redirect_loop`: `This redirect would send visitors in a loop: its destination leads back to where it starts.`

Both answer `422`, and a refused re-point keeps the redirect's old `to`. Point each redirect at the final page. A `to` on your help center is always a page, never a path that only a redirect answers, so these two come from the redirects that already lead into `from`, such as ones made in the dashboard.

## Create many redirects

Send up to 200 redirects as `{"redirects": [...]}`, each with `from` and `to`, under the same rules as one. The answer is `207 Multi-Status` whenever the list itself is valid, even when every item failed: read each result.

POST`/v1/redirects/bulk`

Create or re-point up to 200 redirects. Each is saved or refused on its own, in order.

- Items are saved one at a time, in the order you sent them. One that fails doesn't stop or undo the others.
- `results` holds one entry per item, in order, with its `index` in your list and a `status`: `created` and `updated` come with the `redirect`, `error` with an `error` that has a `code` and a `message`. A `validation_error` also has `errors` by field; an item that isn't an object has `Each item must be an object with from and to.` under `errors.redirect`.
- `meta` counts `total`, `created`, `updated` and `failed`. Send again only the items that failed, after you fix them.
- A later item for the same path re-points the redirect an earlier one saved, so the last one wins.
- More than 200 items answers `422` with `Too many redirects in one request. The maximum is 200 per batch.` under `errors.redirects`, and saves nothing. An empty list, or a `redirects` that is not a JSON array, answers `422` too.

An import's redirect map comes in pages of 200 in exactly this shape: see "Get the redirect map" in [Imports](https://developers.helpcenter.io/content/imports-api).

## Delete a redirect

Delete one redirect by its id. Its path goes back to the help center's not-found page.

DELETE`/v1/redirects/{redirectId}`

Delete a redirect. The path goes back to the help center's not-found page.

An id that doesn't exist, was deleted already or belongs to another help center answers `404` with `Redirect not found.`

## With an AI agent's key

An AI agent's key manages redirects only once the help center it created is claimed and its owner has verified their email address and activated a plan. Until then, every call answers `403` with `capability_locked`, the conditions it still `requires` and a `next_step`. Its redirects follow the rules above, and it can never be allowed to redirect to other sites: another site answers `422` with `external_target_forbidden` and `An AI agent's redirects stay on this help center: point it at one of its pages, as a path like /en/content/billing or a URL on its own address. A redirect to another site is the help center owner's to add, in the dashboard.` While a migration request holds the help center, its changes answer `409` with `migration_in_progress`. The whole flow is in [Migrate a knowledge base as an AI agent](https://developers.helpcenter.io/content/migrate-a-knowledge-base-as-an-ai-agent).

## Errors

| Status | Code or message | When |
| --- | --- | --- |
| `401` | `"status": "unauthorized"` | No key, or one the API doesn't accept. |
| `403` | `api_key_required` | An OAuth access token. |
| `403` | `insufficient_scope` | A Read only key on a change. |
| `404` | `Redirect not found.` | Deleting a redirect that isn't there. |
| `422` | `validation_error` | A `from`, `to` or query value that breaks a rule above. `errors` names each field. |
| `422` | `source_not_redirectable` | A `from` the help center answers before it looks for a redirect. |
| `422` | `target_not_found` | A `to` on this help center that isn't one of its pages right now, such as a draft. |
| `422` | `external_target_forbidden` | A `to` on another site, from a key without **Allow redirects to other sites** or from an AI agent's key. |
| `422` | `too_many_hops`, `redirect_loop` | A chain longer than 3 redirects, or one that comes back to its start. |
| `429` | `{"message": "Too Many Attempts."}` | A rate limit. Wait `Retry-After` seconds. |

Refusals of a redirect, `validation_error` aside, have `"status": "error"`, a `code` and a `message`. In a bulk request, the same `code` and `message` sit in the item's `error`. See [Errors](https://developers.helpcenter.io/content/errors) for the shapes every endpoint shares.

## Related

- [Imports](https://developers.helpcenter.io/content/imports-api)
- [Custom domain](https://developers.helpcenter.io/content/custom-domain-api)
- [Migrate a knowledge base as an AI agent](https://developers.helpcenter.io/content/migrate-a-knowledge-base-as-an-ai-agent)
- [Articles](https://developers.helpcenter.io/content/articles-api)
