GenuineAIGenuineAI
For usersFor developers
  • Overview
  • Guides
  • How-to
  • API reference
Documentation
  • Quickstart
  • Authentication
  • Errors
  • Rate limits
  • API reference
Platform
  • Platform overview
  • Solutions
  • Pricing
  • Sign in
Company
  • Status
  • Trust Center
  • Contact
  • Terms of Service
  • Privacy Policy

© 2026 GenuineAI Ventures LLC

support@genuinehq.com
Overview
Core
Media library
AI
Documents
Knowledge
Creative
Website & content
    Website content
      List sitesgetCreate a sitepostGet a sitegetDelete a sitedeleteUpdate a sitepatchSet the review flowputApprove all pagespostList site commentsgetSet the writing agentpatchGenerate all pagespostGenerate all briefspostGenerate the site briefpostGenerate the structurepostGet generation defaultsgetImport pagespostAdd a pagepostGet a pagegetDelete a pagedeleteUpdate a pagepatchWrite image alt textpostGet the approved copygetAssign a pageputList page commentsgetPost a commentpostDelete a commentdeleteEdit a commentpatchReopen a threadpostResolve a threadpostGenerate one pagepostGenerate one fieldpostGenerate a branchpostGenerate page meta datapostCancel page generationpostList page historygetProofread a pagepostUpload a reference docpostDelete a reference docdeleteReorder a pagepostRestore approved copypostWrite a page reviewpostSet the page statuspatchList page versionsgetRevert page to versionpostGenerate pagespostMove pagespostProofread pagespostReview pagespostSet page statusespostMove pagespostUpdate page settingspostReset stuck pagespostDiscover pagespost
    Content schemas
    Content approval flows
    CMS sync
    Social
Automation
Customer channels
Administration
Schemas
GenuineAI API
GenuineAI API

Website content

Sites, their page trees, and the AI that drafts and fills them.


List sites

GET
https://api.genuineai.app/api/v1
/sites

Returns the workspace's sites, each with page, folder and per-status counts, newest first. sitemap_last_edited is the later of the site's own sitemap_updated and the last change to any of its pages; sort=-sitemap_last_edited puts the site most recently worked on first. A "site" is a sitemap: the page tree plus the instructions that content is generated from.

List sites › query Parameters

sitemap_active
​boolean
sort
​string · minLength: 1 · maxLength: 64 · pattern: ^-?[a-z0-9_]+$

List sites › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

List sites › Responses

Success

​Site[] · required
has_more
​boolean · required

Whether more rows exist past this page.

next_cursor
​string | null · required

Pass back as cursor for the next page. Null on the last page.

GET/sites
curl https://api.genuineai.app/api/v1/sites \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
{ "data": [ { "sitemap_id": "00000000-0000-0000-0000-000000000000", "sitemap_created": "2024-08-25T15:00:00Z", "sitemap_updated": "2024-08-25T15:00:00Z", "sitemap_user": "sitemap_user", "sitemap_name": "sitemap_name", "sitemap_domain": "sitemap_domain", "sitemap_description": "sitemap_description", "sitemap_config": {}, "sitemap_metadata": {}, "sitemap_tpl_group": "sitemap_tpl_group", "sitemap_approval_mode": "default", "sitemap_approval_flow": "00000000-0000-0000-0000-000000000000", "sitemap_last_edited": "2024-08-25T15:00:00Z" } ], "has_more": true, "next_cursor": "next_cursor" }
application/json

Create a site

POST
https://api.genuineai.app/api/v1
/sites

Creates an empty site. Pages are added afterwards — by hand, by importing an existing sitemap, or by having a structure proposed from a description of the business.

Create a site › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Create a site › Request Body

sitemap_name
​string · minLength: 1 · required
sitemap_domain
​string
sitemap_description
​string
sitemap_config
​object
sitemap_tpl_group
​string · uuid

Create a site › Responses

Created

A website whose page tree and content the platform drafts, fills and keeps in step with a CMS.
Site
sitemap_id
​string · uuid
sitemap_created
​string · date-time
sitemap_updated
​string · date-time
sitemap_user
​string
sitemap_name
​string
sitemap_domain
​string
sitemap_description
​string
sitemap_config
​object
sitemap_metadata
​object
sitemap_tpl_group
​string
sitemap_approval_mode
​string · enum

How the site is reviewed: the workspace's default approval flow, the one sitemap_approval_flow names, or not at all. Set through PUT /sites/{id}/approval-flow.

Enum values:
default
flow
none
sitemap_approval_flow
​string | null · uuid

The approval flow the site chose; only read when sitemap_approval_mode is flow.

sitemap_last_edited
​string · date-time

The later of sitemap_updated and the last change to any of the site's pages. Only sent by the list.

POST/sites
curl https://api.genuineai.app/api/v1/sites \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "sitemap_name": "sitemap_name",
  "sitemap_domain": "sitemap_domain",
  "sitemap_description": "sitemap_description",
  "sitemap_config": {},
  "sitemap_tpl_group": "00000000-0000-0000-0000-000000000000"
}'
Example Request Body
{
  "sitemap_name": "sitemap_name",
  "sitemap_domain": "sitemap_domain",
  "sitemap_description": "sitemap_description",
  "sitemap_config": {},
  "sitemap_tpl_group": "00000000-0000-0000-0000-000000000000"
}
Example Responses
{ "sitemap_id": "00000000-0000-0000-0000-000000000000", "sitemap_created": "2024-08-25T15:00:00Z", "sitemap_updated": "2024-08-25T15:00:00Z", "sitemap_user": "sitemap_user", "sitemap_name": "sitemap_name", "sitemap_domain": "sitemap_domain", "sitemap_description": "sitemap_description", "sitemap_config": {}, "sitemap_metadata": {}, "sitemap_tpl_group": "sitemap_tpl_group", "sitemap_approval_mode": "default", "sitemap_approval_flow": "00000000-0000-0000-0000-000000000000", "sitemap_last_edited": "2024-08-25T15:00:00Z" }
application/json

Get a site

GET
https://api.genuineai.app/api/v1
/sites/{id}

Returns the site and every page under it in one response. Pages carry node_parent and node_order, so the tree is rebuilt from a flat list rather than requested level by level. The pages come without node_content: a body is the largest thing a page carries and a site has no bounded number of pages, so a tree that carried every body would grow past what one response can hold. Read the body of one page from GET /sites/{id}/pages/{page_id}, or pass include=content to fetch the site with every body inline — for an export, where the whole thing is the point.

Get a site › path Parameters

id
​string · uuid · required

Get a site › query Parameters

include
​string

Get a site › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Get a site › Responses

Success

A website whose page tree and content the platform drafts, fills and keeps in step with a CMS.
Site
sitemap_id
​string · uuid
sitemap_created
​string · date-time
sitemap_updated
​string · date-time
sitemap_user
​string
sitemap_name
​string
sitemap_domain
​string
sitemap_description
​string
sitemap_config
​object
sitemap_metadata
​object
sitemap_tpl_group
​string
sitemap_approval_mode
​string · enum

How the site is reviewed: the workspace's default approval flow, the one sitemap_approval_flow names, or not at all. Set through PUT /sites/{id}/approval-flow.

Enum values:
default
flow
none
sitemap_approval_flow
​string | null · uuid

The approval flow the site chose; only read when sitemap_approval_mode is flow.

sitemap_last_edited
​string · date-time

The later of sitemap_updated and the last change to any of the site's pages. Only sent by the list.

GET/sites/{id}
curl https://api.genuineai.app/api/v1/sites/:id \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
{ "sitemap_id": "00000000-0000-0000-0000-000000000000", "sitemap_created": "2024-08-25T15:00:00Z", "sitemap_updated": "2024-08-25T15:00:00Z", "sitemap_user": "sitemap_user", "sitemap_name": "sitemap_name", "sitemap_domain": "sitemap_domain", "sitemap_description": "sitemap_description", "sitemap_config": {}, "sitemap_metadata": {}, "sitemap_tpl_group": "sitemap_tpl_group", "sitemap_approval_mode": "default", "sitemap_approval_flow": "00000000-0000-0000-0000-000000000000", "sitemap_last_edited": "2024-08-25T15:00:00Z" }
application/json

Delete a site

DELETE
https://api.genuineai.app/api/v1
/sites/{id}

Soft delete. The site and its pages stop being listed; nothing is removed from a connected CMS.

Delete a site › path Parameters

id
​string · uuid · required

Delete a site › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Delete a site › Responses

Success. No content.

No data returned
DELETE/sites/{id}
curl https://api.genuineai.app/api/v1/sites/:id \
  --request DELETE \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
No example specified for this content type

Update a site

PATCH
https://api.genuineai.app/api/v1
/sites/{id}

Applies the sitemap_-prefixed fields present in the body. This is the site itself; its pages are edited through the page endpoints.

Update a site › path Parameters

id
​string · uuid · required

Update a site › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Update a site › Request Body optional

sitemap_name
​string
sitemap_domain
​string
sitemap_description
​string
sitemap_config
​object
sitemap_tpl_group
​string · uuid

Update a site › Responses

Success

A website whose page tree and content the platform drafts, fills and keeps in step with a CMS.
Site
sitemap_id
​string · uuid
sitemap_created
​string · date-time
sitemap_updated
​string · date-time
sitemap_user
​string
sitemap_name
​string
sitemap_domain
​string
sitemap_description
​string
sitemap_config
​object
sitemap_metadata
​object
sitemap_tpl_group
​string
sitemap_approval_mode
​string · enum

How the site is reviewed: the workspace's default approval flow, the one sitemap_approval_flow names, or not at all. Set through PUT /sites/{id}/approval-flow.

Enum values:
default
flow
none
sitemap_approval_flow
​string | null · uuid

The approval flow the site chose; only read when sitemap_approval_mode is flow.

sitemap_last_edited
​string · date-time

The later of sitemap_updated and the last change to any of the site's pages. Only sent by the list.

PATCH/sites/{id}
curl https://api.genuineai.app/api/v1/sites/:id \
  --request PATCH \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "sitemap_name": "sitemap_name",
  "sitemap_domain": "sitemap_domain",
  "sitemap_description": "sitemap_description",
  "sitemap_config": {},
  "sitemap_tpl_group": "00000000-0000-0000-0000-000000000000"
}'
Example Request Body
{
  "sitemap_name": "sitemap_name",
  "sitemap_domain": "sitemap_domain",
  "sitemap_description": "sitemap_description",
  "sitemap_config": {},
  "sitemap_tpl_group": "00000000-0000-0000-0000-000000000000"
}
Example Responses
{ "sitemap_id": "00000000-0000-0000-0000-000000000000", "sitemap_created": "2024-08-25T15:00:00Z", "sitemap_updated": "2024-08-25T15:00:00Z", "sitemap_user": "sitemap_user", "sitemap_name": "sitemap_name", "sitemap_domain": "sitemap_domain", "sitemap_description": "sitemap_description", "sitemap_config": {}, "sitemap_metadata": {}, "sitemap_tpl_group": "sitemap_tpl_group", "sitemap_approval_mode": "default", "sitemap_approval_flow": "00000000-0000-0000-0000-000000000000", "sitemap_last_edited": "2024-08-25T15:00:00Z" }
application/json

