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
    Content schemas
    Content approval flows
    CMS sync
      Get the CMS connectiongetDisconnect the CMSdeleteGet sync automationgetSet sync automationputConnect a CMSpostPreview a syncgetPull contentpostPush contentpostScan for changespostPull content schemaspostPush content schemaspostList sync runsgetGet a sync rungetCancel a sync runpostResume a sync runpostRetry failed pagespostCreate a change webhookpostDelete a change webhookdeleteCompare a pagegetMerge a pagepostPull one pagepostPush one pagepostList CMS providersget
    Social
Automation
Customer channels
Administration
Schemas
GenuineAI API
GenuineAI API

CMS sync

Two-way sync between a site's content and an external CMS.


Get the CMS connection

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

Returns {connected: false} when the site has no connection, so this is safe to call before one is set up.

Get the CMS connection › path Parameters

id
​string · uuid · required

Get the CMS connection › 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 CMS connection › Responses

Success

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

Disconnect the CMS

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

Forgets the connection and the per-page links to it. Content is kept on both sides — this stops syncing, it does not delete anything.

Disconnect the CMS › path Parameters

id
​string · uuid · required

Disconnect the CMS › 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.

Disconnect the CMS › Responses

Success

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

Get sync automation

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

What this site does about its CMS unattended: whether the nightly comparison covers it, what it may pull without being asked, and the URL the CMS calls when its content changes.

Get sync automation › path Parameters

id
​string · uuid · required

Get sync automation › 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 sync automation › Responses

Success

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

Set sync automation

PUT
https://api.genuineai.app/api/v1
/sites/{id}/cms/automation

Sets the standing rules. autoPull is off, unedited (pages the CMS changed that nobody has touched here since we last sent them) or all; a page that changed on both sides is never pulled automatically under any of them. Nothing here ever pushes — copy reaching a live website stays a person's decision.

Set sync automation › path Parameters

id
​string · uuid · required

Set sync automation › 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 sync automation › Request Body optional

scan
​boolean
autoPull
​string · enum
Enum values:
off
unedited
all
notify
​string · enum
Enum values:
off
changes

Set sync automation › Responses

Success

No data returned
PUT/sites/{id}/cms/automation
curl https://api.genuineai.app/api/v1/sites/:id/cms/automation \ --request PUT \ --header 'Content-Type: application/json' \ --header 'X-Tenant-Id: <string>' \ --header 'X-Api-Key: <api-key>' \ --data '{ "scan": true, "autoPull": "off", "notify": "off" }'
Example Request Body
{ "scan": true, "autoPull": "off", "notify": "off" }
json
Example Responses
No example specified for this content type

Connect a CMS

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

Stores the credentials for a CMS and verifies them against it. Nothing is synced yet — pull or push afterward.

Connect a CMS › path Parameters

id
​string · uuid · required

Connect a CMS › 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.

Connect a CMS › Request Body

provider
​string · enum · required
Enum values:
storyblok
aem
settings
​object · required
dryRun
​boolean

Connect a CMS › Responses

Success

No data returned
POST/sites/{id}/cms/connect
curl https://api.genuineai.app/api/v1/sites/:id/cms/connect \ --request POST \ --header 'Content-Type: application/json' \ --header 'X-Tenant-Id: <string>' \ --header 'X-Api-Key: <api-key>' \ --data '{ "provider": "storyblok", "settings": {}, "dryRun": true }'
Example Request Body
{ "provider": "storyblok", "settings": {}, "dryRun": true }
json
Example Responses
No example specified for this content type

Preview a sync

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

Compares both sides and returns the plan — what would be created, updated, and what conflicts. Read-only: nothing moves until you pull or push.

Preview a sync › path Parameters

id
​string · uuid · required

Preview a sync › query Parameters

direction
​string · enum
Enum values:
pull
push
both

Preview a sync › 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.

Preview a sync › Responses

Success

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

Pull content

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

Brings the CMS's content into the site. Preview it first with GET /sites/{id}/cms/plan, which reports what a pull would change without changing it.

