Create and manage custom storefront pages
/admin/api/pages
List all custom storefront pages. Optionally filter by search term.
/admin/api/pages
Create a new custom storefront page. A default Tera template is generated automatically in the active theme.
/admin/api/pages/{id}
Get a single page with its Tera template content from the active theme.
/admin/api/pages/{id}
Update a page's metadata and optionally its template content. All fields are optional; omitted fields are unchanged.
/admin/api/pages/{id}
Delete a custom page and its templates across all themes.
/admin/api/pages/{id}/translations
List every per-locale meta translation for a page, returned as a flat array of {field, locale, value} entries. Only meta_title and meta_description are stored here; the default-locale values live on the page row itself (see GET /admin/api/pages/{id}).
/admin/api/pages/{id}/translations
Replace all per-locale meta translations for a page with the supplied entries. The entire set is overwritten in one shot. Only meta_title and meta_description fields are accepted; any other field name returns 422. Default-locale values must be set on the page row via PUT /admin/api/pages/{id}, not here. Returns 204 No Content.
/admin/api/pages
List all custom storefront pages. Optionally filter by search term.
| Name | Type | Required | Description |
|---|---|---|---|
| search | string | optional | Case-insensitive search on title or slug |
[
{
"id": 1,
"slug": "about-us",
"title": "About Us",
"published": true,
"created_at": "2025-11-10T08:00:00Z",
"updated_at": "2025-11-15T12:30:00Z"
},
{
"id": 2,
"slug": "terms",
"title": "Terms & Conditions",
"published": true,
"created_at": "2025-11-10T08:05:00Z",
"updated_at": "2025-11-10T08:05:00Z"
},
{
"id": 3,
"slug": "faq",
"title": "FAQ",
"published": false,
"created_at": "2025-11-12T14:00:00Z",
"updated_at": "2025-11-12T14:00:00Z"
}
]
curl "https://yourshop.zeroshop.io/admin/api/pages?search=about" \
-H "Authorization: Bearer zspat_..."
import requests
resp = requests.get(
"https://yourshop.zeroshop.io/admin/api/pages",
params={"search": "about"},
headers={"Authorization": "Bearer zspat_..."}
)
pages = resp.json()
const resp = await fetch(
"https://yourshop.zeroshop.io/admin/api/pages?search=about",
{ headers: { "Authorization": "Bearer zspat_..." } }
);
const pages = await resp.json();
require "net/http"
require "json"
uri = URI("https://yourshop.zeroshop.io/admin/api/pages?search=about")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer zspat_..."
resp = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(resp.body)
/admin/api/pages
Create a new custom storefront page. A default Tera template is generated automatically in the active theme.
{
"slug": "about-us",
"title": "About Us",
"meta_title": "About Acme",
"meta_description": "Hand-curated goods, made by humans.",
"og_image_media_id": 14
}
{
"id": 1,
"slug": "about-us",
"title": "About Us",
"published": false,
"created_at": "2025-11-10T08:00:00Z",
"updated_at": "2025-11-10T08:00:00Z"
}
curl -X POST "https://yourshop.zeroshop.io/admin/api/pages" \
-H "Authorization: Bearer zspat_..." \
-H "Content-Type: application/json" \
-d '{"slug": "about-us", "title": "About Us"}'
import requests
resp = requests.post(
"https://yourshop.zeroshop.io/admin/api/pages",
headers={"Authorization": "Bearer zspat_..."},
json={"slug": "about-us", "title": "About Us"}
)
page = resp.json()
const resp = await fetch(
"https://yourshop.zeroshop.io/admin/api/pages",
{
method: "POST",
headers: {
"Authorization": "Bearer zspat_...",
"Content-Type": "application/json"
},
body: JSON.stringify({ slug: "about-us", title: "About Us" })
}
);
const page = await resp.json();
require "net/http"
require "json"
uri = URI("https://yourshop.zeroshop.io/admin/api/pages")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer zspat_..."
req.content_type = "application/json"
req.body = { slug: "about-us", title: "About Us" }.to_json
resp = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(resp.body)
/admin/api/pages/{id}
Get a single page with its Tera template content from the active theme.
| Name | Type | Required | Description |
|---|---|---|---|
| id | integer | required | Page ID |
{
"id": 1,
"slug": "about-us",
"title": "About Us",
"published": true,
"meta_title": "About Acme",
"meta_description": "Hand-curated goods, made by humans.",
"og_image_media_id": 14,
"og_image_url": "https://cdn.zeroshop.io/acme/og-about.webp",
"content": "{% extends \"base.html\" %}\n\n{% block content %}\n<main class=\"page-content\">\n <h1>{{ t.page_about_us_title | default(value='About Us') }}</h1>\n</main>\n{% endblock %}",
"created_at": "2025-11-10T08:00:00Z",
"updated_at": "2025-11-15T12:30:00Z"
}
curl "https://yourshop.zeroshop.io/admin/api/pages/1" \
-H "Authorization: Bearer zspat_..."
import requests
resp = requests.get(
"https://yourshop.zeroshop.io/admin/api/pages/1",
headers={"Authorization": "Bearer zspat_..."}
)
page = resp.json()
const resp = await fetch(
"https://yourshop.zeroshop.io/admin/api/pages/1",
{ headers: { "Authorization": "Bearer zspat_..." } }
);
const page = await resp.json();
require "net/http"
require "json"
uri = URI("https://yourshop.zeroshop.io/admin/api/pages/1")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer zspat_..."
resp = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(resp.body)
/admin/api/pages/{id}
Update a page's metadata and optionally its template content. All fields are optional; omitted fields are unchanged.
| Name | Type | Required | Description |
|---|---|---|---|
| id | integer | required | Page ID |
{
"title": "About Our Company",
"slug": "about-us",
"published": true,
"content": "{% extends \"base.html\" %}\n\n{% block content %}\n<main>\n <h1>About Our Company</h1>\n <p>We make great things.</p>\n</main>\n{% endblock %}"
}
{
"id": 1,
"slug": "about-us",
"title": "About Our Company",
"published": true,
"created_at": "2025-11-10T08:00:00Z",
"updated_at": "2025-11-20T14:00:00Z"
}
curl -X PUT "https://yourshop.zeroshop.io/admin/api/pages/1" \
-H "Authorization: Bearer zspat_..." \
-H "Content-Type: application/json" \
-d '{"title": "About Our Company", "published": true}'
import requests
resp = requests.put(
"https://yourshop.zeroshop.io/admin/api/pages/1",
headers={"Authorization": "Bearer zspat_..."},
json={"title": "About Our Company", "published": True}
)
page = resp.json()
const resp = await fetch(
"https://yourshop.zeroshop.io/admin/api/pages/1",
{
method: "PUT",
headers: {
"Authorization": "Bearer zspat_...",
"Content-Type": "application/json"
},
body: JSON.stringify({ title: "About Our Company", published: true })
}
);
const page = await resp.json();
require "net/http"
require "json"
uri = URI("https://yourshop.zeroshop.io/admin/api/pages/1")
req = Net::HTTP::Put.new(uri)
req["Authorization"] = "Bearer zspat_..."
req.content_type = "application/json"
req.body = { title: "About Our Company", published: true }.to_json
resp = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(resp.body)
/admin/api/pages/{id}
Delete a custom page and its templates across all themes.
| Name | Type | Required | Description |
|---|---|---|---|
| id | integer | required | Page ID |
curl -X DELETE "https://yourshop.zeroshop.io/admin/api/pages/1" \
-H "Authorization: Bearer zspat_..."
import requests
resp = requests.delete(
"https://yourshop.zeroshop.io/admin/api/pages/1",
headers={"Authorization": "Bearer zspat_..."}
)
assert resp.status_code == 204
const resp = await fetch(
"https://yourshop.zeroshop.io/admin/api/pages/1",
{
method: "DELETE",
headers: { "Authorization": "Bearer zspat_..." }
}
);
// 204 No Content
require "net/http"
uri = URI("https://yourshop.zeroshop.io/admin/api/pages/1")
req = Net::HTTP::Delete.new(uri)
req["Authorization"] = "Bearer zspat_..."
resp = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
# 204 No Content
/admin/api/pages/{id}/translations
List every per-locale meta translation for a page, returned as a flat array of {field, locale, value} entries. Only meta_title and meta_description are stored here; the default-locale values live on the page row itself (see GET /admin/api/pages/{id}).
| Name | Type | Required | Description |
|---|---|---|---|
| id | integer | required | Page ID |
[
{
"field": "meta_title",
"locale": "fr",
"value": "À propos d'Acme"
},
{
"field": "meta_description",
"locale": "fr",
"value": "Des produits soignés, faits par des humains."
},
{
"field": "meta_title",
"locale": "de",
"value": "Über Acme"
}
]
curl "https://yourshop.zeroshop.io/admin/api/pages/1/translations" \
-H "Authorization: Bearer zspat_..."
import requests
resp = requests.get(
"https://yourshop.zeroshop.io/admin/api/pages/1/translations",
headers={"Authorization": "Bearer zspat_..."}
)
translations = resp.json()
const resp = await fetch(
"https://yourshop.zeroshop.io/admin/api/pages/1/translations",
{ headers: { "Authorization": "Bearer zspat_..." } }
);
const translations = await resp.json();
require "net/http"
require "json"
uri = URI("https://yourshop.zeroshop.io/admin/api/pages/1/translations")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer zspat_..."
resp = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(resp.body)
/admin/api/pages/{id}/translations
Replace all per-locale meta translations for a page with the supplied entries. The entire set is overwritten in one shot. Only meta_title and meta_description fields are accepted; any other field name returns 422. Default-locale values must be set on the page row via PUT /admin/api/pages/{id}, not here. Returns 204 No Content.
| Name | Type | Required | Description |
|---|---|---|---|
| id | integer | required | Page ID |
{
"entries": [
{
"field": "meta_title",
"locale": "fr",
"value": "À propos d'Acme"
},
{
"field": "meta_description",
"locale": "fr",
"value": "Des produits soignés, faits par des humains."
}
]
}
curl -X PUT "https://yourshop.zeroshop.io/admin/api/pages/1/translations" \
-H "Authorization: Bearer zspat_..." \
-H "Content-Type: application/json" \
-d '{
"entries": [
{"field": "meta_title", "locale": "fr", "value": "À propos d'\''Acme"},
{"field": "meta_description", "locale": "fr", "value": "Des produits soignés, faits par des humains."}
]
}'
import requests
resp = requests.put(
"https://yourshop.zeroshop.io/admin/api/pages/1/translations",
headers={"Authorization": "Bearer zspat_..."},
json={
"entries": [
{"field": "meta_title", "locale": "fr", "value": "À propos d'Acme"},
{"field": "meta_description", "locale": "fr", "value": "Des produits soignés, faits par des humains."}
]
}
)
assert resp.status_code == 204
const resp = await fetch(
"https://yourshop.zeroshop.io/admin/api/pages/1/translations",
{
method: "PUT",
headers: {
"Authorization": "Bearer zspat_...",
"Content-Type": "application/json"
},
body: JSON.stringify({
entries: [
{ field: "meta_title", locale: "fr", value: "À propos d'Acme" },
{ field: "meta_description", locale: "fr", value: "Des produits soignés, faits par des humains." }
]
})
}
);
// 204 No Content
require "net/http"
require "json"
uri = URI("https://yourshop.zeroshop.io/admin/api/pages/1/translations")
req = Net::HTTP::Put.new(uri)
req["Authorization"] = "Bearer zspat_..."
req.content_type = "application/json"
req.body = {
entries: [
{ field: "meta_title", locale: "fr", value: "À propos d'Acme" },
{ field: "meta_description", locale: "fr", value: "Des produits soignés, faits par des humains." }
]
}.to_json
resp = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
# 204 No Content
We value your privacy
We use cookies for essential site functionality and, with your consent, analytics to understand how our platform is used. No personal data is shared with third parties. See our Privacy Policy for details.