Set the review flow

PUT
https://api.genuineai.app/api/v1
/sites/{id}/approval-flow

Chooses how the site's pages are reviewed: default follows the workspace's default approval flow, flow names one in flow_id, and none turns review off for this site. Requires content:workflow:edit rather than content:edit, because deciding what review a site gets is the same decision as defining the flows. Pages already approved or published land on the new flow's final step; pages mid-review re-enter at its start. The answer names the flow now governing the site in governing_flow — with default that is whichever flow holds the role, and with none it is null.

Set the review flow › path Parameters

id
​string · uuid · required

Set the review flow › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Set the review flow › Request Body

mode
​string · enum · required
Enum values:
default
flow
none
flow_id
​string · uuid

Set the review flow › Responses

Success

No data returned
PUT/sites/{id}/approval-flow
curl https://api.genuineai.app/api/v1/sites/:id/approval-flow \
  --request PUT \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "mode": "default",
  "flow_id": "00000000-0000-0000-0000-000000000000"
}'
Example Request Body
{
  "mode": "default",
  "flow_id": "00000000-0000-0000-0000-000000000000"
}
Example Responses
No example specified for this content type

Approve all pages

POST
https://api.genuineai.app/api/v1
/sites/{id}/approve-all

Marks every generated page on the site as approved in one step, for a site reviewed in bulk rather than page by page.

Approve all pages › path Parameters

id
​string · uuid · required

Approve all pages › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Approve all pages › Responses

Success

No data returned
POST/sites/{id}/approve-all
curl https://api.genuineai.app/api/v1/sites/:id/approve-all \
  --request POST \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
No example specified for this content type

List site comments

GET
https://api.genuineai.app/api/v1
/sites/{id}/comments

Every comment thread on the site, one row each, newest activity first — the review status of a whole site without fetching a page at a time. Threads only: replies are counted in cmt_reply_count and their authors listed in cmt_participants, and the full conversation is read from the page it is on. Each row names its page in cmt_page and its anchor in cmt_anchor_label, resolved against the page as it stands; cmt_anchor_unattached marks a thread whose field has since been moved or removed. Filter with status=open (the default) or status=resolved.

List site comments › path Parameters

id
​string · uuid · required

List site comments › query Parameters

cursor
​string · maxLength: 512 · pattern: ^[A-Za-z0-9_-]+$
limit
​integer
offset
​integer · min: 0
sort
​string · minLength: 1 · maxLength: 64 · pattern: ^-?[a-z0-9_]+$
status
​string · enum
Enum values:
open
resolved
all

List site comments › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

List site comments › Responses

Success

​PageCommentThread[] · required
has_more
​boolean · required

Whether more rows exist past this page.

next_cursor
​string | null · required

Pass back as cursor for the next page. Null on the last page.

GET/sites/{id}/comments
curl https://api.genuineai.app/api/v1/sites/:id/comments \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
{ "data": [ { "cmt_id": "00000000-0000-0000-0000-000000000000", "cmt_created": "2024-08-25T15:00:00Z", "cmt_updated": "2024-08-25T15:00:00Z", "cmt_entity": "00000000-0000-0000-0000-000000000000", "cmt_field_path": [ "string" ], "cmt_user": "cmt_user", "cmt_body": "cmt_body", "cmt_mentions": [ { "id": "00000000-0000-0000-0000-000000000000", "name": "name" } ], "cmt_resolved_at": "2024-08-25T15:00:00Z", "cmt_resolved_by": "cmt_resolved_by", "cmt_author": {}, "cmt_resolved_by_name": "cmt_resolved_by_name", "cmt_participants": [], "cmt_reply_count": 0, "cmt_last_activity": "cmt_last_activity", "cmt_page": {}, "cmt_anchor_label": "cmt_anchor_label", "cmt_anchor_unattached": true } ], "has_more": true, "next_cursor": "next_cursor" }
application/json

Set the writing agent

PATCH
https://api.genuineai.app/api/v1
/sites/{id}/content-generation-agent

Sets the agent used when generating this site's content. Requires content:generate rather than content:edit — choosing who writes is part of generating, not part of editing the site.

A single page overrides this by naming an agent in its own node_instructions_params.contentAgentId.

Set the writing agent › path Parameters

id
​string · uuid · required

Set the writing agent › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Set the writing agent › Request Body optional

agentId
​string · uuid

Set the writing agent › Responses

Success

No data returned
PATCH/sites/{id}/content-generation-agent
curl https://api.genuineai.app/api/v1/sites/:id/content-generation-agent \
  --request PATCH \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "agentId": "00000000-0000-0000-0000-000000000000"
}'
Example Request Body
{
  "agentId": "00000000-0000-0000-0000-000000000000"
}
Example Responses
No example specified for this content type

Generate all pages

POST
https://api.genuineai.app/api/v1
/sites/{id}/generate-all

Queues content generation for each empty page that has instructions of its own or inherited from a parent. Returns once the work is queued; generation runs in the background and pages move to draft as they complete.

A page that names its own agent in node_instructions_params.contentAgentId is written by that one, so a single run can span pages written by different agents. agentId covers the rest.

Generate all pages › path Parameters

id
​string · uuid · required

Generate all pages › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Generate all pages › Request Body optional

agentId
​string · uuid
proofread
​boolean
review
​boolean

Generate all pages › Responses

Success

No data returned
POST/sites/{id}/generate-all
curl https://api.genuineai.app/api/v1/sites/:id/generate-all \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "agentId": "00000000-0000-0000-0000-000000000000",
  "proofread": true,
  "review": true
}'
Example Request Body
{
  "agentId": "00000000-0000-0000-0000-000000000000",
  "proofread": true,
  "review": true
}
Example Responses
No example specified for this content type

Generate all briefs

POST
https://api.genuineai.app/api/v1
/sites/{id}/generate-briefs

Starts background brief generation, one job per page. Returns as soon as the jobs are queued, not when they finish — poll the site's briefsJob for progress.

Generate all briefs › path Parameters

id
​string · uuid · required

Generate all briefs › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Generate all briefs › Request Body optional

nodeIds
​string[]
options
​object
siteBrief
​string · maxLength: 20000

Generate all briefs › Responses

Success

No data returned
POST/sites/{id}/generate-briefs
curl https://api.genuineai.app/api/v1/sites/:id/generate-briefs \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "nodeIds": [
    "00000000-0000-0000-0000-000000000000"
  ],
  "options": {},
  "siteBrief": "siteBrief"
}'
Example Request Body
{
  "nodeIds": [
    "00000000-0000-0000-0000-000000000000"
  ],
  "options": {},
  "siteBrief": "siteBrief"
}
Example Responses
No example specified for this content type

Generate the site brief

POST
https://api.genuineai.app/api/v1
/sites/{id}/generate-site-brief

Drafts the site-level instructions every page inherits, reading the live site where one exists.

Generate the site brief › path Parameters

id
​string · uuid · required

Generate the site brief › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Generate the site brief › Request Body optional

domain
​string · maxLength: 2048
paths
​string[]

Generate the site brief › Responses

Success

No data returned
POST/sites/{id}/generate-site-brief
curl https://api.genuineai.app/api/v1/sites/:id/generate-site-brief \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "domain": "domain",
  "paths": [
    "string"
  ]
}'
Example Request Body
{
  "domain": "domain",
  "paths": [
    "string"
  ]
}
Example Responses
No example specified for this content type

Generate the structure

POST
https://api.genuineai.app/api/v1
/sites/{id}/generate-structure

Proposes a page tree from a description of the business, for a site that does not exist yet. Returns the proposal for review — it is not saved until you import it.

Generate the structure › path Parameters

id
​string · uuid · required

Generate the structure › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Generate the structure › Request Body

description
​string · minLength: 10 · maxLength: 10000 · required
size
​string · enum
Enum values:
starter
standard
full

Generate the structure › Responses

Success

No data returned
POST/sites/{id}/generate-structure
curl https://api.genuineai.app/api/v1/sites/:id/generate-structure \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "description": "description",
  "size": "starter"
}'
Example Request Body
{
  "description": "description",
  "size": "starter"
}
Example Responses
No example specified for this content type

Get generation defaults

GET
https://api.genuineai.app/api/v1
/sites/{id}/generation-defaults

The site's instructions for the two passes that follow the copywriter, beside the platform defaults they replace when set: review (what the review agent writes about each page) and editor (what the editor agent is told to enforce and report). Set through the site's sitemap_config: reviewInstructions and editorInstructions, beside the agents themselves in reviewAgentId and editorAgentId. A page appends its own notes in node_instructions_params.reviewInstructions / editorInstructions.

Get generation defaults › path Parameters

id
​string · uuid · required

Get generation defaults › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Get generation defaults › Responses

Success

​object
​object
GET/sites/{id}/generation-defaults
curl https://api.genuineai.app/api/v1/sites/:id/generation-defaults \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
{
  "review": {
    "instructions": "instructions",
    "default": "default"
  },
  "editor": {
    "instructions": "instructions",
    "default": "default",
    "agentPrompt": "agentPrompt"
  }
}
application/json

Import pages

POST
https://api.genuineai.app/api/v1
/sites/{id}/import

Builds the page tree from URLs or page titles. Accepts a urls array, sitemap XML, a url to fetch the XML from, or a plain one-per-line list — the parser is deliberately tolerant, because this input is usually pasted by a person. A list with nothing URL-shaped in it is read as page titles: each line becomes a page whose slug is made from the title, and a line indented under the one before it becomes that page's child.

Every listed path becomes a page, whether or not other listed paths sit beneath it: a section landing page is still a page. A segment that appears only as an ancestor becomes a folder, since nothing was said about it. A type of page or folder, from a column or the sidecar, decides either way, and on a path that already exists it changes the node in place, except that a page holding content is never turned into a folder.

A comma, tab or semicolon separated file is read as a table when its first row names the columns: url (or path, slug) locates the page, name (or title) names it, and instructions and type are applied the same way the instructions sidecar is. A heading is matched on any word it carries, so V1 URL (slug) is a URL column; a table with a name column and no URL column gets its slugs from the names. Without a recognized header row the first URL-looking cell of each line is taken and the rest of the line is discarded. A page that arrives without a name is named after its slug, dashes to spaces and words capitalized. On a dry run the response reports which format was read and which columns went unused.

It also accepts a site export: post the exported document as the body, or its nodes array on its own. A URL path cannot carry capitalization, sibling order, or two pages under the same folder sharing a slug, so an export is the input that survives a round trip — the tree, page names, order and type are read from the file. Pages are matched on the ids in the file, then on their slug path, and updated where they match; nothing is deleted, so a page the file no longer mentions stays. Content in the file is written as a draft, which withdraws any sign-off it had, exactly as editing the page would.

Import pages › path Parameters

id
​string · uuid · required

Import pages › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Import pages › Request Body optional

xml
​string
url
​string
urls
​string[]
​object[]
keys
​string[]
dryRun
​boolean
instructions
​object

Import pages › Responses

Success