A pull touching more than a handful of pages answers 202 with a job instead of the result: hundreds of rate-limited calls to a CMS take minutes, which is longer than any request should be held open. Follow the run at GET /sites/{id}/cms/sync-jobs/{job_id}. Send background: true to be given a job whatever the size.

Pull content › path Parameters

id
​string · uuid · required

Pull content › 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.

Pull content › Request Body optional

resolution
​string · enum
Enum values:
manual
local
remote
nodeIds
​string[]
externalIds
​string[]
​array
publish
​boolean
force
​boolean
background
​boolean
dryRun
​boolean

Pull content › Responses

Success

No data returned
POST/sites/{id}/cms/pull
curl https://api.genuineai.app/api/v1/sites/:id/cms/pull \ --request POST \ --header 'Content-Type: application/json' \ --header 'X-Tenant-Id: <string>' \ --header 'X-Api-Key: <api-key>' \ --data '{ "resolution": "manual", "nodeIds": [ "00000000-0000-0000-0000-000000000000" ], "externalIds": [ "string" ], "statuses": [ {} ], "publish": true, "force": true, "background": true, "dryRun": true }'
Example Request Body
{ "resolution": "manual", "nodeIds": [ "00000000-0000-0000-0000-000000000000" ], "externalIds": [ "string" ], "statuses": [ {} ], "publish": true, "force": true, "background": true, "dryRun": true }
json
Example Responses
No example specified for this content type

Push content

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

Sends the site's content to the CMS. Local edits win over anything changed in the CMS since the last sync; the plan endpoint reports those conflicts before you commit to them.

Answers 202 with a job once the push is larger than a handful of pages — see pullFromCms.

Push content › path Parameters

id
​string · uuid · required

Push content › 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.

Push content › Request Body optional

resolution
​string · enum
Enum values:
manual
local
remote
nodeIds
​string[]
externalIds
​string[]
​array
publish
​boolean
force
​boolean
background
​boolean
dryRun
​boolean

Push content › Responses

Success

No data returned
POST/sites/{id}/cms/push
curl https://api.genuineai.app/api/v1/sites/:id/cms/push \ --request POST \ --header 'Content-Type: application/json' \ --header 'X-Tenant-Id: <string>' \ --header 'X-Api-Key: <api-key>' \ --data '{ "resolution": "manual", "nodeIds": [ "00000000-0000-0000-0000-000000000000" ], "externalIds": [ "string" ], "statuses": [ {} ], "publish": true, "force": true, "background": true, "dryRun": true }'
Example Request Body
{ "resolution": "manual", "nodeIds": [ "00000000-0000-0000-0000-000000000000" ], "externalIds": [ "string" ], "statuses": [ {} ], "publish": true, "force": true, "background": true, "dryRun": true }
json
Example Responses
No example specified for this content type

Scan for changes

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

Same comparison as the plan, and it remembers the verdict on the site so the page tree can show where the two sides disagree without comparing again. Use the plan endpoint for a read that leaves nothing behind.

Scan for changes › path Parameters

id
​string · uuid · required

Scan for changes › query Parameters

direction
​string · enum
Enum values:
pull
push
both

Scan for changes › 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.

Scan for changes › Responses

Success

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

Pull content schemas

POST
https://api.genuineai.app/api/v1
/sites/{id}/cms/schemas/pull

Mirrors the CMS's component definitions into a content-schema library, so generated content is shaped the way the CMS expects it.

Pull content schemas › path Parameters

id
​string · uuid · required

Pull content schemas › 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.

Pull content schemas › Responses

Success

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

Push content schemas

POST
https://api.genuineai.app/api/v1
/sites/{id}/cms/schemas/push

Sends the workspace's content schemas to the CMS as component definitions, so pages pushed afterward have somewhere to land.

Push content schemas › path Parameters

id
​string · uuid · required

Push content schemas › 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.

Push content schemas › Request Body optional

templateIds
​string[]

Push content schemas › Responses

Success

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

List sync runs

GET
https://api.genuineai.app/api/v1
/sites/{id}/cms/sync-jobs

