1. Overview
The Eyivo REST API is the programmatic control plane for structured websites. It uses HTTPS and JSON, is versioned under /api/v1, and applies the same ownership, plan limits, validation, and audit boundaries as the dashboard.
JSON and multipart
Accept: application/json. Content-Type: application/json; file: multipart/form-data.
User-owned
Every site operation is constrained to sites owned by the authenticated user.
Draft-first
Save a draft, inspect its readiness, then publish that item and language when the user wants the change live. Publishing does not publish unrelated drafts.
Versioning: breaking contract changes require a new base version. Additive fields may appear in existing JSON objects, so clients should ignore fields they do not recognize.
2. Quick start
- 1Create a token.
Sign in to the App, open API tokens, choose the smallest necessary scopes, and copy the
ey_secret when it is shown. - 2List your sites.
Confirm authentication and obtain the numeric site ID used by nested endpoints.
- 3Send idempotent writes.
Give every POST, PATCH, and DELETE request a unique
Idempotency-Key.
curl --request GET \
--url https://app.eyivo.com/api/v1/sites \
--header 'Accept: application/json' \
--header 'Authorization: Bearer ey_your_token'
A successful collection response wraps records in data:
{
"data": [
{
"id": 42,
"platform_subdomain_key": "7kr9mp2x",
"template_key": "corporate-01",
"status": "active",
"native_locale": "en",
"english_enabled": false,
"content_version": 1,
"search_visibility": "platform_noindex"
}
]
}
3. Authentication
Every /api/v1 request requires a bearer token:
Authorization: Bearer <token>
Personal API tokens
Best for scripts, server integrations, and direct account automation. Tokens begin with ey_, are stored hashed, and are displayed only once.
- Create and revoke them at https://app.eyivo.com/tokens.
- Choose one or more scopes.
- Optionally restrict a token to one owned site.
- Unexpired tokens remain valid until revoked.
OAuth 2.0 + PKCE
Best for applications acting on behalf of Eyivo users. Use Authorization Code with PKCE; do not use password or implicit grants.
- Authorization:
https://app.eyivo.com/oauth/authorize - Token and refresh:
https://app.eyivo.com/oauth/token - Access tokens expire after 2 hours.
- Refresh tokens expire after 1 year.
PKCE authorization flow
- Create a high-entropy
code_verifierand its SHA-256code_challenge. - Redirect the user to
/oauth/authorizewithresponse_type=code, the publicclient_id, exact registeredredirect_uri, requested space-separatedscope,state,code_challenge, andcode_challenge_method=S256. - Verify
stateafter the redirect, then exchange the returned authorization code at/oauth/tokenusing the originalcode_verifier. - Store access and refresh tokens securely. Refresh before expiration and replace rotated credentials atomically.
4. Scopes
Requests need the exact scope shown below. A personal token containing * passes all scope checks; applications should still request the smallest practical scope set.
| Scope | REST capability |
|---|---|
sites:read | List owned sites and retrieve one site. |
sites:write | Create sites with an account-wide token. |
content:read | Read content, schemas, menus, forms and settings. |
content:write | Create, edit, delete and explicitly publish content; run declared panel actions. |
media:read | GET /sites/{site}/media, GET /sites/{site}/media/stock-photos |
media:write | POST /sites/{site}/media, DELETE /sites/{site}/media/{media} |
domains:read | List site domains. |
domains:write | Request, activate, and select custom domains. |
REST requires the endpoint scope, not mcp:use. Media endpoints use media:read or media:write.
5. Idempotent writes
Send an Idempotency-Key with JSON mutations so the same request can be retried safely. Multipart media uploads and upload-ticket issuance are excluded; verify the transfer result before retrying.
Idempotency-Key: site-42-page-create-01J7M6Y8RRF3ZE6WB4Z0Y9XJXC
| Situation | Result |
|---|---|
| First request | The operation runs and its status code and JSON body are stored. |
| Same key, method, path, query parameters, and body | The stored response is replayed without repeating the operation. |
| Same key with a different request | 422 — key reuse with a different payload is rejected. |
| Same key while the first request is running | 409 — the request is already in progress. |
| No key | The request still runs, but safe retry behavior is not available. |
A request match includes the HTTP method, request path, and exact raw request body. Reuse the original serialized JSON when retrying.
6. Responses and resource shapes
Content operations return a data envelope. MCP supplies structuredContent plus a JSON text mirror; REST returns JSON. Read each output schema for IDs, publication readiness and pagination. Blogs, media and submissions paginate; follow their returned page information.
Single resource
{ "data": { "id": 42, "status": "active" } }Collection
{ "data": [{ "id": 42 }, { "id": 43 }] }Collection shapes vary. Follow each endpoint’s documented pagination metadata; do not assume every result is a flat or unpaginated list.
7. Sites
/sitesList owned sites · sites:read
/sitesCreate a site · sites:write
/sites/{site}Retrieve one owned site · sites:read
Create a site
curl --request POST \
--url https://app.eyivo.com/api/v1/sites \
--header 'Accept: application/json' \
--header 'Authorization: Bearer ey_your_token' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: site-create-01J7M6Y8' \
--data '{
"display_name": "Northstar Studio",
"template_key": "corporate-01",
"native_locale": "en"
}'
| Field | Rules |
|---|---|
display_name | Required string, maximum 200 characters. |
template_key | Required built-in template key. The complete current enum is in OpenAPI. |
native_locale | Required supported locale code. |
Response: 201 Created with a site resource. Provisioning can initially report a non-active status while placement completes. Site and plan limits return 422.
Site resource fields: id, platform_subdomain_key, template_key, status, native_locale, english_enabled, content_version, search_visibility, optional runtime, and timestamps.
8. Pages
/sites/{site}/pagesList pages · content:read
/sites/{site}/pagesCreate a draft · content:write
/sites/{site}/pages/{page}Retrieve a page · content:read
/sites/{site}/pages/{page}Update a draft · content:write
/sites/{site}/pages/{page}Delete a page · content:write
/sites/{site}/pages/{page}/publicationcontent:write · locale
Create a page
{
"type": "page",
"sort_order": 300,
"locale": "en",
"slug": "our-story",
"title": "Our story",
"body": "<p>How the company began.</p>",
"meta_title": "Our story | Northstar Studio",
"meta_description": "Learn about Northstar Studio."
}
| Field | Create/update rules |
|---|---|
type | Use page for ordinary content. Supported types and theme component schemas are available through site capabilities. |
sort_order | Optional nullable integer from 0 to 9999; create defaults to 0. |
locale | Create: required. Update: required when changing title, slug, or body. |
slug | Create: required. ASCII letters, digits, dashes, and underscores; maximum 160. |
title | Create: required string; maximum 200. |
body | Nullable string; maximum 50,000. |
meta_title | Nullable string; maximum 200. |
meta_description | Nullable string; maximum 500. |
Create response: 201. Update response: 200. Delete response: 204 with no body.
Save a draft, inspect its readiness, then publish that item and language when the user wants the change live. Publishing does not publish unrelated drafts.
9. Catalog items
/sites/{site}/catalog-itemsList items · content:read
/sites/{site}/catalog-itemsCreate a draft item · content:write
/sites/{site}/catalog-items/{catalogItem}Retrieve an item · content:read
/sites/{site}/catalog-items/{catalogItem}Delete an item · content:write
/sites/{site}/catalog-items/{catalogItem}content:write · locale, slug, title
/sites/{site}/catalog-items/{catalogItem}/publicationcontent:write · locale
Create a catalog item
{
"sort_order": 100,
"locale": "en",
"slug": "strategy-advisory",
"title": "Strategy advisory",
"summary": "Focused advice for consequential decisions.",
"body": "Talk to our senior advisory team."
}
| Field | Rules |
|---|---|
sort_order | Optional nullable integer from 0 to 9999; defaults to 0. |
locale | Required supported locale code. |
slug | Required ASCII slug; maximum 160. |
title | Required string; maximum 200. |
summary | Nullable string; maximum 1,000. |
body | Nullable string; maximum 50,000. |
Save a draft, inspect its readiness, then publish that item and language when the user wants the change live. Publishing does not publish unrelated drafts.
These are examples by content family. Discover the complete input/output schemas and required scopes through tools/list or the canonical OpenAPI document. REST paths below are relative to /api/v1/sites/{site}.
Site and capability discovery
GET /capabilities · GET /readiness
Pages, components and internal links
/pages · /pages/{page_id}/components · POST /pages/{page_id}/publication
Products, galleries and videos
/catalog-items · /galleries · /videos · POST /{collection}/{id}/publication
Blog drafts, categories and scheduling
/blog/posts · /blog/categories · /blog/links · /blog/posts/{post_id}/publication · /blog/posts/{post_id}/schedule
Main navigation and additional menus
/menus · /menus/destinations · /menu-assignments/{type}/{target_id} · /menus/{menu_key}/publication
Media library and stock photos
/media · /media/transfers · /media/stock-photos
Forms, submissions and plugin panels
/forms · /submissions · /plugins · /plugins/panel · /plugins/panel/actions
Company, theme, languages and integrations
/company · /theme · /theme/settings · /locales · /integrations · /demo · /demo/drafts · /cache/reset
Capabilities and authoring contract
Select a site, then read its capabilities: enabled languages, theme fields, component schemas, menu locations and installed plugin panels. Read the content-workflow resource before writing. Clients without resources can use get-site-capabilities-tool. Follow tools/list nextCursor until discovery is complete.
GET /api/v1/sites/{site}/capabilities
GET /api/v1/sites/{site}/pages/{page_id}/components/schema?locale=en
Use the requested locale and current version or revision. Blog saves replace the complete document and cancel its schedule. Menu edits use integer revision; content assignments use a separate menu_revision hash. Preserve entries in replacement arrays. On a conflict, read again and merge the intended edits before retrying.
Main navigation stays automatic. Additional menus have their own drafts and publication. Saving content stages its menu assignments; publishing that item applies them without publishing other menu drafts. Publishing a menu never publishes its linked content. A page source_hash rejects translations based on outdated source text.
Page body and theme richtext fields accept sanitized HTML. Use list-page-links-tool for stable /__eyivo/pages/{id} links, resolved in the visitor’s language when published. Blog posts use structured document JSON with same-site media_id and page/post link targets, not body_html.
{
"type": "doc",
"content": [{
"type": "paragraph",
"content": [{ "type": "text", "text": "Our story" }]
}]
}
Image and video uploads
upload-media-tool opens a file picker in MCP Apps clients such as Claude. ChatGPT can also supply its host attachment reference; a Claude chat attachment is not an OpenAI file reference. Never invent a download URL or send base64 file bytes as tool arguments.
Codex and Claude Code can call prepare-media-upload-tool, then send multipart file bytes to the returned upload_url with its one-use upload_token within five minutes. Keep that token secret. REST clients can upload with their own media:write bearer. Only a completed upload returns the media ID to use in content; issuing a ticket does not upload a file.
POST /api/v1/sites/{site}/media
Authorization: Bearer <media:write token>
Content-Type: multipart/form-data; boundary=<generated by HTTP client>
file: <original file bytes>
10. Custom domains
/sites/{site}/domainsList domains · domains:read
/sites/{site}/domainsRequest a domain · domains:write
/sites/{site}/domains/{domain}/activateActivate after verification · domains:write
/sites/{site}/domains/{domain}/primarySelect the primary hostname · domains:write
Request a domain
curl --request POST \
--url https://app.eyivo.com/api/v1/sites/42/domains \
--header 'Accept: application/json' \
--header 'Authorization: Bearer ey_your_token' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: domain-request-example-com' \
--data '{"domain":"www.example.com"}'
A pending domain resource includes verification_token. Add the required DNS ownership record using the value returned for your domain, wait for DNS propagation, then call the activation endpoint. Once active, call the primary endpoint if it should become the canonical public hostname.
Domain resource fields: id, domain, site_id, is_primary, status, conditional verification_token, and timestamps. Custom-domain plan limits and domain validation failures return 422.
11. Errors
| Status | Meaning | Typical action |
|---|---|---|
401 | Missing, invalid, expired, or revoked bearer token. | Replace or refresh the credential. |
403 | Missing scope, a site restriction, or a site write by a non-owner. | Use an authorized scope/site; do not retry unchanged. |
404 | Resource is missing or is not owned by the user. | Verify IDs and ownership. |
409 | A revision/version conflict requires a fresh read and merge. Do not retry a stale payload unchanged. | Read the selected language draft and current revision before editing. |
422 | Validation, plan limit, domain rule, or idempotency mismatch. | Read errors or message, correct the request, and use a new key if its body changes. |
5xx | Unexpected platform or upstream failure. | Retry safe reads; retry writes only with the same idempotency key. |
Validation response
{
"message": "The title field is required.",
"errors": {
"title": ["The title field is required."]
}
}
Authentication or authorization response
{
"message": "Missing required scope [content:write]."
}
12. Current boundaries and integration checklist
REST v1 intentionally exposes a focused subset of Eyivo. The absence of an endpoint is a contract boundary, not permission to call internal APIs.
Available
- Site listing, inspection, and provisioning
- Pages, components and internal links
- Manage pages, components, blogs, products, galleries, videos, menus, media, forms and site settings through scoped content tools and REST endpoints.
- Domain listing, request, activation, and primary selection
Not exposed in REST v1
- Billing and subscription changes
- Account password and token management
- Direct storage access and Internal APIs
- Arbitrary theme code installation or execution
Before production
- Use HTTPS and validate the expected
app.eyivo.comhost. - Keep bearer tokens server-side and redact authorization headers from logs.
- Request only necessary scopes and prefer site-restricted personal tokens when possible.
- Use a unique idempotency key for every logical write and preserve it across retries.
- Treat
401,403,404, and422as non-transient unless the underlying request or credential changes. - Use exponential backoff with jitter for transient failures and respect any future
Retry-Afterheader. - Collection shapes vary. Follow each endpoint’s documented pagination metadata; do not assume every result is a flat or unpaginated list.