No data returned
POST/sites/{id}/import
curl https://api.genuineai.app/api/v1/sites/:id/import \ --request POST \ --header 'Content-Type: application/json' \ --header 'X-Tenant-Id: <string>' \ --header 'X-Api-Key: <api-key>' \ --data '{ "xml": "xml", "url": "url", "urls": [ "string" ], "nodes": [ {} ], "keys": [ "string" ], "dryRun": true, "instructions": {} }'
Example Request Body
{ "xml": "xml", "url": "url", "urls": [ "string" ], "nodes": [ {} ], "keys": [ "string" ], "dryRun": true, "instructions": {} }
Example Responses
No example specified for this content type

Add a page

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages

Adds a page or folder to the tree. Omit node_order and it is appended to the bottom of its sibling group, which is almost always what you want — passing 0 collides with every existing sibling.

node_slug is the page's own URL segment and is derived from node_name when omitted. Two pages under one parent cannot share it, so a slug already in use is numbered off it (about, about-2) and the response carries the one the page was given.

Add a page › path Parameters

id
​string · uuid · required

Add a page › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Add a page › Request Body

node_name
​string · minLength: 1 · required
node_parent
​string · uuid
node_type
​string · enum
Enum values:
page
folder
node_slug
​string
node_instructions
​string
node_instructions_params
​object
node_children_instructions
​string
node_component
​string
node_order
​integer

Add a page › Responses

Created

One page or folder in a site's tree: what it is for, what has been written into it, and how far along that is.
Page
node_id
​string · uuid
node_created
​string · date-time
node_updated
​string · date-time
node_sitemap
​string
node_parent
​string
node_order
​integer
node_type
​string · enum

A page holds content and may have pages under it; a folder only holds pages. A page with content cannot become a folder.

Enum values:
page
folder
node_name
​string
node_slug
​string
node_instructions
​string
node_instructions_params
​string
node_children_instructions
​string
node_component
​string
node_content
​object
node_content_summary
​string
node_content_status
​string
node_state_item
​string
node_threads
​string
node_metadata
​object
​object

The page's SEO meta data. Filled by generation from the draft copy and editable; kept out of node_content so it is never pushed to a connected CMS.

​object

Reviewer material written alongside the copy: the review agent's account of the page when the site names one (review), and the editor agent's pass when the site names an editor (editor). Kept out of node_content so it never reaches a CMS. Left out of the site tree; read it from one page, or include=review.

node_assignee
​string | null · uuid

Who the page currently waits on. Set freely via PUT /sites/{id}/pages/{page_id}/assignee and carried unchanged through workflow moves unless the move says otherwise.

node_approved_at
​string | null · date-time

When the page was last signed off. A page carrying this and standing at draft has been edited since — the copy that was approved is still kept, and GET /sites/{id}/pages/{page_id}/approved-content answers with it.

node_approved_by
​string | null · uuid

Who signed it off. Null on pages approved before this was recorded.

node_open_comments
​integer

Unresolved comment threads on this page. Only sent when a site is fetched whole.

POST/sites/{id}/pages
curl https://api.genuineai.app/api/v1/sites/:id/pages \ --request POST \ --header 'Content-Type: application/json' \ --header 'X-Tenant-Id: <string>' \ --header 'X-Api-Key: <api-key>' \ --data '{ "node_parent": "00000000-0000-0000-0000-000000000000", "node_name": "node_name", "node_type": "page", "node_slug": "node_slug", "node_instructions": "node_instructions", "node_instructions_params": {}, "node_children_instructions": "node_children_instructions", "node_component": "node_component", "node_order": 0 }'
Example Request Body
{
  "node_parent": "00000000-0000-0000-0000-000000000000",
  "node_name": "node_name",
  "node_type": "page",
  "node_slug": "node_slug",
  "node_instructions": "node_instructions",
  "node_instructions_params": {},
  "node_children_instructions": "node_children_instructions",
  "node_component": "node_component",
  "node_order": 0
}
Example Responses
{ "node_id": "00000000-0000-0000-0000-000000000000", "node_created": "2024-08-25T15:00:00Z", "node_updated": "2024-08-25T15:00:00Z", "node_sitemap": "node_sitemap", "node_parent": "node_parent", "node_order": 0, "node_type": "page", "node_name": "node_name", "node_slug": "node_slug", "node_instructions": "node_instructions", "node_instructions_params": "node_instructions_params", "node_children_instructions": "node_children_instructions", "node_component": "node_component", "node_content": {}, "node_content_summary": "node_content_summary", "node_content_status": "node_content_status", "node_state_item": "node_state_item", "node_threads": "node_threads", "node_metadata": {}, "node_meta": { "title": "title", "description": "description" }, "node_review": { "review": { "text": "text", "generated_at": "2024-08-25T15:00:00Z", "agent_id": "00000000-0000-0000-0000-000000000000", "agent_name": "agent_name" }, "editor": { "agent_id": "00000000-0000-0000-0000-000000000000", "agent_name": "agent_name", "summary": "summary", "changes": [ { "field": "field", "what": "what", "why": "why", "applied": true } ], "proposed": {}, "notes": "notes", "changed": true, "reviewed_at": "2024-08-25T15:00:00Z", "error": "error" } }, "node_assignee": "00000000-0000-0000-0000-000000000000", "node_approved_at": "2024-08-25T15:00:00Z", "node_approved_by": "00000000-0000-0000-0000-000000000000", "node_open_comments": 0 }
application/json

Get a page

GET
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}

One page: what it is for, what has been written into it, and how far through review it is.

Get a page › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

Get a page › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Get a page › Responses

Success

One page or folder in a site's tree: what it is for, what has been written into it, and how far along that is.
Page
node_id
​string · uuid
node_created
​string · date-time
node_updated
​string · date-time
node_sitemap
​string
node_parent
​string
node_order
​integer
node_type
​string · enum

A page holds content and may have pages under it; a folder only holds pages. A page with content cannot become a folder.

Enum values:
page
folder
node_name
​string
node_slug
​string
node_instructions
​string
node_instructions_params
​string
node_children_instructions
​string
node_component
​string
node_content
​object
node_content_summary
​string
node_content_status
​string
node_state_item
​string
node_threads
​string
node_metadata
​object
​object

The page's SEO meta data. Filled by generation from the draft copy and editable; kept out of node_content so it is never pushed to a connected CMS.

​object

Reviewer material written alongside the copy: the review agent's account of the page when the site names one (review), and the editor agent's pass when the site names an editor (editor). Kept out of node_content so it never reaches a CMS. Left out of the site tree; read it from one page, or include=review.

node_assignee
​string | null · uuid

Who the page currently waits on. Set freely via PUT /sites/{id}/pages/{page_id}/assignee and carried unchanged through workflow moves unless the move says otherwise.

node_approved_at
​string | null · date-time

When the page was last signed off. A page carrying this and standing at draft has been edited since — the copy that was approved is still kept, and GET /sites/{id}/pages/{page_id}/approved-content answers with it.

node_approved_by
​string | null · uuid

Who signed it off. Null on pages approved before this was recorded.

node_open_comments
​integer

Unresolved comment threads on this page. Only sent when a site is fetched whole.

GET/sites/{id}/pages/{page_id}
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
{ "node_id": "00000000-0000-0000-0000-000000000000", "node_created": "2024-08-25T15:00:00Z", "node_updated": "2024-08-25T15:00:00Z", "node_sitemap": "node_sitemap", "node_parent": "node_parent", "node_order": 0, "node_type": "page", "node_name": "node_name", "node_slug": "node_slug", "node_instructions": "node_instructions", "node_instructions_params": "node_instructions_params", "node_children_instructions": "node_children_instructions", "node_component": "node_component", "node_content": {}, "node_content_summary": "node_content_summary", "node_content_status": "node_content_status", "node_state_item": "node_state_item", "node_threads": "node_threads", "node_metadata": {}, "node_meta": { "title": "title", "description": "description" }, "node_review": { "review": { "text": "text", "generated_at": "2024-08-25T15:00:00Z", "agent_id": "00000000-0000-0000-0000-000000000000", "agent_name": "agent_name" }, "editor": { "agent_id": "00000000-0000-0000-0000-000000000000", "agent_name": "agent_name", "summary": "summary", "changes": [ { "field": "field", "what": "what", "why": "why", "applied": true } ], "proposed": {}, "notes": "notes", "changed": true, "reviewed_at": "2024-08-25T15:00:00Z", "error": "error" } }, "node_assignee": "00000000-0000-0000-0000-000000000000", "node_approved_at": "2024-08-25T15:00:00Z", "node_approved_by": "00000000-0000-0000-0000-000000000000", "node_open_comments": 0 }
application/json

Delete a page

DELETE
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}

Removes the page and everything filed under it.

Delete a page › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

Delete a page › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Delete a page › Responses

Success. No content.

No data returned
DELETE/sites/{id}/pages/{page_id}
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id \
  --request DELETE \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
No example specified for this content type

Update a page

PATCH
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}

Applies the node_-prefixed fields present in the body. Writing content directly is allowed — generation is one way to fill a page, not the only one. Writing node_content into a page that had been approved withdraws that sign-off: the page returns to draft and re-enters the site's review at the first step, since nobody has read the text it now holds. The approved copy is kept — POST /sites/{id}/pages/{page_id}/restore-approved puts it back.

A node_slug that another page under the same parent already answers to is refused with 409: the address was named rather than derived, so numbering it off would publish the page somewhere other than where it was asked for.

node_type moves a node between page and folder; pages under it stay either way. A page that holds content is refused as a folder with 409. Discard the content first.

Update a page › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

Update a page › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Update a page › Request Body optional

node_name
​string
node_slug
​string
node_type
​string · enum
Enum values:
page
folder
node_instructions
​string
node_instructions_params
​object
node_children_instructions
​string
node_component
​string
node_order
​integer
node_content
​object
node_content_summary
​string
node_metadata
​object
​object
node_review
​object

Update a page › Responses

Success

One page or folder in a site's tree: what it is for, what has been written into it, and how far along that is.
Page
node_id
​string · uuid
node_created
​string · date-time
node_updated
​string · date-time
node_sitemap
​string
node_parent
​string
node_order
​integer
node_type
​string · enum

A page holds content and may have pages under it; a folder only holds pages. A page with content cannot become a folder.

Enum values:
page
folder
node_name
​string
node_slug
​string
node_instructions
​string
node_instructions_params
​string
node_children_instructions
​string
node_component
​string
node_content
​object
node_content_summary
​string
node_content_status
​string
node_state_item
​string
node_threads
​string
node_metadata
​object
​object

The page's SEO meta data. Filled by generation from the draft copy and editable; kept out of node_content so it is never pushed to a connected CMS.

​object

Reviewer material written alongside the copy: the review agent's account of the page when the site names one (review), and the editor agent's pass when the site names an editor (editor). Kept out of node_content so it never reaches a CMS. Left out of the site tree; read it from one page, or include=review.

node_assignee
​string | null · uuid

Who the page currently waits on. Set freely via PUT /sites/{id}/pages/{page_id}/assignee and carried unchanged through workflow moves unless the move says otherwise.

node_approved_at
​string | null · date-time

When the page was last signed off. A page carrying this and standing at draft has been edited since — the copy that was approved is still kept, and GET /sites/{id}/pages/{page_id}/approved-content answers with it.

node_approved_by
​string | null · uuid

Who signed it off. Null on pages approved before this was recorded.

node_open_comments
​integer

