On this page
Redirects
Redirects belong to the site and are stored as one table. There is a read endpoint and a whole-map replace endpoint; there is no way to add or remove a single entry.
For matching rules and precedence, see Redirects.
All endpoints require an organization API key. :site accepts a slug or an id.
Read the table
GET /v1/sites/:site/redirects
curl -s -H "authorization: Bearer $DOCSARY_KEY" \
"$DOCSARY_API/v1/sites/acme/redirects"
{
"redirects": [
{ "source_path": "/beta", "target": "/", "code": 302 },
{ "source_path": "/old-guide/setup", "target": "/guide/install", "code": 301 },
{ "source_path": "/welcome", "target": "/", "code": 301 }
]
}
Ordered by source path.
Replace the table
PUT /v1/sites/:site/redirects
curl -s -X PUT "$DOCSARY_API/v1/sites/acme/redirects" \
-H "authorization: Bearer $DOCSARY_KEY" \
-H "content-type: application/json" \
-d '{
"redirects": [
{ "source": "/welcome", "target": "/" },
{ "source": "/old-guide/setup", "target": "/guide/install" },
{ "source": "/beta", "target": "/", "code": 302 },
{ "source": "/status", "target": "https://status.acme.com" }
]
}'
{ "count": 4 }
| Field | Type | Required | Notes |
|---|---|---|---|
redirects |
array | yes | The complete table. An empty array clears it. |
redirects[].source |
string | yes | Request path. Leading slashes are normalized to exactly one. |
redirects[].target |
string | yes | Site-relative path or absolute http/https URL. |
redirects[].code |
number | no | 302 for temporary; anything else, including omission, stores as 301. |
This is a replace, not a merge. Every entry not present in the request is deleted. Read the table first and send it back complete.
The write field is source; the read field is source_path. A read-modify-write loop must rename the key between the two calls.
The whole table is written in a single transaction — it applies completely or not at all — and takes effect immediately.
A table longer than your redirect ceiling is refused with 403 and quota: "redirects_per_site". Nothing is written, and the table you already had stays in place.
Target validation
Every target is checked before anything is written. One bad target fails the entire request; nothing is partially applied.
Accepted:
- A site-relative path beginning with a single
/ - An absolute
http:orhttps:URL
Rejected with 400:
{"error":"unsafe redirect target 'javascript:alert(1)' (allowed: http:, https:, or a /site-relative path)"}
That covers javascript:, data:, vbscript:, file:, protocol-relative //host/path, bare #fragment values, schemeless relative paths, and any value containing control characters. mailto: is accepted in site settings but not here — a redirect target becomes a Location header, which is a stricter position than an href.
The same check runs again when a redirect is served. A stored target that fails it is ignored entirely, and the request falls through to the normal page lookup or a 404, rather than emitting an unsafe Location header.
Other errors
| Response | Cause |
|---|---|
400 {"error":"redirects array required"} |
redirects missing or not an array |
400 {"error":"each redirect needs a string source and target"} |
An entry is null, or source/target is not a string |
404 {"error":"site not found"} |
The site slug or id does not resolve |