curl --request POST \
--url https://app.base44.com/api/apps/{app_id}/url-redirects \
--header 'Content-Type: application/json' \
--header 'api_key: <api-key>' \
--data '
{
"source_path": "/old-pricing",
"target_path": "/pricing",
"match_type": "single"
}
'import requests
url = "https://app.base44.com/api/apps/{app_id}/url-redirects"
payload = {
"source_path": "/old-pricing",
"target_path": "/pricing",
"match_type": "single"
}
headers = {
"api_key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {api_key: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({source_path: '/old-pricing', target_path: '/pricing', match_type: 'single'})
};
fetch('https://app.base44.com/api/apps/{app_id}/url-redirects', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.base44.com/api/apps/{app_id}/url-redirects",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'source_path' => '/old-pricing',
'target_path' => '/pricing',
'match_type' => 'single'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"api_key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.base44.com/api/apps/{app_id}/url-redirects"
payload := strings.NewReader("{\n \"source_path\": \"/old-pricing\",\n \"target_path\": \"/pricing\",\n \"match_type\": \"single\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("api_key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.base44.com/api/apps/{app_id}/url-redirects")
.header("api_key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"source_path\": \"/old-pricing\",\n \"target_path\": \"/pricing\",\n \"match_type\": \"single\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/apps/{app_id}/url-redirects")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["api_key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"source_path\": \"/old-pricing\",\n \"target_path\": \"/pricing\",\n \"match_type\": \"single\"\n}"
response = http.request(request)
puts response.read_body{
"id": "68c2d1e5f3b8a4216e9b5583",
"source_path": "/old-pricing",
"target_path": "/pricing",
"match_type": "single"
}Create URL redirect
Adds a 301 redirect to the app’s published site.
Use it to keep old links working after you rename or remove a page. The rule takes effect on the published site once this call returns.
Base44 also tries to drop any cached copy of the source path so visitors get the redirect straight away, but that step can fail without failing the call. When it does, the source URL keeps serving its cached page with a 200 instead of the new 301 until the cache refreshes on its own. A prefix rule affects child paths too, and each one has its own cached copy. Base44 drops the copies it knows about, and any it doesn’t keep serving their old page until that cache expires.
A rule is rejected when:
- Its source is a path the redirect layer never sees, which covers
/sitemap.xml,/robots.txt,/favicon.ico,/manifest.json,/link_preview.png,/llms.txt,/.well-known/*, and the app’s auth paths - Its source and target are the same
- It’s a
prefixrule starting at/ - It overlaps another rule, so a
prefixrule at/docsblocks asinglerule at/docs/intro - It would make a visitor follow two redirects in a row, which happens when an internal target matches another rule’s source
An app can hold up to 50 rules, and the 51st is rejected.
URL redirects are part of custom domains, so creating, updating and deleting one needs a workspace whose plan includes them. Listing them doesn’t.
curl --request POST \
--url https://app.base44.com/api/apps/{app_id}/url-redirects \
--header 'Content-Type: application/json' \
--header 'api_key: <api-key>' \
--data '
{
"source_path": "/old-pricing",
"target_path": "/pricing",
"match_type": "single"
}
'import requests
url = "https://app.base44.com/api/apps/{app_id}/url-redirects"
payload = {
"source_path": "/old-pricing",
"target_path": "/pricing",
"match_type": "single"
}
headers = {
"api_key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {api_key: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({source_path: '/old-pricing', target_path: '/pricing', match_type: 'single'})
};
fetch('https://app.base44.com/api/apps/{app_id}/url-redirects', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.base44.com/api/apps/{app_id}/url-redirects",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'source_path' => '/old-pricing',
'target_path' => '/pricing',
'match_type' => 'single'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"api_key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.base44.com/api/apps/{app_id}/url-redirects"
payload := strings.NewReader("{\n \"source_path\": \"/old-pricing\",\n \"target_path\": \"/pricing\",\n \"match_type\": \"single\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("api_key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.base44.com/api/apps/{app_id}/url-redirects")
.header("api_key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"source_path\": \"/old-pricing\",\n \"target_path\": \"/pricing\",\n \"match_type\": \"single\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/apps/{app_id}/url-redirects")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["api_key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"source_path\": \"/old-pricing\",\n \"target_path\": \"/pricing\",\n \"match_type\": \"single\"\n}"
response = http.request(request)
puts response.read_body{
"id": "68c2d1e5f3b8a4216e9b5583",
"source_path": "/old-pricing",
"target_path": "/pricing",
"match_type": "single"
}Authorizations
Personal API key.
Path Parameters
ID of the app whose URL redirects you want to work with.
Body
Path visitors request, starting with / and carrying no query string or fragment. Base44 strips a trailing slash and decodes percent-escapes before storing it, so /old/ and /%6Fld are the same rule.
1 - 512"/old-pricing"
Where to send the visitor. Either an internal path starting with /, normalized the same way as source_path, or an absolute https:// URL on another site, kept as you sent it. Send it without a query string or fragment. The visitor's own query string is carried over to the destination, so /old?utm=x lands on /new?utm=x.
1 - 512"/pricing"
Use single to redirect that exact path, or prefix to redirect it and everything under it, keeping the remainder of the path. Defaults to single, so omitting it on an update turns an existing prefix rule into a single one and its child paths stop redirecting.
single, prefix "single"
Response
The redirect that was created.
One 301 redirect rule on the app's published site.
ID of the redirect. Pass it as redirect_id to Update URL redirect and Delete URL redirect.
"68c2d1e5f3b8a4216e9b5583"
The path visitors request, normalized with no trailing slash and percent-escapes decoded.
"/old-pricing"
Where the visitor is sent. Either an internal path, normalized the same way as source_path, or an absolute https:// URL on another site, kept exactly as you sent it.
"/pricing"
How the rule matches. A single rule redirects that exact path, and a prefix rule redirects it and everything under it, keeping the remainder of the path.
single, prefix "single"
Was this page helpful?