Unresolved comment threads on this page. Only sent when a site is fetched whole.

PATCH/sites/{id}/pages/{page_id}
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id \ --request PATCH \ --header 'Content-Type: application/json' \ --header 'X-Tenant-Id: <string>' \ --header 'X-Api-Key: <api-key>' \ --data '{ "node_name": "node_name", "node_slug": "node_slug", "node_type": "page", "node_instructions": "node_instructions", "node_instructions_params": {}, "node_children_instructions": "node_children_instructions", "node_component": "node_component", "node_order": 0, "node_content": {}, "node_content_summary": "node_content_summary", "node_metadata": {}, "node_meta": { "title": "title", "description": "description" }, "node_review": {} }'
Example Request Body
{ "node_name": "node_name", "node_slug": "node_slug", "node_type": "page", "node_instructions": "node_instructions", "node_instructions_params": {}, "node_children_instructions": "node_children_instructions", "node_component": "node_component", "node_order": 0, "node_content": {}, "node_content_summary": "node_content_summary", "node_metadata": {}, "node_meta": { "title": "title", "description": "description" }, "node_review": {} }
Example Responses
{ "node_id": "00000000-0000-0000-0000-000000000000", "node_created": "2024-08-25T15:00:00Z", "node_updated": "2024-08-25T15:00:00Z", "node_sitemap": "node_sitemap", "node_parent": "node_parent", "node_order": 0, "node_type": "page", "node_name": "node_name", "node_slug": "node_slug", "node_instructions": "node_instructions", "node_instructions_params": "node_instructions_params", "node_children_instructions": "node_children_instructions", "node_component": "node_component", "node_content": {}, "node_content_summary": "node_content_summary", "node_content_status": "node_content_status", "node_state_item": "node_state_item", "node_threads": "node_threads", "node_metadata": {}, "node_meta": { "title": "title", "description": "description" }, "node_review": { "review": { "text": "text", "generated_at": "2024-08-25T15:00:00Z", "agent_id": "00000000-0000-0000-0000-000000000000", "agent_name": "agent_name" }, "editor": { "agent_id": "00000000-0000-0000-0000-000000000000", "agent_name": "agent_name", "summary": "summary", "changes": [ { "field": "field", "what": "what", "why": "why", "applied": true } ], "proposed": {}, "notes": "notes", "changed": true, "reviewed_at": "2024-08-25T15:00:00Z", "error": "error" } }, "node_assignee": "00000000-0000-0000-0000-000000000000", "node_approved_at": "2024-08-25T15:00:00Z", "node_approved_by": "00000000-0000-0000-0000-000000000000", "node_open_comments": 0 }
application/json

Write image alt text

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/alt-text

Looks at one image and writes alt text for it in the context of this page: the site, the page, the field it sits in (fieldPath) and the copy beside it. The image is either a file of the workspace (file_id, which the caller must be able to read) or a public web address (url, fetched once and never stored). Returns the text for review; nothing is saved until the page is. currentContent lets an editor pass unsaved copy and instruction steers this one run.

Write image alt text › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

Write image alt text › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Write image alt text › Request Body optional

agentId
​string · uuid
file_id
​string · uuid
url
​string · uri
fieldPath
​string[]
currentContent
​object
instruction
​string · maxLength: 2000

Write image alt text › Responses

Success

No data returned
POST/sites/{id}/pages/{page_id}/alt-text
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/alt-text \ --request POST \ --header 'Content-Type: application/json' \ --header 'X-Tenant-Id: <string>' \ --header 'X-Api-Key: <api-key>' \ --data '{ "agentId": "00000000-0000-0000-0000-000000000000", "file_id": "00000000-0000-0000-0000-000000000000", "url": "https://www.example.com/path/to/resource", "fieldPath": [ "string" ], "currentContent": {}, "instruction": "instruction" }'
Example Request Body
{
  "agentId": "00000000-0000-0000-0000-000000000000",
  "file_id": "00000000-0000-0000-0000-000000000000",
  "url": "https://www.example.com/path/to/resource",
  "fieldPath": [
    "string"
  ],
  "currentContent": {},
  "instruction": "instruction"
}
Example Responses
No example specified for this content type

Get the approved copy

GET
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/approved-content

The content as it stood when the page was last signed off, which is not what node_content holds once anyone has edited it. Editing an approved page returns it to draft and keeps this, so it answers both "what changed since approval" and "what does the live website still show". 404 while nothing about the page has ever been approved — node_approved_at on the page says which it is.

Get the approved copy › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

Get the approved copy › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Get the approved copy › Responses

Success

No data returned
GET/sites/{id}/pages/{page_id}/approved-content
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/approved-content \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
No example specified for this content type

Assign a page

PUT
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/assignee

Hands the page to a member of the workspace, or takes it from them with assignee_id: null. Assignment says who the page waits on and nothing more — it does not move the page, and workflow moves do not change it. The new assignee is notified.

Requires either content:edit or content:review.

Assign a page › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

Assign a page › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Assign a page › Request Body

assignee_id
​string · required

Assign a page › Responses

Success

One page or folder in a site's tree: what it is for, what has been written into it, and how far along that is.
Page
node_id
​string · uuid
node_created
​string · date-time
node_updated
​string · date-time
node_sitemap
​string
node_parent
​string
node_order
​integer
node_type
​string · enum

A page holds content and may have pages under it; a folder only holds pages. A page with content cannot become a folder.

Enum values:
page
folder
node_name
​string
node_slug
​string
node_instructions
​string
node_instructions_params
​string
node_children_instructions
​string
node_component
​string
node_content
​object
node_content_summary
​string
node_content_status
​string
node_state_item
​string
node_threads
​string
node_metadata
​object
​object

The page's SEO meta data. Filled by generation from the draft copy and editable; kept out of node_content so it is never pushed to a connected CMS.

​object

Reviewer material written alongside the copy: the review agent's account of the page when the site names one (review), and the editor agent's pass when the site names an editor (editor). Kept out of node_content so it never reaches a CMS. Left out of the site tree; read it from one page, or include=review.

node_assignee
​string | null · uuid

Who the page currently waits on. Set freely via PUT /sites/{id}/pages/{page_id}/assignee and carried unchanged through workflow moves unless the move says otherwise.

node_approved_at
​string | null · date-time

When the page was last signed off. A page carrying this and standing at draft has been edited since — the copy that was approved is still kept, and GET /sites/{id}/pages/{page_id}/approved-content answers with it.

node_approved_by
​string | null · uuid

Who signed it off. Null on pages approved before this was recorded.

node_open_comments
​integer

Unresolved comment threads on this page. Only sent when a site is fetched whole.

PUT/sites/{id}/pages/{page_id}/assignee
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/assignee \
  --request PUT \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "assignee_id": "assignee_id"
}'
Example Request Body
{
  "assignee_id": "assignee_id"
}
Example Responses
{ "node_id": "00000000-0000-0000-0000-000000000000", "node_created": "2024-08-25T15:00:00Z", "node_updated": "2024-08-25T15:00:00Z", "node_sitemap": "node_sitemap", "node_parent": "node_parent", "node_order": 0, "node_type": "page", "node_name": "node_name", "node_slug": "node_slug", "node_instructions": "node_instructions", "node_instructions_params": "node_instructions_params", "node_children_instructions": "node_children_instructions", "node_component": "node_component", "node_content": {}, "node_content_summary": "node_content_summary", "node_content_status": "node_content_status", "node_state_item": "node_state_item", "node_threads": "node_threads", "node_metadata": {}, "node_meta": { "title": "title", "description": "description" }, "node_review": { "review": { "text": "text", "generated_at": "2024-08-25T15:00:00Z", "agent_id": "00000000-0000-0000-0000-000000000000", "agent_name": "agent_name" }, "editor": { "agent_id": "00000000-0000-0000-0000-000000000000", "agent_name": "agent_name", "summary": "summary", "changes": [ { "field": "field", "what": "what", "why": "why", "applied": true } ], "proposed": {}, "notes": "notes", "changed": true, "reviewed_at": "2024-08-25T15:00:00Z", "error": "error" } }, "node_assignee": "00000000-0000-0000-0000-000000000000", "node_approved_at": "2024-08-25T15:00:00Z", "node_approved_by": "00000000-0000-0000-0000-000000000000", "node_open_comments": 0 }
application/json

List page comments

GET
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/comments

Returns every comment on the page in one response, threads and replies together, oldest first — group them by cmt_parent rather than requesting a thread at a time. A thread names what it is about through cmt_field_path, an array of field keys and array indices addressing one part of the record (["body", 0, "headline"]); a thread with no field path is about the page as a whole. Filter with status=open or status=resolved; resolution belongs to the thread, so replies are filtered with the thread they are in.

List page comments › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

List page comments › query Parameters

cmt_parent
​string · uuid
cursor
​string · maxLength: 512 · pattern: ^[A-Za-z0-9_-]+$
limit
​integer
offset
​integer · min: 0
sort
​string · minLength: 1 · maxLength: 64 · pattern: ^-?[a-z0-9_]+$
status
​string · enum
Enum values:
open
resolved
all

List page comments › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

List page comments › Responses

Success

​Comment[] · required
has_more
​boolean · required

Whether more rows exist past this page.

next_cursor
​string | null · required

Pass back as cursor for the next page. Null on the last page.

GET/sites/{id}/pages/{page_id}/comments
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/comments \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
{ "data": [ { "cmt_id": "00000000-0000-0000-0000-000000000000", "cmt_created": "2024-08-25T15:00:00Z", "cmt_updated": "2024-08-25T15:00:00Z", "cmt_entity_type": "cmt_entity_type", "cmt_entity": "00000000-0000-0000-0000-000000000000", "cmt_field_path": [ "string" ], "cmt_parent": "cmt_parent", "cmt_user": "cmt_user", "cmt_body": "cmt_body", "cmt_mentions": [ { "id": "00000000-0000-0000-0000-000000000000", "name": "name" } ], "cmt_resolved_at": "2024-08-25T15:00:00Z", "cmt_resolved_by": "cmt_resolved_by", "cmt_author": {}, "cmt_resolved_by_name": "cmt_resolved_by_name" } ], "has_more": true, "next_cursor": "next_cursor" }
application/json

Post a comment

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/comments

Starts a thread, or replies to one when cmt_parent is given. A reply inherits the anchor and the resolved state of the thread it joins, so it takes no cmt_field_path of its own. A path is stored as sent except that array indices are normalized to numbers, so ["body", "0"] and ["body", 0] are one anchor rather than two. Anyone named in cmt_mentions is notified, as is everyone already in the thread.

Post a comment › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

Post a comment › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Post a comment › Request Body

cmt_body
​string · minLength: 1 · maxLength: 10000 · required
​object[]
cmt_field_path
​string[]
cmt_parent
​string · uuid

Post a comment › Responses

Created

A comment on a record, optionally anchored to one part of it, and its replies.
Comment
cmt_id
​string · uuid
cmt_created
​string · date-time
cmt_updated
​string · date-time
cmt_entity_type
​string
cmt_entity
​string · uuid
cmt_field_path
​array | null

Field keys and array indices addressing one part of the record, from its root. Null means the record as a whole.

cmt_parent
​string
cmt_user
​string
cmt_body
​string
​object[]