Every recent pull and push for this site, newest first: who ran it, what it moved, how long it took and which pages it could not settle.

List sync runs › path Parameters

id
​string · uuid · required

List sync runs › query Parameters

limit
​integer · min: 1 · max: 100

List sync runs › 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 sync runs › Responses

Success

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

Get a sync run

GET
https://api.genuineai.app/api/v1
/sites/{id}/cms/sync-jobs/{job_id}

Progress and outcome of one run. done and failed advance against total while it works; stalled reports a run whose worker was lost, which can be resumed without losing what it applied.

Get a sync run › path Parameters

id
​string · uuid · required
job_id
​string · uuid · required

Get a sync run › 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 sync run › Responses

Success

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

Cancel a sync run

POST
https://api.genuineai.app/api/v1
/sites/{id}/cms/sync-jobs/{job_id}/cancel

Stops the run at its next checkpoint. Pages already synced stay synced — this is a stop, not a rollback.

Cancel a sync run › path Parameters

id
​string · uuid · required
job_id
​string · uuid · required

Cancel a sync run › 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 a sync run › Responses

Success

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

Resume a sync run

POST
https://api.genuineai.app/api/v1
/sites/{id}/cms/sync-jobs/{job_id}/resume

Continues a run that stalled or failed, from its last checkpoint. Pages it already settled are not touched again.

Resume a sync run › path Parameters

id
​string · uuid · required
job_id
​string · uuid · required

Resume a sync run › 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.

Resume a sync run › Responses

Success

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

Retry failed pages

POST
https://api.genuineai.app/api/v1
/sites/{id}/cms/sync-jobs/{job_id}/retry-failed

Starts a new run over exactly the pages the named run could not settle. The original run keeps its record.

Retry failed pages › path Parameters

id
​string · uuid · required
job_id
​string · uuid · required

Retry failed 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.

Retry failed pages › Responses

Success

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

Create a change webhook

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

Issues the URL to register in the CMS, so a change there is noticed in seconds rather than at the next scheduled comparison. Called again it replaces the URL, which is how one is rotated. Send secret to have deliveries checked against the provider's signature. Refused where the connected CMS cannot call a URL.

Create a change webhook › path Parameters

id
​string · uuid · required

Create a change webhook › 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 change webhook › Request Body optional

secret
​string · maxLength: 200

Create a change webhook › Responses

Success

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

Delete a change webhook

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

Withdraws the URL. Deliveries to it stop being accepted immediately; the site falls back to the scheduled comparison.

Delete a change webhook › path Parameters

id
​string · uuid · required

Delete a change webhook › 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 change webhook › Responses

Success

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

Compare a page

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

Both versions of one page, field by field, with the differing fields first — what a conflict needs in order to be decidable.

Compare a page › path Parameters

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

Compare 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.

Compare a page › Responses

Success

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

Merge a page

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

Settles a page that changed on both sides by keeping each field from the side named in choices — {"title": "local", "body": "remote"}. Fields nobody names keep the local value; _name, _slug and _text name the three that are not content fields. The merged copy is written here and then sent to the CMS under the same approval gate as any push, so on a site under review it waits for a reviewer and the response reports awaitingApproval.

Merge a page › path Parameters

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

Merge 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.

Merge a page › Request Body

choices
​object · required
publish
​boolean

Merge a page › Responses

Success

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

Pull one page

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

Replaces the local copy of this page with the CMS's. The local version is discarded.

Pull one page › path Parameters

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

Pull 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.

Pull one page › Responses

Success

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

Push one page

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

Pushes a single page named explicitly. force bypasses the approval gate and the nothing-changed check, since the caller asked for this page by name; a genuine conflict is still reported unless resolution says how to settle it.

Push one page › path Parameters

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

Push 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.

Push one page › Responses

Success

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

List CMS providers

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

The providers a site can connect to, and the fields each one needs to connect. Static catalog — it reflects no workspace data.

List CMS providers › 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 CMS providers › Responses

Success

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

Content approval flowsSocial