REST API
Everything PageCycle's tools do is also an HTTPS route: check, move and find pages, read History and change a space's workflow from your own scripts. Each call acts as the person who signed in, with exactly their Confluence permissions. PageCycle runs on Atlassian's platform and answers only the integrations people on your site authorize.
App REST APIs are an Atlassian Preview feature. PageCycle's routes and answers follow its OpenAPI file, and answers may gain fields, so ignore the ones you don't know. How you turn the API on and sign in is Atlassian's, and may change as Atlassian finishes it.
Turn it on
A site or org admin switches the API on once per site. It is off until then, and switching it off again stops every call at once.
- Open Atlassian Administration, pick your site and open Connected apps.
- Find PageCycle and select View app details.
- On the Details tab, under App REST APIs, select Enable app REST APIs, then Enable.
The section then reads Enabled. Reinstalling PageCycle turns it off again.
Create an OAuth 2.0 integration
Calls are signed in as a person with an OAuth 2.0 (3LO) integration you create in the Atlassian developer console. It acts only for a person on your site who signs in and accepts it, and only with their permissions.
- Open the developer console, select Create, then OAuth 2.0 integration. Name it, choose Resource-level and create it.
- Open Permissions and select Add Marketplace or custom app. Pick your site, then PageCycle, then the scopes your script needs (below).
- Still in Permissions, open Confluence API, select Configure, then Granular scopes, and add
read:forge-app:confluence. Every call needs it. - Open Authorization and add your callback URL.
- Open Settings for the Client ID and Secret. Keep the secret out of your code and your logs.
Get a token
Send the person to the authorization URL. They sign in, pick your site and accept:
https://auth.atlassian.com/authorize
?audience=api.atlassian.com
&client_id=YOUR_CLIENT_ID
&scope=read%3Apagecycle%3Acustom%20write%3Apagecycle-page%3Acustom%20read%3Aforge-app%3Aconfluence%20offline_access
&redirect_uri=YOUR_CALLBACK_URL
&state=A_VALUE_YOU_CHECK
&response_type=code
&prompt=consent
&sns=db702242-b6fc-4a44-bcea-e6ad97e8db84.7b249cc1-1e34-4a73-850f-d51fafab4b8cKeep sns as written. It names PageCycle; without it, Atlassian leaves PageCycle's scopes out of the token and every call answers 403 Missing required scopes. With it, the consent screen lists what the script may do in PageCycle.
Exchange the code your callback receives for a token:
curl -s -X POST https://auth.atlassian.com/oauth/token \
-H 'Content-Type: application/json' \
-d '{"grant_type":"authorization_code","client_id":"YOUR_CLIENT_ID","client_secret":"YOUR_SECRET","code":"THE_CODE","redirect_uri":"YOUR_CALLBACK_URL"}'The answer's access_token lasts an hour; offline_access adds a refresh_token to get a new one. Your site's cloud id is at https://YOUR_SITE.atlassian.net/_edge/tenant_info.
Basics
Every route lives under one base URL:
https://api.atlassian.com/svc/confluence/YOUR_CLOUD_ID/apps/db702242-b6fc-4a44-bcea-e6ad97e8db84_7b249cc1-1e34-4a73-850f-d51fafab4b8cSend the token as Authorization: Bearer YOUR_TOKEN and Accept: application/json; send JSON bodies with Content-Type: application/json, up to 16,000 bytes. Every answer from PageCycle has the same four fields (Atlassian's own refusals, below, are plain):
| Field | Type | Meaning |
|---|---|---|
ok | boolean | true when PageCycle did it |
answer | string | What happened, in the same words Rovo and MCP get. A refused change starts "Not done." |
code | string or null | null on success, working for a long change that goes on, or why it was refused (status codes, below) |
data | object or null | The same answer as fields: the reference lists every route's |
A page's next review is reviewBy in every answer; you send it as reviewDate with a move and as date to PUT /pages/{page}/reviewDate. Lists check the person's view of every page, so a large space takes a few seconds a call: page through it with after, and don't poll in a tight loop.
Note
A change to a space's settings never applies on the first call. The route answers a preview and a confirmation; nothing changes until the same person sends that confirmation to POST /confirmations/{id}, at least 8 seconds later and within 15 minutes.
Examples
Check a page
curl -s "$BASE/pages/8013/status" -H "Authorization: Bearer $TOKEN" -H 'Accept: application/json'Move a page
curl -s -X POST "$BASE/pages/8013/moves" -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -H 'Accept: application/json' \
-d '{"to":"In review","comment":"Added the over-limit case"}'Change a space's workflow
Ask for the change; the answer is a preview:
curl -s -X POST "$BASE/spaces/WS/statuses" -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -H 'Accept: application/json' \
-d '{"name":"On hold","after":"Draft"}'Show the person data.preview. When they agree, confirm it with data.confirmation:
curl -s -X POST "$BASE/confirmations/op_8f2c1d0e9a7b6c5d" -H "Authorization: Bearer $TOKEN" -H 'Accept: application/json'A change that touches many pages (removing or renaming a status, turning PageCycle on) answers 202 with data.operation. Call POST /operations/{id} until it answers 200; GET /operations/{id} only reads where it stands.
Routes
Every route's fields and a real answer are in the API reference. To generate a client, download the OpenAPI file.
{page} is a page id. {space} is a space key. A status, move or automation in a path is its name or its id; a name with / or % in it must be named by its id, because Atlassian refuses those characters in a path.
Read
Scope read:pagecycle:custom. Anyone who can view the page or space.
| Method | Path | What it does |
|---|---|---|
| GET | /pages/{page}/status | A page's status, since when and who moved it there, the version approved, its next review and the moves you can make |
| GET | /pages/{page}/history?limit= | Every move with who, when and why, newest first |
| GET | /spaces?limit= | The spaces that use PageCycle, with their counts |
| GET | /spaces/{space}/summary | Pages in each status, and the pages that need attention |
| GET | /spaces/{space}/pages?status=&overdue=&stuckDays=&limit=&after= | Pages by status, overdue for review, or stuck for some days |
| GET | /spaces/{space}/history?person=&status=&from=&to=&limit= | A space's History, filtered by person, status or dates |
| GET | /spaces/{space}/workflow | Statuses, moves and who may make them |
| GET | /spaces/{space}/automations | Automations, and for space admins whether they run well |
| GET | /operations/{id} | Where a change stands (space admins; read only) |
Move pages
Scope write:pagecycle-page:custom. People who can edit the page, within the move's groups.
| Method | Path | What it does |
|---|---|---|
| POST | /pages/{page}/moves | Moves a page: { "to", "comment", "reviewDate", "confirm" } |
| PUT | /pages/{page}/reviewDate | Sets the next review, { "date": "2027-03-30" } or "in 6 months"; null clears it |
Space admin
Scope manage:pagecycle-space:custom. Space admins. Each answers a preview.
| Method | Path | What it does |
|---|---|---|
| POST | /spaces/{space}/statuses | Adds a status after an existing one |
| PATCH | /spaces/{space}/statuses/{status} | Renames, recolors or changes the kind of a status |
| DELETE | /spaces/{space}/statuses/{status}?moveTo= | Removes a status after moving its pages |
| POST | /spaces/{space}/moves | Adds a move between two statuses |
| PATCH | /spaces/{space}/moves/{move} | Changes a move's name, groups or comment rule |
| DELETE | /spaces/{space}/moves/{move} | Removes a move |
| PUT | /spaces/{space}/workflowDefaults | Sets where pages start and the status new pages get |
| POST | /spaces/{space}/automations | Adds an automation: { "when", "if", "then", "name" } |
| PATCH | /spaces/{space}/automations/{automation} | Changes, switches on or off, or reorders an automation |
| DELETE | /spaces/{space}/automations/{automation} | Removes an automation |
| PUT | /spaces/{space}/settings | What happens when someone breaks the workflow, and how long History is kept |
| POST | /spaces/{space}/pagecycle | Turns PageCycle on in a space |
| DELETE | /spaces/{space}/pagecycle | Turns PageCycle off in a space; History is kept |
Confirmations and long changes
Scope manage:pagecycle-space:custom.
| Method | Path | What it does |
|---|---|---|
| POST | /confirmations/{id} | Applies your own preview. Repeating it answers the saved result |
| POST | /operations/{id} | Continues a long change by its next part (its previewer or another space admin) |
Pages a person can't view never appear in a list, a count or a History line. Lists answer at most 50 rows: only the page list takes after (pass the previous answer's data.next); the others say data.truncated and narrow by their filters.
Status codes
| Status | Means |
|---|---|
| 200 | Done, or a preview |
| 202 | A long change goes on: call POST /operations/{id} |
| 400 | The request is malformed, or names something PageCycle can't read |
| 401 | No person is signed in behind the token |
| 403 | The person may not do this, it is someone else's preview, or they have not allowed PageCycle access yet |
| 404 | Not found, or the person can't view it; nothing in the answer names it |
| 409 | PageCycle is off in the space, the workflow doesn't allow it, or the preview expired or the settings changed (a fresh preview is in data). When data.confirmNeeded is true, the page's Confluence status was removed, which can mean a Confluence approval is running: ask the person, then send the move again with "confirm": true |
| 425 | Too early: a confirmation waits 8 seconds after its preview |
| 429 | Too many unconfirmed previews in two minutes |
| 500 | PageCycle couldn't finish; the answer says so |
| 503 | The PageCycle license is inactive: moves, review dates and long changes are paused; reads and settings still answer |
Refusals from Atlassian
Some calls never reach PageCycle. Atlassian's gateway answers them itself, in plain text or its own JSON, without the four fields:
| Status | When |
|---|---|
| 401 | No token, or one that expired |
| 403 | Missing required scopes: the token lacks the route's scope (or was taken without sns) |
| 403 | REST APIs are not allowed: App REST APIs are off for PageCycle on the site |
| 400 | A path value with / or % in it, even encoded: name the status, move or automation by its id |
| 404 | No matching API route found: the path is not one of PageCycle's routes |
Scopes
| Scope | Shown as | Opens |
|---|---|---|
read:pagecycle:custom | Read PageCycle | Every Read route |
write:pagecycle-page:custom | Move PageCycle pages | Moving pages and setting review dates |
manage:pagecycle-space:custom | Change PageCycle space settings | Every Space admin route, confirmations and continuing a long change |
read:forge-app:confluence | Atlassian's | Every call, beside one of the above |
A token acts as the person who signed in, with everything they may do in PageCycle. Ask only for the scopes your script needs, keep tokens out of code and logs, and revoke an integration you no longer use in the developer console.