Users named in the body, as the directory spells them.

cmt_resolved_at
​string · date-time
cmt_resolved_by
​string
cmt_author
​object

Who wrote it — enough to attribute and draw an avatar without a second request.

cmt_resolved_by_name
​string | null

Display name of whoever resolved the thread. Null while it is open.

POST/sites/{id}/pages/{page_id}/comments
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/comments \ --request POST \ --header 'Content-Type: application/json' \ --header 'X-Tenant-Id: <string>' \ --header 'X-Api-Key: <api-key>' \ --data '{ "cmt_body": "cmt_body", "cmt_mentions": [ { "id": "00000000-0000-0000-0000-000000000000" } ], "cmt_field_path": [ "string" ], "cmt_parent": "00000000-0000-0000-0000-000000000000" }'
Example Request Body
{
  "cmt_body": "cmt_body",
  "cmt_mentions": [
    {
      "id": "00000000-0000-0000-0000-000000000000"
    }
  ],
  "cmt_field_path": [
    "string"
  ],
  "cmt_parent": "00000000-0000-0000-0000-000000000000"
}
Example Responses
{ "cmt_id": "00000000-0000-0000-0000-000000000000", "cmt_created": "2024-08-25T15:00:00Z", "cmt_updated": "2024-08-25T15:00:00Z", "cmt_entity_type": "cmt_entity_type", "cmt_entity": "00000000-0000-0000-0000-000000000000", "cmt_field_path": [ "string" ], "cmt_parent": "cmt_parent", "cmt_user": "cmt_user", "cmt_body": "cmt_body", "cmt_mentions": [ { "id": "00000000-0000-0000-0000-000000000000", "name": "name" } ], "cmt_resolved_at": "2024-08-25T15:00:00Z", "cmt_resolved_by": "cmt_resolved_by", "cmt_author": {}, "cmt_resolved_by_name": "cmt_resolved_by_name" }
application/json

Delete a comment

DELETE
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/comments/{comment_id}

Soft delete, by the author or by anyone holding content:comment:delete. Deleting the first comment of a thread removes its replies with it.

Delete a comment › path Parameters

comment_id
​string · uuid · required
id
​string · uuid · required
page_id
​string · uuid · required

Delete a comment › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Delete a comment › Responses

Success. No content.

No data returned
DELETE/sites/{id}/pages/{page_id}/comments/{comment_id}
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/comments/:comment_id \
  --request DELETE \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
No example specified for this content type

Edit a comment

PATCH
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/comments/{comment_id}

Rewrites the text of your own comment. Moderating someone else's means deleting it, not rewriting what they said.

Edit a comment › path Parameters

comment_id
​string · uuid · required
id
​string · uuid · required
page_id
​string · uuid · required

Edit a comment › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Edit a comment › Request Body

cmt_body
​string · minLength: 1 · maxLength: 10000 · required
​object[]

Edit a comment › Responses

Success

A comment on a record, optionally anchored to one part of it, and its replies.
Comment
cmt_id
​string · uuid
cmt_created
​string · date-time
cmt_updated
​string · date-time
cmt_entity_type
​string
cmt_entity
​string · uuid
cmt_field_path
​array | null

Field keys and array indices addressing one part of the record, from its root. Null means the record as a whole.

cmt_parent
​string
cmt_user
​string
cmt_body
​string
​object[]

Users named in the body, as the directory spells them.

cmt_resolved_at
​string · date-time
cmt_resolved_by
​string
cmt_author
​object

Who wrote it — enough to attribute and draw an avatar without a second request.

cmt_resolved_by_name
​string | null

Display name of whoever resolved the thread. Null while it is open.

PATCH/sites/{id}/pages/{page_id}/comments/{comment_id}
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/comments/:comment_id \
  --request PATCH \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "cmt_body": "cmt_body",
  "cmt_mentions": [
    {
      "id": "00000000-0000-0000-0000-000000000000"
    }
  ]
}'
Example Request Body
{
  "cmt_body": "cmt_body",
  "cmt_mentions": [
    {
      "id": "00000000-0000-0000-0000-000000000000"
    }
  ]
}
Example Responses
{ "cmt_id": "00000000-0000-0000-0000-000000000000", "cmt_created": "2024-08-25T15:00:00Z", "cmt_updated": "2024-08-25T15:00:00Z", "cmt_entity_type": "cmt_entity_type", "cmt_entity": "00000000-0000-0000-0000-000000000000", "cmt_field_path": [ "string" ], "cmt_parent": "cmt_parent", "cmt_user": "cmt_user", "cmt_body": "cmt_body", "cmt_mentions": [ { "id": "00000000-0000-0000-0000-000000000000", "name": "name" } ], "cmt_resolved_at": "2024-08-25T15:00:00Z", "cmt_resolved_by": "cmt_resolved_by", "cmt_author": {}, "cmt_resolved_by_name": "cmt_resolved_by_name" }
application/json

Reopen a thread

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/comments/{comment_id}/reopen

Puts a resolved thread back on the open list.

Reopen a thread › path Parameters

comment_id
​string · uuid · required
id
​string · uuid · required
page_id
​string · uuid · required

Reopen a thread › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Reopen a thread › Responses

Success

A comment on a record, optionally anchored to one part of it, and its replies.
Comment
cmt_id
​string · uuid
cmt_created
​string · date-time
cmt_updated
​string · date-time
cmt_entity_type
​string
cmt_entity
​string · uuid
cmt_field_path
​array | null

Field keys and array indices addressing one part of the record, from its root. Null means the record as a whole.

cmt_parent
​string
cmt_user
​string
cmt_body
​string
​object[]

Users named in the body, as the directory spells them.

cmt_resolved_at
​string · date-time
cmt_resolved_by
​string
cmt_author
​object

Who wrote it — enough to attribute and draw an avatar without a second request.

cmt_resolved_by_name
​string | null

Display name of whoever resolved the thread. Null while it is open.

POST/sites/{id}/pages/{page_id}/comments/{comment_id}/reopen
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/comments/:comment_id/reopen \
  --request POST \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
{ "cmt_id": "00000000-0000-0000-0000-000000000000", "cmt_created": "2024-08-25T15:00:00Z", "cmt_updated": "2024-08-25T15:00:00Z", "cmt_entity_type": "cmt_entity_type", "cmt_entity": "00000000-0000-0000-0000-000000000000", "cmt_field_path": [ "string" ], "cmt_parent": "cmt_parent", "cmt_user": "cmt_user", "cmt_body": "cmt_body", "cmt_mentions": [ { "id": "00000000-0000-0000-0000-000000000000", "name": "name" } ], "cmt_resolved_at": "2024-08-25T15:00:00Z", "cmt_resolved_by": "cmt_resolved_by", "cmt_author": {}, "cmt_resolved_by_name": "cmt_resolved_by_name" }
application/json

Resolve a thread

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/comments/{comment_id}/resolve

Marks the thread settled and drops it out of the open list. Nothing is deleted — a resolved thread is the record of the review, and reopening restores it.

Resolve a thread › path Parameters

comment_id
​string · uuid · required
id
​string · uuid · required
page_id
​string · uuid · required

Resolve a thread › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Resolve a thread › Responses

Success

A comment on a record, optionally anchored to one part of it, and its replies.
Comment
cmt_id
​string · uuid
cmt_created
​string · date-time
cmt_updated
​string · date-time
cmt_entity_type
​string
cmt_entity
​string · uuid
cmt_field_path
​array | null

Field keys and array indices addressing one part of the record, from its root. Null means the record as a whole.

cmt_parent
​string
cmt_user
​string
cmt_body
​string
​object[]

Users named in the body, as the directory spells them.

cmt_resolved_at
​string · date-time
cmt_resolved_by
​string
cmt_author
​object

Who wrote it — enough to attribute and draw an avatar without a second request.

cmt_resolved_by_name
​string | null

Display name of whoever resolved the thread. Null while it is open.

POST/sites/{id}/pages/{page_id}/comments/{comment_id}/resolve
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/comments/:comment_id/resolve \
  --request POST \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
{ "cmt_id": "00000000-0000-0000-0000-000000000000", "cmt_created": "2024-08-25T15:00:00Z", "cmt_updated": "2024-08-25T15:00:00Z", "cmt_entity_type": "cmt_entity_type", "cmt_entity": "00000000-0000-0000-0000-000000000000", "cmt_field_path": [ "string" ], "cmt_parent": "cmt_parent", "cmt_user": "cmt_user", "cmt_body": "cmt_body", "cmt_mentions": [ { "id": "00000000-0000-0000-0000-000000000000", "name": "name" } ], "cmt_resolved_at": "2024-08-25T15:00:00Z", "cmt_resolved_by": "cmt_resolved_by", "cmt_author": {}, "cmt_resolved_by_name": "cmt_resolved_by_name" }
application/json

Generate one page

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/generate

Queues this page to be written from its brief and the site's knowledge, into the shape its content schema declares. Returns once the work is queued, not when the copy is ready: the page sits in generating and moves to draft when it lands.

The writer is the agent this page names in node_instructions_params.contentAgentId, falling back to agentId and then to the site's agent.

Generate one page › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

Generate one page › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Generate one page › Request Body optional

agentId
​string · uuid
proofread
​boolean
review
​boolean

Generate one page › Responses

Success

No data returned
POST/sites/{id}/pages/{page_id}/generate
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/generate \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "agentId": "00000000-0000-0000-0000-000000000000",
  "proofread": true,
  "review": true
}'
Example Request Body
{
  "agentId": "00000000-0000-0000-0000-000000000000",
  "proofread": true,
  "review": true
}
Example Responses
No example specified for this content type

Generate one field

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/generate-field

Rewrites one field of a page's structured content, optionally against an instruction, leaving the rest untouched. The page must already have structured content to regenerate a field from.

Generate one field › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

Generate one field › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Generate one field › Request Body

fieldPath
​string[] · required
agentId
​string · uuid
proofread
​boolean
review
​boolean
currentContent
​object
instruction
​string · maxLength: 2000

Generate one field › Responses

Success

No data returned
POST/sites/{id}/pages/{page_id}/generate-field
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/generate-field \ --request POST \ --header 'Content-Type: application/json' \ --header 'X-Tenant-Id: <string>' \ --header 'X-Api-Key: <api-key>' \ --data '{ "agentId": "00000000-0000-0000-0000-000000000000", "proofread": true, "review": true, "fieldPath": [ "string" ], "currentContent": {}, "instruction": "instruction" }'
Example Request Body
{
  "agentId": "00000000-0000-0000-0000-000000000000",
  "proofread": true,
  "review": true,
  "fieldPath": [
    "string"
  ],
  "currentContent": {},
  "instruction": "instruction"
}
Example Responses
No example specified for this content type

Generate a branch

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/generate-folder

The same as generating the whole site, scoped to one branch — every empty page beneath this node, however deeply nested. The node may be a folder or a page that has pages under it.

Generate a branch › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

Generate a branch › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Generate a branch › Request Body optional

agentId
​string · uuid
proofread
​boolean
review
​boolean

Generate a branch › Responses

Success

