Hosted page and template API reference
Manage hosted-page content, publication, SEO settings, and media, and read the public template catalog.
These endpoints manage hosted pages and their supporting assets. They are dashboard API routes under /api, not public REST v1 resources. Page publication is separate from publishing the project's snippet configuration.
Authentication, paths, and rate limits
Project routes use https://api.rightmessage.com/api/{teamPid}. They require an authenticated session or API token, project membership or administrator access, and account and project suspension checks. Browser-session writes require CSRF protection. Bearer tokens are accepted, but these routes do not enforce v1 read, write, or publish abilities.
Template catalog routes use https://api.rightmessage.com/api/template-catalog and do not require authentication. Neither the page/media route group nor the catalog declares a numeric per-route throttle. Do not apply v1's 120-per-minute declaration to them. Global infrastructure limits can still apply.
Send Accept: application/json and JSON bodies with Content-Type: application/json. The illustrative requests below use bearer placeholders for clarity and were not executed.
Page operations
A pid is the encoded numeric-string page identifier returned by the API, not its slug or database ID. Item routes require digits. The project scopes every lookup.
| Method and path | Body or query | Success |
|---|---|---|
GET /pages | Optional status, type, and search query strings | 200, bare array of page resources |
POST /pages | Create fields below | 201, bare page resource |
GET /pages/{pid} | None | 200, bare page resource |
PUT /pages/{pid} | Update fields below | 200, bare page resource |
DELETE /pages/{pid} | None | 204, empty body |
POST /pages/{pid}/publish | None | 200, bare page resource |
POST /pages/{pid}/unpublish | None | 200, bare page resource |
POST /pages/{pid}/duplicate | Optional name | 201, bare new page resource |
POST /pages/{pid}/archive | None | 200, bare page resource |
Page resources are not wrapped in data. This differs from public v1 resource serializers. There is no PATCH page route.
The list orders newest first and has no pagination. Status and type are exact filters without request validation. Search uses SQL LIKE matching on name or slug. Duplicate accepts an unvalidated name, defaulting to the original name plus (Copy). It copies content, settings, flow, and metadata, and creates a draft with a unique slug. A slug collision on duplication falls back to a random slug.
Page create and update fields
| Field | Create | Update |
|---|---|---|
name | Required string, maximum 255 | Optional string, maximum 255 |
type | Required: lead_gen or quiz_funnel | Optional, same values |
slug | Nullable string, maximum 255; generated when empty | Optional non-null string, maximum 255 |
status | Nullable: draft, published, archived; defaults draft | Optional non-null value from the same set |
content | Nullable object | Optional nullable object |
settings | Nullable object | Optional nullable object |
Slugs must match ^[a-z0-9]+(?:-[a-z0-9]+)*$ and be unique within the project. Duplicate slugs return 422. A new page also creates the project's hosted-page slug if one is missing.
Both create and update declare these nullable content fields:
| Keys | Type and limit |
|---|---|
flowId | String, maximum 255. No existence check in the request. |
template | centered or split |
logo, headline, authorSignature | Strings, maximum 500 |
description | String, maximum 2,000 |
backgroundColor, accentColor | Strings, maximum 50 |
Update additionally declares these nullable content fields:
| Keys | Type and limit |
|---|---|
containerBackgroundColor, borderColor, textColor | Strings, maximum 50 |
borderRadius, borderWidth, buttonBorderRadius, answerButtonSpacing, formFieldSpacing | Strings, maximum 20 |
flowHeadlineFontSize, flowDescriptionFontSize, answerButtonFontSize, formFieldFontSize, submitButtonFontSize, headlineFontSize, descriptionFontSize | Strings, maximum 20 |
containerShadow | Boolean |
font | String, maximum 100 |
lineHeight | Number, 1 through 3 |
customCss | String, maximum 10,000 |
The parent objects are not closed key allowlists. The tables describe fields with explicit validation, not a guarantee that unknown nested keys are rejected. Supplied content and settings replace their stored objects rather than merging nested values.
SEO settings available through the API
Both requests accept nullable settings.metaTitle, string up to 255; settings.metaDescription, string up to 1,000; and settings.ogImage and settings.favicon, strings up to 500. These fields are API-accessible even when a dashboard editor does not expose a control.
Use those nested settings for SEO. The response also includes legacy top-level meta_title, meta_description, og_image, favicon, and flow_id columns. The request validators do not declare those top-level write fields. The page renderer uses content.flowId for the flow, and the page's meta-title helper uses settings.metaTitle with the page name as fallback.
Page response and immediate effects
Each page resource contains pid, slug, name, type, status, content, settings, the legacy columns above, views_count, conversions_count, percentage conversion_rate, full_url, nullable published_at, nullable last_published_at, created_at, updated_at, and booleans is_published, is_draft, is_archived, is_lead_gen, and is_quiz_funnel.
Item and lifecycle responses also include team with pid, name, slug, and pages_base_url. Other responses include it when project details are loaded. The URL uses the project slug, configured pages domain, and page slug. Conversion rate is conversions divided by views times 100, rounded to two decimals, or zero with no views.
Publish changes status to published, initializes published_at if absent, and updates last_published_at. Unpublish changes status to draft; archive changes it to archived. Both retain publication timestamps. Only published, non-deleted pages on nonsuspended projects render publicly.
There is no second page-content draft hidden behind a published page. Updating a published page changes its stored live content directly. Supplying status: "published" on create or update changes visibility without calling the publish method, so it does not have that method's timestamp behavior. The embedded flow is extracted from stable project configuration, not the current flow draft.
Page publishing does not validate that content.flowId exists or has usable dependencies. Hosted pages include questions referenced through segment-group IDs. Direct raq_ question references, including input questions with no group, are not included in that question list. A successful page publish is not proof that such a flow will render correctly. Check the public page before sharing it.
Illustrative page requests and shared response
These operations use the fictional WorkForge project. The same page resource contract covers list, create, read, update, publish, unpublish, duplicate, and archive. Status fields reflect the requested lifecycle operation. The duplicate response has a new pid and draft status.
Illustrative page-resource excerpt. The list returns an array of these objects; DELETE returns no body. The field list above defines the full resource.
Project media operations
These routes share the page authentication boundary and affect storage immediately, without publishing.
| Method and path | Request | Success |
|---|---|---|
GET /files | No parameters | 200, bare array of filenames |
POST /files | Multipart file, required file, maximum 2,048 KiB; jpeg, jpg, png, gif, or svg | 200, filename string, not a JSON resource envelope |
DELETE /files/{hash} | Filename from storage, no body | 200, {"message":"File deleted successfully"} |
POST /files/from-url | JSON url, required valid URL, maximum 2,048 characters | 200, {"hashName":"..."} |
URL import downloads the image with a 30-second timeout. Allowed response media types are image/jpeg, image/png, image/gif, image/svg+xml, and image/webp. Content must not exceed 2,097,152 bytes. WebP is accepted by URL import but not by the multipart validator.
Imported filenames use the content MD5 plus a normalized extension. Existing identical content at that project path returns existing: true with hashName. New uploads omit existing. Import stores the file, not a page reference. A hosted page logo can use usf://FILENAME, which the page renderer resolves to the project's media URL.
Deduplication is by content hash plus extension, not content alone. Identical bytes imported with different accepted extensions can create separate files. Deleting media does not inspect or rewrite page references, and a missing filename still receives the same success message. Remove references before deleting an image that a published page uses.
Illustrative calls, not executed:
Corresponding illustrative bodies, in request order:
URL import returns 422 with error for Invalid image type. Allowed: jpeg, png, gif, svg, webp, Image too large (max 2MB), or Failed to download image from URL. Request validation instead returns the normal message and errors envelope. Other storage or network failures can return 500.
Public template catalog
The catalog describes available templates without installing them or changing a project.
GET /api/template-catalog returns 200 with data, an array of entries, and meta: {last_page:1}. It returns the entire catalog, ordered by configured order then title. There are no supported query filters or pagination inputs.
GET /api/template-catalog/{idOrSlug} accepts an exact template UUID or filename-derived slug. It returns 200 with data, one entry, or 404 when absent. Neither endpoint installs a template or changes a project.
Each entry contains id, slug, title, nullable description, nullable preview_image with a url, template_type: {value:string}, boolean hide_preview, nullable impact_statement, nullable best_for, array template_categories, nullable structural_archetype, boolean is_skeleton, and payload_json: {value:string}. The payload value is serialized JSON text, not an already-decoded object. Template type defaults flow; boolean flags default false.
Illustrative requests and response excerpts, not executed. The slug and entry are fictional:
The item response uses {"data":ENTRY} with the same full entry schema. The abbreviated example payload is not an installable template.
Common failures
Authenticated routes return 401 for missing authentication and 403 for denied access or suspension. Invalid page or media inputs return 422. Missing, deleted, wrong-project, or nondigit page IDs return 404. Browser-session writes can fail CSRF protection. Maintenance returns 503 for these API routes, including the otherwise-public catalog. These operations do not create v1 configuration drafts or snapshots, or send v1 draft-update notifications.