No data returned
POST/sites/{id}/pages/{page_id}/generate-folder
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/generate-folder \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "agentId": "00000000-0000-0000-0000-000000000000",
  "proofread": true,
  "review": true
}'
Example Request Body
{
  "agentId": "00000000-0000-0000-0000-000000000000",
  "proofread": true,
  "review": true
}
Example Responses
No example specified for this content type

Generate page meta data

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/generate-meta

Writes an SEO meta title (at most 60 characters) and meta description (at most 150) from the page's draft copy. The content template's metaTitleInstructions and metaDescriptionInstructions replace the platform defaults (see GET /content-schemas/meta-defaults); the page-level notes are appended. Returns the pair for review; nothing is saved until the page is. The page must hold draft content to write from. currentContent lets an editor pass unsaved copy; instructions overrides the saved page-level meta instructions for this one run.

Generate page meta data › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

Generate page meta data › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Generate page meta data › Request Body optional

currentContent
​object
instructions
​string · maxLength: 2000

Generate page meta data › Responses

Success

No data returned
POST/sites/{id}/pages/{page_id}/generate-meta
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/generate-meta \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "currentContent": {},
  "instructions": "instructions"
}'
Example Request Body
{
  "currentContent": {},
  "instructions": "instructions"
}
Example Responses
No example specified for this content type

Cancel page generation

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/generate/cancel

Frees a page stuck in generating, back to draft where it still holds copy and empty where it never had any, so it can be edited again. The queued job is not recalled: copy that lands later is still saved. Answers 409 when the page is not generating.

Requires either content:edit or content:generate.

Cancel page generation › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

Cancel page generation › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Cancel page generation › Responses

Success

One page or folder in a site's tree: what it is for, what has been written into it, and how far along that is.
Page
node_id
​string · uuid
node_created
​string · date-time
node_updated
​string · date-time
node_sitemap
​string
node_parent
​string
node_order
​integer
node_type
​string · enum

A page holds content and may have pages under it; a folder only holds pages. A page with content cannot become a folder.

Enum values:
page
folder
node_name
​string
node_slug
​string
node_instructions
​string
node_instructions_params
​string
node_children_instructions
​string
node_component
​string
node_content
​object
node_content_summary
​string
node_content_status
​string
node_state_item
​string
node_threads
​string
node_metadata
​object
​object

The page's SEO meta data. Filled by generation from the draft copy and editable; kept out of node_content so it is never pushed to a connected CMS.

​object

Reviewer material written alongside the copy: the review agent's account of the page when the site names one (review), and the editor agent's pass when the site names an editor (editor). Kept out of node_content so it never reaches a CMS. Left out of the site tree; read it from one page, or include=review.

node_assignee
​string | null · uuid

Who the page currently waits on. Set freely via PUT /sites/{id}/pages/{page_id}/assignee and carried unchanged through workflow moves unless the move says otherwise.

node_approved_at
​string | null · date-time

When the page was last signed off. A page carrying this and standing at draft has been edited since — the copy that was approved is still kept, and GET /sites/{id}/pages/{page_id}/approved-content answers with it.

node_approved_by
​string | null · uuid

Who signed it off. Null on pages approved before this was recorded.

node_open_comments
​integer

Unresolved comment threads on this page. Only sent when a site is fetched whole.

POST/sites/{id}/pages/{page_id}/generate/cancel
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/generate/cancel \
  --request POST \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
{ "node_id": "00000000-0000-0000-0000-000000000000", "node_created": "2024-08-25T15:00:00Z", "node_updated": "2024-08-25T15:00:00Z", "node_sitemap": "node_sitemap", "node_parent": "node_parent", "node_order": 0, "node_type": "page", "node_name": "node_name", "node_slug": "node_slug", "node_instructions": "node_instructions", "node_instructions_params": "node_instructions_params", "node_children_instructions": "node_children_instructions", "node_component": "node_component", "node_content": {}, "node_content_summary": "node_content_summary", "node_content_status": "node_content_status", "node_state_item": "node_state_item", "node_threads": "node_threads", "node_metadata": {}, "node_meta": { "title": "title", "description": "description" }, "node_review": { "review": { "text": "text", "generated_at": "2024-08-25T15:00:00Z", "agent_id": "00000000-0000-0000-0000-000000000000", "agent_name": "agent_name" }, "editor": { "agent_id": "00000000-0000-0000-0000-000000000000", "agent_name": "agent_name", "summary": "summary", "changes": [ { "field": "field", "what": "what", "why": "why", "applied": true } ], "proposed": {}, "notes": "notes", "changed": true, "reviewed_at": "2024-08-25T15:00:00Z", "error": "error" } }, "node_assignee": "00000000-0000-0000-0000-000000000000", "node_approved_at": "2024-08-25T15:00:00Z", "node_approved_by": "00000000-0000-0000-0000-000000000000", "node_open_comments": 0 }
application/json

List page history

GET
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/history

What has happened to a page, newest first: every workflow move, who made it, and the note they left, and every write into its copy with what made it in history_ref.source. Page content itself is omitted — this answers how a page got where it is, not what it said at each point, which GET /sites/{id}/pages/{page_id}/versions does.

List page history › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

List page history › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

List page history › Responses

Success

No data returned
GET/sites/{id}/pages/{page_id}/history
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/history \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
No example specified for this content type

Proofread a page

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/proofread

Runs the site's editor agent over the page's saved copy, in the background, without regenerating it. The editor reads the brief the page was written to and its own guideline knowledge, corrects wording that breaks them, and lists each change in editor.changes with the rule it serves. The structure is never touched: fields, blocks, images and links come back exactly as saved, only words change. Answers 202 with the page marked node_metadata.proofreading; the copy is not touched, so the page keeps its status and stays editable. The record lands under node_review.editor when the editor is done, or the failure under node_metadata.lastProofreadError, and either clears the mark; the corrected copy waits on the record as proposed and reaches node_content only when a caller applies it, marking each change applied. A page already being proofread is answered as it stands; one that is generating is refused with 409. The editor is the agent this page names in node_instructions_params.editorAgentId, then agentId, then the site's; a page that opted out of the automatic pass is still proofread here, since the request is the opt-in. 400 when no editor is set. instructions stands in for the page's editor notes for this one run.

Proofread a page › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

Proofread a page › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Proofread a page › Request Body optional

agentId
​string · uuid
instructions
​string · maxLength: 4000

Proofread a page › Responses

Success

One page or folder in a site's tree: what it is for, what has been written into it, and how far along that is.
Page
node_id
​string · uuid
node_created
​string · date-time
node_updated
​string · date-time
node_sitemap
​string
node_parent
​string
node_order
​integer
node_type
​string · enum

A page holds content and may have pages under it; a folder only holds pages. A page with content cannot become a folder.

Enum values:
page
folder
node_name
​string
node_slug
​string
node_instructions
​string
node_instructions_params
​string
node_children_instructions
​string
node_component
​string
node_content
​object
node_content_summary
​string
node_content_status
​string
node_state_item
​string
node_threads
​string
node_metadata
​object
​object

The page's SEO meta data. Filled by generation from the draft copy and editable; kept out of node_content so it is never pushed to a connected CMS.

​object

Reviewer material written alongside the copy: the review agent's account of the page when the site names one (review), and the editor agent's pass when the site names an editor (editor). Kept out of node_content so it never reaches a CMS. Left out of the site tree; read it from one page, or include=review.

node_assignee
​string | null · uuid

Who the page currently waits on. Set freely via PUT /sites/{id}/pages/{page_id}/assignee and carried unchanged through workflow moves unless the move says otherwise.

node_approved_at
​string | null · date-time

When the page was last signed off. A page carrying this and standing at draft has been edited since — the copy that was approved is still kept, and GET /sites/{id}/pages/{page_id}/approved-content answers with it.

node_approved_by
​string | null · uuid

Who signed it off. Null on pages approved before this was recorded.

node_open_comments
​integer

Unresolved comment threads on this page. Only sent when a site is fetched whole.

POST/sites/{id}/pages/{page_id}/proofread
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/proofread \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "agentId": "00000000-0000-0000-0000-000000000000",
  "instructions": "instructions"
}'
Example Request Body
{
  "agentId": "00000000-0000-0000-0000-000000000000",
  "instructions": "instructions"
}
Example Responses
{ "node_id": "00000000-0000-0000-0000-000000000000", "node_created": "2024-08-25T15:00:00Z", "node_updated": "2024-08-25T15:00:00Z", "node_sitemap": "node_sitemap", "node_parent": "node_parent", "node_order": 0, "node_type": "page", "node_name": "node_name", "node_slug": "node_slug", "node_instructions": "node_instructions", "node_instructions_params": "node_instructions_params", "node_children_instructions": "node_children_instructions", "node_component": "node_component", "node_content": {}, "node_content_summary": "node_content_summary", "node_content_status": "node_content_status", "node_state_item": "node_state_item", "node_threads": "node_threads", "node_metadata": {}, "node_meta": { "title": "title", "description": "description" }, "node_review": { "review": { "text": "text", "generated_at": "2024-08-25T15:00:00Z", "agent_id": "00000000-0000-0000-0000-000000000000", "agent_name": "agent_name" }, "editor": { "agent_id": "00000000-0000-0000-0000-000000000000", "agent_name": "agent_name", "summary": "summary", "changes": [ { "field": "field", "what": "what", "why": "why", "applied": true } ], "proposed": {}, "notes": "notes", "changed": true, "reviewed_at": "2024-08-25T15:00:00Z", "error": "error" } }, "node_assignee": "00000000-0000-0000-0000-000000000000", "node_approved_at": "2024-08-25T15:00:00Z", "node_approved_by": "00000000-0000-0000-0000-000000000000", "node_open_comments": 0 }
application/json

Upload a reference doc

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/reference-docs

Takes one document as a multipart upload (files field) and stores it as reference material for the page. Put the returned file_id into an entry of the page's node_instructions_params.referenceUrls ({fileId, name, note?}) and generation reads the document the way it reads a reference link. PDF, Word, PowerPoint, Excel, CSV, plain text and Markdown are accepted.

Upload a reference doc › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

Upload a reference doc › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Upload a reference doc › Responses

Created

file_id
​string · uuid · required
file_name
​string · required
file_type
​string · required
file_size
​integer
POST/sites/{id}/pages/{page_id}/reference-docs
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/reference-docs \
  --request POST \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
{
  "file_id": "00000000-0000-0000-0000-000000000000",
  "file_name": "file_name",
  "file_type": "file_type",
  "file_size": 0
}
application/json

Delete a reference doc

DELETE
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/reference-docs/{file_id}

Removes a reference document that was uploaded for this page, bytes included. Only files stored as this page's reference material can be addressed here; the matching node_instructions_params.referenceUrls entry is the caller's to clean up.

Delete a reference doc › path Parameters

file_id
​string · uuid · required
id
​string · uuid · required
page_id
​string · uuid · required

Delete a reference doc › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Delete a reference doc › Responses

Success. No content.

No data returned
DELETE/sites/{id}/pages/{page_id}/reference-docs/{file_id}
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/reference-docs/:file_id \
  --request DELETE \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
No example specified for this content type

Reorder a page

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/reorder

Moves a page to a position under a parent, carrying everything nested under it. node_parent must name a page of the same site outside the moved page's own subtree; anything else is refused. Both affected sibling groups are renumbered contiguously from 0, so a tree that arrived with gapped or duplicated ordering comes back consistent.

A move already rewrites the page's address, so a slug the destination has spoken for is numbered off rather than refused. Read node_slug off the response for what the page ended up at.

Reorder a page › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

Reorder a page › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Reorder a page › Request Body

node_order
​integer · required
node_parent
​string · uuid

Reorder a page › Responses

Success

No data returned
POST/sites/{id}/pages/{page_id}/reorder
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/reorder \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "node_parent": "00000000-0000-0000-0000-000000000000",
  "node_order": 0
}'
Example Request Body
{
  "node_parent": "00000000-0000-0000-0000-000000000000",
  "node_order": 0
}
Example Responses
No example specified for this content type

Restore approved copy

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/restore-approved

Discards the edits made since the last sign-off and puts that copy back, returning the page to approved. Nothing unreviewed reaches a website this way — the content written is exactly what was approved — which is why it takes content:edit rather than a review permission. Where the site is under review this ends a review in progress: the page leaves whichever step it had reached and lands back on the final one.

Restore approved copy › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

Restore approved copy › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Restore approved copy › Responses

Success

One page or folder in a site's tree: what it is for, what has been written into it, and how far along that is.
Page
node_id
​string · uuid
node_created
​string · date-time
node_updated
​string · date-time
node_sitemap
​string
node_parent
​string
node_order
​integer
node_type
​string · enum

A page holds content and may have pages under it; a folder only holds pages. A page with content cannot become a folder.

Enum values:
page
folder
node_name
​string
node_slug
​string
node_instructions
​string
node_instructions_params
​string
node_children_instructions
​string
node_component
​string
node_content
​object
node_content_summary
​string
node_content_status
​string
node_state_item
​string
node_threads
​string
node_metadata
​object
​object

The page's SEO meta data. Filled by generation from the draft copy and editable; kept out of node_content so it is never pushed to a connected CMS.

​object

Reviewer material written alongside the copy: the review agent's account of the page when the site names one (review), and the editor agent's pass when the site names an editor (editor). Kept out of node_content so it never reaches a CMS. Left out of the site tree; read it from one page, or include=review.

node_assignee
​string | null · uuid

Who the page currently waits on. Set freely via PUT /sites/{id}/pages/{page_id}/assignee and carried unchanged through workflow moves unless the move says otherwise.

node_approved_at
​string | null · date-time

When the page was last signed off. A page carrying this and standing at draft has been edited since — the copy that was approved is still kept, and GET /sites/{id}/pages/{page_id}/approved-content answers with it.

node_approved_by
​string | null · uuid

Who signed it off. Null on pages approved before this was recorded.

node_open_comments
​integer

Unresolved comment threads on this page. Only sent when a site is fetched whole.

POST/sites/{id}/pages/{page_id}/restore-approved
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/restore-approved \
  --request POST \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
{ "node_id": "00000000-0000-0000-0000-000000000000", "node_created": "2024-08-25T15:00:00Z", "node_updated": "2024-08-25T15:00:00Z", "node_sitemap": "node_sitemap", "node_parent": "node_parent", "node_order": 0, "node_type": "page", "node_name": "node_name", "node_slug": "node_slug", "node_instructions": "node_instructions", "node_instructions_params": "node_instructions_params", "node_children_instructions": "node_children_instructions", "node_component": "node_component", "node_content": {}, "node_content_summary": "node_content_summary", "node_content_status": "node_content_status", "node_state_item": "node_state_item", "node_threads": "node_threads", "node_metadata": {}, "node_meta": { "title": "title", "description": "description" }, "node_review": { "review": { "text": "text", "generated_at": "2024-08-25T15:00:00Z", "agent_id": "00000000-0000-0000-0000-000000000000", "agent_name": "agent_name" }, "editor": { "agent_id": "00000000-0000-0000-0000-000000000000", "agent_name": "agent_name", "summary": "summary", "changes": [ { "field": "field", "what": "what", "why": "why", "applied": true } ], "proposed": {}, "notes": "notes", "changed": true, "reviewed_at": "2024-08-25T15:00:00Z", "error": "error" } }, "node_assignee": "00000000-0000-0000-0000-000000000000", "node_approved_at": "2024-08-25T15:00:00Z", "node_approved_by": "00000000-0000-0000-0000-000000000000", "node_open_comments": 0 }
application/json

Write a page review

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/review

Writes the page's review from its saved copy, in the background: where each fact, figure and claim came from, the search terms and questions the page covers, and the trademarks and acronyms it uses, following the site's review instructions (see GET /sites/{id}/generation-defaults) with the page's notes appended. The writer is the review agent this page names in node_instructions_params.reviewAgentId, then agentId, then the site's; any of those may be the word copywriter, meaning the agent that writes the page reviews it too, which is the one that can trace a fact to its source. Generation writes the review on its own when the site names a review agent; this is for a page written before that, or edited since. Answers 202 with the page marked node_metadata.reviewing; the copy is not touched, so the page keeps its status and stays editable. The review lands under node_review.review when the agent is done, or the failure under node_metadata.lastReviewError, and either clears the mark. A page already being reviewed is answered as it stands. 400 when no review agent is set. instructions stands in for the page's saved review notes for this one run.

Write a page review › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

Write a page review › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Write a page review › Request Body optional

agentId
​string · uuid
instructions
​string · maxLength: 4000

Write a page review › Responses

Success

One page or folder in a site's tree: what it is for, what has been written into it, and how far along that is.
Page
node_id
​string · uuid
node_created
​string · date-time
node_updated
​string · date-time
node_sitemap
​string
node_parent
​string
node_order
​integer
node_type
​string · enum

A page holds content and may have pages under it; a folder only holds pages. A page with content cannot become a folder.

Enum values:
page
folder
node_name
​string
node_slug
​string
node_instructions
​string
node_instructions_params
​string
node_children_instructions
​string
node_component
​string
node_content
​object
node_content_summary
​string
node_content_status
​string
node_state_item
​string
node_threads
​string
node_metadata
​object
​object

The page's SEO meta data. Filled by generation from the draft copy and editable; kept out of node_content so it is never pushed to a connected CMS.

​object

Reviewer material written alongside the copy: the review agent's account of the page when the site names one (review), and the editor agent's pass when the site names an editor (editor). Kept out of node_content so it never reaches a CMS. Left out of the site tree; read it from one page, or include=review.

node_assignee
​string | null · uuid

Who the page currently waits on. Set freely via PUT /sites/{id}/pages/{page_id}/assignee and carried unchanged through workflow moves unless the move says otherwise.

node_approved_at
​string | null · date-time

When the page was last signed off. A page carrying this and standing at draft has been edited since — the copy that was approved is still kept, and GET /sites/{id}/pages/{page_id}/approved-content answers with it.

node_approved_by
​string | null · uuid

Who signed it off. Null on pages approved before this was recorded.

node_open_comments
​integer

Unresolved comment threads on this page. Only sent when a site is fetched whole.

POST/sites/{id}/pages/{page_id}/review
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/review \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "agentId": "00000000-0000-0000-0000-000000000000",
  "instructions": "instructions"
}'
Example Request Body
{
  "agentId": "00000000-0000-0000-0000-000000000000",
  "instructions": "instructions"
}
Example Responses
{ "node_id": "00000000-0000-0000-0000-000000000000", "node_created": "2024-08-25T15:00:00Z", "node_updated": "2024-08-25T15:00:00Z", "node_sitemap": "node_sitemap", "node_parent": "node_parent", "node_order": 0, "node_type": "page", "node_name": "node_name", "node_slug": "node_slug", "node_instructions": "node_instructions", "node_instructions_params": "node_instructions_params", "node_children_instructions": "node_children_instructions", "node_component": "node_component", "node_content": {}, "node_content_summary": "node_content_summary", "node_content_status": "node_content_status", "node_state_item": "node_state_item", "node_threads": "node_threads", "node_metadata": {}, "node_meta": { "title": "title", "description": "description" }, "node_review": { "review": { "text": "text", "generated_at": "2024-08-25T15:00:00Z", "agent_id": "00000000-0000-0000-0000-000000000000", "agent_name": "agent_name" }, "editor": { "agent_id": "00000000-0000-0000-0000-000000000000", "agent_name": "agent_name", "summary": "summary", "changes": [ { "field": "field", "what": "what", "why": "why", "applied": true } ], "proposed": {}, "notes": "notes", "changed": true, "reviewed_at": "2024-08-25T15:00:00Z", "error": "error" } }, "node_assignee": "00000000-0000-0000-0000-000000000000", "node_approved_at": "2024-08-25T15:00:00Z", "node_approved_by": "00000000-0000-0000-0000-000000000000", "node_open_comments": 0 }
application/json

Set the page status

PATCH
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/status

Sets the status by hand: empty, draft, approved or pushed, from any status but generating, which belongs to the worker until the copy lands or the run is cancelled. A page needs no copy to be approved; marking one approved as it stands is how a page that wants no new text is signed off. empty discards whatever content the page holds. Approval is what marks content ready to leave the platform, so it is the gate a CMS push checks. Where the site has an approval workflow, both ends of the sign-off are that workflow's moves to make and this endpoint answers 409 for either: approving (or publishing) a page, and withdrawing an approval by sending it back to draft or empty.

Set the page status › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

Set the page status › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Set the page status › Request Body

status
​string · enum · required
Enum values:
empty
draft
approved
pushed

Set the page status › Responses

Success

No data returned
PATCH/sites/{id}/pages/{page_id}/status
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/status \
  --request PATCH \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "status": "empty"
}'
Example Request Body
{
  "status": "empty"
}
Example Responses
No example specified for this content type

List page versions

GET
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/versions

Every version of the page's copy on record, newest first, each reduced to what changed against the one before it: the name, the slug, the content and the metadata, with the status and review step where the write moved them. history_ref.source says what wrote it: a save, a generation, a proofread, a CMS pull or merge, an import, a restore, a discard. The state a page held before its first recorded write is kept once, unattributed, as the oldest entry, so the earliest diff is against what the page really held. At most 100 entries come back.

List page versions › path Parameters

id
​string · uuid · required
page_id
​string · uuid · required

List page versions › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

List page versions › Responses

Success

​object[]
history_id
​string · uuid
history_timestamp
​string · date-time
history_user
​string | null · uuid
history_op
​string

The kind of change — insert, update or delete.

​object

One entry per field that changed, keyed by column name.

history_ref
​object

What the writer noted about the change rather than the columns: the action that made it, or the reason it was made.

GET/sites/{id}/pages/{page_id}/versions
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/versions \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
[ { "history_id": "00000000-0000-0000-0000-000000000000", "history_timestamp": "2024-08-25T15:00:00Z", "history_user": "00000000-0000-0000-0000-000000000000", "history_op": "history_op", "history_changeset": { "key": { "from": {}, "to": {} } }, "history_ref": {} } ]
application/json

Revert page to version

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/{page_id}/versions/{history_id}/revert

Puts the content and metadata of a version back on the page, and records the restore as a version of its own so it can be undone in turn. The name and slug a version carried are shown but not written back. Restoring is an edit: the page returns to draft, an approval it held is withdrawn and, under review, it re-enters at the flow's first step. The approved copy is untouched. 409 while the page is being generated.

Revert page to version › path Parameters

history_id
​string · uuid · required
id
​string · uuid · required
page_id
​string · uuid · required

Revert page to version › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Revert page to version › Responses

Success

restored
​string[]

Columns written back, by name.

​object[]

Columns the version carried that were deliberately not written.

POST/sites/{id}/pages/{page_id}/versions/{history_id}/revert
curl https://api.genuineai.app/api/v1/sites/:id/pages/:page_id/versions/:history_id/revert \
  --request POST \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
{
  "restored": [
    "string"
  ],
  "skipped": [
    {
      "field": "field",
      "reason": "unrecorded",
      "detail": "detail"
    }
  ]
}
application/json

Generate pages

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/actions/generate

The same as generating the whole site, scoped to the pages named in page_ids — a hand-picked set rather than a branch. Pages already holding content, and pages with no instructions of their own or inherited, sit the run out rather than failing it; queued reports how many were taken.

Generate pages › path Parameters

id
​string · uuid · required

Generate pages › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Generate pages › Request Body

page_ids
​string[] · required
agentId
​string · uuid
proofread
​boolean
review
​boolean

Generate pages › Responses

Success

No data returned
POST/sites/{id}/pages/actions/generate
curl https://api.genuineai.app/api/v1/sites/:id/pages/actions/generate \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "page_ids": [
    "00000000-0000-0000-0000-000000000000"
  ],
  "agentId": "00000000-0000-0000-0000-000000000000",
  "proofread": true,
  "review": true
}'
Example Request Body
{
  "page_ids": [
    "00000000-0000-0000-0000-000000000000"
  ],
  "agentId": "00000000-0000-0000-0000-000000000000",
  "proofread": true,
  "review": true
}
Example Responses
No example specified for this content type

Move pages

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/actions/move

Puts every page named in page_ids under node_parent (top level when null), after whatever is already there and in the order they hold in the tree. A page whose ancestor is also named travels with it and keeps its place, so a selected branch arrives intact. node_parent must be a page of the same site outside every moved subtree; anything else is refused.

A move already rewrites a page's address, so a slug the destination has spoken for is numbered off rather than refused; renamedCount says how many were. Pages the site does not hold are reported back rather than failing the rest.

Move pages › path Parameters

id
​string · uuid · required

Move pages › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Move pages › Request Body

page_ids
​string[] · required
node_parent
​string · uuid

Move pages › Responses

Success

No data returned
POST/sites/{id}/pages/actions/move
curl https://api.genuineai.app/api/v1/sites/:id/pages/actions/move \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "page_ids": [
    "00000000-0000-0000-0000-000000000000"
  ],
  "node_parent": "00000000-0000-0000-0000-000000000000"
}'
Example Request Body
{
  "page_ids": [
    "00000000-0000-0000-0000-000000000000"
  ],
  "node_parent": "00000000-0000-0000-0000-000000000000"
}
Example Responses
No example specified for this content type

Proofread pages

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/actions/proofread

Runs the editor agent over the pages named in page_ids, in the background, the way generation runs: each page sits in generating while the editor works and returns to draft with its copy corrected and the editor's record on its review. Corrections are applied outright rather than proposed, so only draft pages holding copy are taken; empty, generating, approved and published pages sit the run out, as do folders. queued reports how many were taken. The editor is each page's own, then agentId, then the site's; a page that opted out of the automatic pass is still proofread, since the request is the opt-in. 400 when none of the pages has an editor.

Proofread pages › path Parameters

id
​string · uuid · required

Proofread pages › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Proofread pages › Request Body

page_ids
​string[] · required
agentId
​string · uuid

Proofread pages › Responses

Success

No data returned
POST/sites/{id}/pages/actions/proofread
curl https://api.genuineai.app/api/v1/sites/:id/pages/actions/proofread \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "page_ids": [
    "00000000-0000-0000-0000-000000000000"
  ],
  "agentId": "00000000-0000-0000-0000-000000000000"
}'
Example Request Body
{
  "page_ids": [
    "00000000-0000-0000-0000-000000000000"
  ],
  "agentId": "00000000-0000-0000-0000-000000000000"
}
Example Responses
No example specified for this content type

Review pages

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/actions/review

Writes the review of each page named in page_ids, in the background, one task per page. Unlike generation or proofreading the copy is not touched, so a page keeps its status and stays editable; node_metadata.reviewing marks it until the review lands under node_review.review. Pages with copy (draft, approved or published) are taken; empty and generating pages, folders and pages already being reviewed sit the run out. The reviewer is each page's own, then agentId, then the site's, with copywriter meaning whoever writes that page. queued reports how many were taken; 400 when none of the pages has a review agent. Each review costs a full writer-sized call.

Review pages › path Parameters

id
​string · uuid · required

Review pages › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Review pages › Request Body

page_ids
​string[] · required
agentId
​string

Review pages › Responses

Success

No data returned
POST/sites/{id}/pages/actions/review
curl https://api.genuineai.app/api/v1/sites/:id/pages/actions/review \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "page_ids": [
    "00000000-0000-0000-0000-000000000000"
  ],
  "agentId": "agentId"
}'
Example Request Body
{
  "page_ids": [
    "00000000-0000-0000-0000-000000000000"
  ],
  "agentId": "agentId"
}
Example Responses
No example specified for this content type

Set page statuses

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/actions/status

Sets the status of every page named in page_ids under the rules of setting one page's status. A page already holding it counts as done. One that is generating, or whose move the site's approval workflow reserves, is reported back with the reason rather than failing the rest, as is a page the site does not hold.

Set page statuses › path Parameters

id
​string · uuid · required

Set page statuses › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Set page statuses › Request Body

page_ids
​string[] · required
status
​string · enum · required
Enum values:
empty
draft
approved
pushed

Set page statuses › Responses

Success

No data returned
POST/sites/{id}/pages/actions/status
curl https://api.genuineai.app/api/v1/sites/:id/pages/actions/status \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "page_ids": [
    "00000000-0000-0000-0000-000000000000"
  ],
  "status": "empty"
}'
Example Request Body
{
  "page_ids": [
    "00000000-0000-0000-0000-000000000000"
  ],
  "status": "empty"
}
Example Responses
No example specified for this content type

Move pages

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/actions/transition

Puts pages on a step of the site's approval workflow — any step, in either direction, from wherever they stand. The step's own gate decides who may set it (403 when it does not name the caller); pages the caller cannot write are reported per page rather than refusing the rest. Reaching the final step is what marks a page approved; leaving it withdraws that. The note is kept on each page's history and sent to whoever the page waits on — its assignee, or whoever can act on it next while unassigned. Passing assignee_id hands the pages over in the same move (null clears); leaving it out keeps every page with whoever holds it.

Move pages › path Parameters

id
​string · uuid · required

Move pages › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Move pages › Request Body

page_ids
​string[] · required
target_state_id
​string · uuid · required
note
​string · maxLength: 2000
assignee_id
​string · uuid

Move pages › Responses

Success

No data returned
POST/sites/{id}/pages/actions/transition
curl https://api.genuineai.app/api/v1/sites/:id/pages/actions/transition \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "page_ids": [
    "00000000-0000-0000-0000-000000000000"
  ],
  "target_state_id": "00000000-0000-0000-0000-000000000000",
  "note": "note",
  "assignee_id": "00000000-0000-0000-0000-000000000000"
}'
Example Request Body
{
  "page_ids": [
    "00000000-0000-0000-0000-000000000000"
  ],
  "target_state_id": "00000000-0000-0000-0000-000000000000",
  "note": "note",
  "assignee_id": "00000000-0000-0000-0000-000000000000"
}
Example Responses
No example specified for this content type

Update page settings

POST
https://api.genuineai.app/api/v1
/sites/{id}/pages/actions/update-settings

Writes generation settings onto every page named in page_ids at once: content instructions, content template, audience profiles, tone and copywriter agent. Only the fields present in the body are touched, and null (or an empty list) clears a field, returning the page to what it inherits from its folder or the site. Everything else a page carries, its content and review state included, is left alone. Pages the site does not hold are reported back rather than failing the rest.

Update page settings › path Parameters

id
​string · uuid · required

Update page settings › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Update page settings › Request Body

page_ids
​string[] · required
node_instructions
​string · maxLength: 20000
template_id
​string · uuid
audience_profile_ids
​string[]
tone_id
​string · maxLength: 200
content_agent_id
​string · uuid
editor_agent_id
​string · uuid
editor_skipped
​boolean
review_agent_id
​string

Update page settings › Responses

Success

No data returned
POST/sites/{id}/pages/actions/update-settings
curl https://api.genuineai.app/api/v1/sites/:id/pages/actions/update-settings \ --request POST \ --header 'Content-Type: application/json' \ --header 'X-Tenant-Id: <string>' \ --header 'X-Api-Key: <api-key>' \ --data '{ "page_ids": [ "00000000-0000-0000-0000-000000000000" ], "node_instructions": "node_instructions", "template_id": "00000000-0000-0000-0000-000000000000", "audience_profile_ids": [ "string" ], "tone_id": "tone_id", "content_agent_id": "00000000-0000-0000-0000-000000000000", "editor_agent_id": "00000000-0000-0000-0000-000000000000", "editor_skipped": true, "review_agent_id": "review_agent_id" }'
Example Request Body
{ "page_ids": [ "00000000-0000-0000-0000-000000000000" ], "node_instructions": "node_instructions", "template_id": "00000000-0000-0000-0000-000000000000", "audience_profile_ids": [ "string" ], "tone_id": "tone_id", "content_agent_id": "00000000-0000-0000-0000-000000000000", "editor_agent_id": "00000000-0000-0000-0000-000000000000", "editor_skipped": true, "review_agent_id": "review_agent_id" }
Example Responses
No example specified for this content type

Reset stuck pages

POST
https://api.genuineai.app/api/v1
/sites/{id}/reset-generating

Frees any page left in generating for more than 15 minutes, back to draft where it still holds copy and empty where it never had any. A safety valve for the case where the background job exhausted its retries and the page would otherwise never come back.

Reset stuck pages › path Parameters

id
​string · uuid · required

Reset stuck pages › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Reset stuck pages › Responses

Success

No data returned
POST/sites/{id}/reset-generating
curl https://api.genuineai.app/api/v1/sites/:id/reset-generating \
  --request POST \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>'
Example Responses
No example specified for this content type

Discover pages

POST
https://api.genuineai.app/api/v1
/sites/import/discover

Given a URL, finds its pages: robots.txt, then the well-known sitemap paths (expanding sitemap indexes), then a homepage navigation crawl as a fallback. Runs server-side because a browser cannot fetch another origin. Returns the URLs it found without writing anything.

Discover pages › Headers

X-Tenant-Id
​string · uuid · required

The workspace this request acts in. Every row the API returns is isolated to it at the database layer by row-level security.

Discover pages › Request Body

url
​string · minLength: 3 · maxLength: 2048 · required

Discover pages › Responses

Success

No data returned
POST/sites/import/discover
curl https://api.genuineai.app/api/v1/sites/import/discover \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <string>' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "url": "url"
}'
Example Request Body
{
  "url": "url"
}
Example Responses
No example specified for this content type

Presentation templatesContent schemas