Introduction
The Andishi API lets you read and write everything in a workspace over plain JSON. Because it is the same data your studio uses internally, no resource is treated differently through the API than it is in the studio itself.
Generated from server/openapi.yaml · OpenAPI 3.0.3 · 142 operations on this track.
The product API base path is https://api.andishi.nexuslabsstudio.com/v1/api; studio session routes sit on /v1. Every request and response body is JSON, and API keys are workspace-scoped, so no extra workspace header is required.
Setup & API keys
Before making your first request, you need a secret key, which is the default credential for everything on this track.
Base URL
Every request goes to https://api.andishi.nexuslabsstudio.com. There is nothing to install or deploy, since your workspace already exists and your API key identifies it. Paths below are relative to that host, so the articles endpoint in full is https://api.andishi.nexuslabsstudio.com/v1/api/articles.
Creating a secret key
In the studio, open Developers → New API Key. Give it a name you will recognise later, tick the scopes it needs, and create it. Only workspace owners and admins can mint keys.
The secret appears in that one response and nowhere else, because Andishi stores only a hash and the visible prefix, so it cannot be shown again or recovered by support. Put it in your environment config immediately; if you lose it, revoke the key and mint another.
Calling the API
Send it from wherever your code runs, such as a server component or route handler, a static-site build step, or your own backend, but never from a browser, since the key can write to, and read every draft in, your workspace.
curl "https://api.andishi.nexuslabsstudio.com/v1/api/articles?limit=5" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
A client-only SPA has no server of its own to hold this key, so it needs a small backend or serverless function that proxies to Andishi. None of this needs React, because it is plain REST, so any language that can make an HTTP request can integrate.
Rotating and revoking
Revoking a key takes effect on the next request; there is no cache to wait out. Keys cannot be edited, so changing scopes means creating a replacement and revoking the old one.
Authentication
This track's default is the secret key, sent as a Bearer token. It reads and writes everything your scopes allow, drafts included, from any server, and it must never be sent from a browser.
| Secret key (REST API track) | Publishable key (React SDK track) | |
|---|---|---|
| Base path | /v1/api | /v1/public |
| Sent as | Header Authorization: Bearer andishi_live_... | Query param ?key=pk_... |
| Access | Full CRUD, everything the key's scopes allow, drafts included | Published content, one site, read only |
| Safe in a browser? | No, never expose this key client-side | Yes, it is designed for it |
| Rate limit | None enforced at this layer | 120 requests/minute, burst up to 240 |
Sending the secret key
curl -H "Authorization: Bearer andishi_live_YOUR_KEY" "https://api.andishi.nexuslabsstudio.com/v1/api/articles?limit=10"
Scopes
Every /v1/api route names the scope it needs, shown on the endpoint below. A request whose key lacks that scope fails with 403 missing_scope. Issue narrow keys per integration.
Studio routes under /v1/workspaces authenticate with a session instead and are the studio UI's own backend, documented here for completeness rather than as a supported integration contract.
Pagination & errors
Every endpoint follows the same two shapes, so you only need to learn them once.
List responses
{
"articles": [
{
"id": "...",
"title": "Elections 2027",
"status": "published"
},
{
"id": "...",
"title": "Budget explainer",
"status": "draft"
}
],
"pagination": {
"total": 84,
"limit": 2,
"offset": 0
}
}Use limit (max 100) and offset to page through results. Collection keys match the resource. A few endpoints return only total and offset; the response tables below say which.
Errors
{
"error": "title is required",
"code": "title_required"
}Branch on code, never on error. Each endpoint lists the statuses it can return, and the shared meanings are:
| Field | Type | Notes |
|---|---|---|
401 | status | No credential, or it is wrong, revoked or expired. |
403 | status | Authenticated but not permitted: missing scope, role too low, or the module is disabled. |
404 | status | No such resource here. Public reads also return this for a draft, so a reader cannot probe for unpublished content. |
409 | status | Collides with existing state: slug_taken, email_taken, or a section version conflict. |
429 | status | Rate limited. Back off and retry. |
Rate limits
No limit is enforced on /v1/api at this layer today; that may change.
Articles
Long-form written content, the writing module's core resource.
Includes drafts. Scope: articles:read.
Query parameters
| Field | Type | Notes |
|---|---|---|
status optional | string | One of: draft, in_review, scheduled, published, archived. |
locale optional | string | Filter by language code. Defaults to the workspace's default locale. Example: en. |
limit optional | integer | Page size. Out-of-range values fall back to the endpoint default. Defaults to 50. Max 100. |
offset optional | integer | Number of records to skip. Defaults to 0. |
Response · 200
{
"articles": [
{
"id": "...",
"title": "Elections 2027: what to watch",
"slug": "elections-2027-what-to-watch",
"subtitle": "...",
"excerpt": "...",
"body": "...",
"status": "draft",
"locale": "en",
"image_url": "...",
"category_id": "...",
"author_id": "...",
"tags": [
"politics",
"elections"
],
"is_featured": true,
"is_breaking": true,
"is_opinion": true,
"focus_keyword": "...",
"read_time_minutes": 3,
"comment_count": 0,
"published_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
],
"pagination": {
"total": 84,
"limit": 50,
"offset": 0
}
}Responses
| Status | Meaning |
|---|---|
200 | A page of articles. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · articles · Article
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | Example: Elections 2027: what to watch. |
slug | string | Example: elections-2027-what-to-watch. |
subtitle | string | Deck / standfirst. |
excerpt | string | Alias of subtitle, for template convenience. |
body | string | Sanitized rich-text HTML. |
status | string | One of: draft, in_review, scheduled, published, archived. |
locale | string | Example: en. |
image_url | string | |
category_id | string (uuid) | null | |
author_id | string (uuid) | null | |
tags | string[] | Example: politics,elections. |
is_featured | boolean | |
is_breaking | boolean | |
is_opinion | boolean | |
focus_keyword | string | Target keyword for the SEO analyser. |
read_time_minutes | integer | Estimated at ~200 words/minute when not supplied. Example: 3. |
comment_count | integer | |
published_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/api/articles" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
Creating directly at published or scheduled needs publish rights. Scope: articles:write.
Request · CreateArticleInput
| Field | Type | Notes |
|---|---|---|
title required | string |
15 optional fields
| Field | Type | Notes |
|---|---|---|
slug optional | string | Lowercase, digits and dashes. Derived from title when omitted. Matches ^[a-z0-9]+(-[a-z0-9]+)*$. |
subtitle optional | string | |
body optional | string | HTML. Sanitized server-side. |
image_url optional | string | |
locale optional | string | |
category_id optional | string (uuid) | null | |
author_id optional | string (uuid) | null | Defaults to the authenticated user. |
tags optional | string[] | |
status optional | string | Reaching published or scheduled requires a role with publish rights (owner, admin, editor, author); otherwise cannot_publish. One of: draft, in_review, scheduled, published, archived. Defaults to draft. |
is_featured optional | boolean | |
is_breaking optional | boolean | |
is_opinion optional | boolean | |
focus_keyword optional | string | |
read_time_minutes optional | integer | Overrides the estimate when > 0. |
published_at optional | string (date-time) | RFC 3339. Set automatically when status becomes published. |
{
"title": "...",
...
}Response · 201
{
"article": {
"id": "...",
"title": "Elections 2027: what to watch",
"slug": "elections-2027-what-to-watch",
"subtitle": "...",
"excerpt": "...",
"body": "...",
"status": "draft",
"locale": "en",
"image_url": "...",
"category_id": "...",
"author_id": "...",
"tags": [
"politics",
"elections"
],
"is_featured": true,
"is_breaking": true,
"is_opinion": true,
"focus_keyword": "...",
"read_time_minutes": 3,
"comment_count": 0,
"published_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
201 | The created article. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
409 | The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse. |
201 payload · article · Article
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | Example: Elections 2027: what to watch. |
slug | string | Example: elections-2027-what-to-watch. |
subtitle | string | Deck / standfirst. |
excerpt | string | Alias of subtitle, for template convenience. |
body | string | Sanitized rich-text HTML. |
status | string | One of: draft, in_review, scheduled, published, archived. |
locale | string | Example: en. |
image_url | string | |
category_id | string (uuid) | null | |
author_id | string (uuid) | null | |
tags | string[] | Example: politics,elections. |
is_featured | boolean | |
is_breaking | boolean | |
is_opinion | boolean | |
focus_keyword | string | Target keyword for the SEO analyser. |
read_time_minutes | integer | Estimated at ~200 words/minute when not supplied. Example: 3. |
comment_count | integer | |
published_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/api/articles" \
-H "Authorization: Bearer $ANDISHI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"..."}'Returns drafts too. Scope: articles:read.
Path parameters
| Field | Type | Notes |
|---|---|---|
articleId required | string (uuid) |
Response · 200
{
"article": {
"id": "...",
"title": "Elections 2027: what to watch",
"slug": "elections-2027-what-to-watch",
"subtitle": "...",
"excerpt": "...",
"body": "...",
"status": "draft",
"locale": "en",
"image_url": "...",
"category_id": "...",
"author_id": "...",
"tags": [
"politics",
"elections"
],
"is_featured": true,
"is_breaking": true,
"is_opinion": true,
"focus_keyword": "...",
"read_time_minutes": 3,
"comment_count": 0,
"published_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The article. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · article · Article
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | Example: Elections 2027: what to watch. |
slug | string | Example: elections-2027-what-to-watch. |
subtitle | string | Deck / standfirst. |
excerpt | string | Alias of subtitle, for template convenience. |
body | string | Sanitized rich-text HTML. |
status | string | One of: draft, in_review, scheduled, published, archived. |
locale | string | Example: en. |
image_url | string | |
category_id | string (uuid) | null | |
author_id | string (uuid) | null | |
tags | string[] | Example: politics,elections. |
is_featured | boolean | |
is_breaking | boolean | |
is_opinion | boolean | |
focus_keyword | string | Target keyword for the SEO analyser. |
read_time_minutes | integer | Estimated at ~200 words/minute when not supplied. Example: 3. |
comment_count | integer | |
published_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/api/articles/YOUR_ARTICLEID" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
Partial update. Editing an article that is already live requires publish rights, otherwise 403 cannot_edit_live. A revision snapshot is taken when content or status changes, unless autosave is set. Scope: articles:write.
Path parameters
| Field | Type | Notes |
|---|---|---|
articleId required | string (uuid) |
Request · UpdateArticleInput
18 optional fields
| Field | Type | Notes |
|---|---|---|
title optional | string | |
slug optional | string | Matches ^[a-z0-9]+(-[a-z0-9]+)*$. |
subtitle optional | string | |
body optional | string | |
image_url optional | string | |
locale optional | string | |
category_id optional | string (uuid) | null | |
clear_category optional | boolean | Set true to unset the category (wins over category_id). |
author_id optional | string (uuid) | |
tags optional | string[] | |
status optional | string | One of: draft, in_review, scheduled, published, archived. |
is_featured optional | boolean | |
is_breaking optional | boolean | |
is_opinion optional | boolean | |
focus_keyword optional | string | |
read_time_minutes optional | integer | |
published_at optional | string | RFC 3339, or an empty string to clear it. |
autosave optional | boolean | Marks an editor-issued background save. Suppresses revision snapshotting so keystroke batches do not evict real checkpoints. Defaults to false. |
{
...
}Response · 200
{
"article": {
"id": "...",
"title": "Elections 2027: what to watch",
"slug": "elections-2027-what-to-watch",
"subtitle": "...",
"excerpt": "...",
"body": "...",
"status": "draft",
"locale": "en",
"image_url": "...",
"category_id": "...",
"author_id": "...",
"tags": [
"politics",
"elections"
],
"is_featured": true,
"is_breaking": true,
"is_opinion": true,
"focus_keyword": "...",
"read_time_minutes": 3,
"comment_count": 0,
"published_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated article. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
409 | The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse. |
200 payload · article · Article
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | Example: Elections 2027: what to watch. |
slug | string | Example: elections-2027-what-to-watch. |
subtitle | string | Deck / standfirst. |
excerpt | string | Alias of subtitle, for template convenience. |
body | string | Sanitized rich-text HTML. |
status | string | One of: draft, in_review, scheduled, published, archived. |
locale | string | Example: en. |
image_url | string | |
category_id | string (uuid) | null | |
author_id | string (uuid) | null | |
tags | string[] | Example: politics,elections. |
is_featured | boolean | |
is_breaking | boolean | |
is_opinion | boolean | |
focus_keyword | string | Target keyword for the SEO analyser. |
read_time_minutes | integer | Estimated at ~200 words/minute when not supplied. Example: 3. |
comment_count | integer | |
published_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/api/articles/YOUR_ARTICLEID" \
-H "Authorization: Bearer $ANDISHI_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'Soft delete; trashed rows are purged after 30 days. Deleting live content requires publish rights. Scope: articles:write.
Path parameters
| Field | Type | Notes |
|---|---|---|
articleId required | string (uuid) |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/api/articles/YOUR_ARTICLEID" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Query parameters
| Field | Type | Notes |
|---|---|---|
status | string | One of: draft, in_review, scheduled, published, archived. |
locale optional | string | Filter by language code. Defaults to the workspace's default locale. Example: en. |
limit optional | integer | Page size. Out-of-range values fall back to the endpoint default. Defaults to 50. Max 100. |
offset optional | integer | Number of records to skip. Defaults to 0. |
Response · 200
{
"articles": [
{
"id": "...",
"title": "Elections 2027: what to watch",
"slug": "elections-2027-what-to-watch",
"subtitle": "...",
"excerpt": "...",
"body": "...",
"status": "draft",
"locale": "en",
"image_url": "...",
"category_id": "...",
"author_id": "...",
"tags": [
"politics",
"elections"
],
"is_featured": true,
"is_breaking": true,
"is_opinion": true,
"focus_keyword": "...",
"read_time_minutes": 3,
"comment_count": 0,
"published_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
],
"pagination": {
"total": 84,
"limit": 50,
"offset": 0
}
}Responses
| Status | Meaning |
|---|---|
200 | A page of articles. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · articles · Article
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | Example: Elections 2027: what to watch. |
slug | string | Example: elections-2027-what-to-watch. |
subtitle | string | Deck / standfirst. |
excerpt | string | Alias of subtitle, for template convenience. |
body | string | Sanitized rich-text HTML. |
status | string | One of: draft, in_review, scheduled, published, archived. |
locale | string | Example: en. |
image_url | string | |
category_id | string (uuid) | null | |
author_id | string (uuid) | null | |
tags | string[] | Example: politics,elections. |
is_featured | boolean | |
is_breaking | boolean | |
is_opinion | boolean | |
focus_keyword | string | Target keyword for the SEO analyser. |
read_time_minutes | integer | Estimated at ~200 words/minute when not supplied. Example: 3. |
comment_count | integer | |
published_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/articles" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Request · CreateArticleInput
| Field | Type | Notes |
|---|---|---|
title required | string |
15 optional fields
| Field | Type | Notes |
|---|---|---|
slug optional | string | Lowercase, digits and dashes. Derived from title when omitted. Matches ^[a-z0-9]+(-[a-z0-9]+)*$. |
subtitle optional | string | |
body optional | string | HTML. Sanitized server-side. |
image_url optional | string | |
locale optional | string | |
category_id optional | string (uuid) | null | |
author_id optional | string (uuid) | null | Defaults to the authenticated user. |
tags optional | string[] | |
status optional | string | Reaching published or scheduled requires a role with publish rights (owner, admin, editor, author); otherwise cannot_publish. One of: draft, in_review, scheduled, published, archived. Defaults to draft. |
is_featured optional | boolean | |
is_breaking optional | boolean | |
is_opinion optional | boolean | |
focus_keyword optional | string | |
read_time_minutes optional | integer | Overrides the estimate when > 0. |
published_at optional | string (date-time) | RFC 3339. Set automatically when status becomes published. |
{
"title": "...",
...
}Response · 201
{
"article": {
"id": "...",
"title": "Elections 2027: what to watch",
"slug": "elections-2027-what-to-watch",
"subtitle": "...",
"excerpt": "...",
"body": "...",
"status": "draft",
"locale": "en",
"image_url": "...",
"category_id": "...",
"author_id": "...",
"tags": [
"politics",
"elections"
],
"is_featured": true,
"is_breaking": true,
"is_opinion": true,
"focus_keyword": "...",
"read_time_minutes": 3,
"comment_count": 0,
"published_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
201 | The created article. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
409 | The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse. |
201 payload · article · Article
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | Example: Elections 2027: what to watch. |
slug | string | Example: elections-2027-what-to-watch. |
subtitle | string | Deck / standfirst. |
excerpt | string | Alias of subtitle, for template convenience. |
body | string | Sanitized rich-text HTML. |
status | string | One of: draft, in_review, scheduled, published, archived. |
locale | string | Example: en. |
image_url | string | |
category_id | string (uuid) | null | |
author_id | string (uuid) | null | |
tags | string[] | Example: politics,elections. |
is_featured | boolean | |
is_breaking | boolean | |
is_opinion | boolean | |
focus_keyword | string | Target keyword for the SEO analyser. |
read_time_minutes | integer | Estimated at ~200 words/minute when not supplied. Example: 3. |
comment_count | integer | |
published_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/articles" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"title":"..."}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
articleId required | string (uuid) |
Response · 200
{
"article": {
"id": "...",
"title": "Elections 2027: what to watch",
"slug": "elections-2027-what-to-watch",
"subtitle": "...",
"excerpt": "...",
"body": "...",
"status": "draft",
"locale": "en",
"image_url": "...",
"category_id": "...",
"author_id": "...",
"tags": [
"politics",
"elections"
],
"is_featured": true,
"is_breaking": true,
"is_opinion": true,
"focus_keyword": "...",
"read_time_minutes": 3,
"comment_count": 0,
"published_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The article. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · article · Article
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | Example: Elections 2027: what to watch. |
slug | string | Example: elections-2027-what-to-watch. |
subtitle | string | Deck / standfirst. |
excerpt | string | Alias of subtitle, for template convenience. |
body | string | Sanitized rich-text HTML. |
status | string | One of: draft, in_review, scheduled, published, archived. |
locale | string | Example: en. |
image_url | string | |
category_id | string (uuid) | null | |
author_id | string (uuid) | null | |
tags | string[] | Example: politics,elections. |
is_featured | boolean | |
is_breaking | boolean | |
is_opinion | boolean | |
focus_keyword | string | Target keyword for the SEO analyser. |
read_time_minutes | integer | Estimated at ~200 words/minute when not supplied. Example: 3. |
comment_count | integer | |
published_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/articles/YOUR_ARTICLEID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
articleId required | string (uuid) |
Request · UpdateArticleInput
18 optional fields
| Field | Type | Notes |
|---|---|---|
title optional | string | |
slug optional | string | Matches ^[a-z0-9]+(-[a-z0-9]+)*$. |
subtitle optional | string | |
body optional | string | |
image_url optional | string | |
locale optional | string | |
category_id optional | string (uuid) | null | |
clear_category optional | boolean | Set true to unset the category (wins over category_id). |
author_id optional | string (uuid) | |
tags optional | string[] | |
status optional | string | One of: draft, in_review, scheduled, published, archived. |
is_featured optional | boolean | |
is_breaking optional | boolean | |
is_opinion optional | boolean | |
focus_keyword optional | string | |
read_time_minutes optional | integer | |
published_at optional | string | RFC 3339, or an empty string to clear it. |
autosave optional | boolean | Marks an editor-issued background save. Suppresses revision snapshotting so keystroke batches do not evict real checkpoints. Defaults to false. |
{
...
}Response · 200
{
"article": {
"id": "...",
"title": "Elections 2027: what to watch",
"slug": "elections-2027-what-to-watch",
"subtitle": "...",
"excerpt": "...",
"body": "...",
"status": "draft",
"locale": "en",
"image_url": "...",
"category_id": "...",
"author_id": "...",
"tags": [
"politics",
"elections"
],
"is_featured": true,
"is_breaking": true,
"is_opinion": true,
"focus_keyword": "...",
"read_time_minutes": 3,
"comment_count": 0,
"published_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated article. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
409 | The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse. |
200 payload · article · Article
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | Example: Elections 2027: what to watch. |
slug | string | Example: elections-2027-what-to-watch. |
subtitle | string | Deck / standfirst. |
excerpt | string | Alias of subtitle, for template convenience. |
body | string | Sanitized rich-text HTML. |
status | string | One of: draft, in_review, scheduled, published, archived. |
locale | string | Example: en. |
image_url | string | |
category_id | string (uuid) | null | |
author_id | string (uuid) | null | |
tags | string[] | Example: politics,elections. |
is_featured | boolean | |
is_breaking | boolean | |
is_opinion | boolean | |
focus_keyword | string | Target keyword for the SEO analyser. |
read_time_minutes | integer | Estimated at ~200 words/minute when not supplied. Example: 3. |
comment_count | integer | |
published_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/articles/YOUR_ARTICLEID" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
articleId required | string (uuid) |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/articles/YOUR_ARTICLEID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Always 200. Whether the lock is yours is reported by lock.mine, not by an HTTP error — a lock held by someone else is a valid state the editor renders, not a failure.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
articleId required | string (uuid) |
Response · 200
{
"lock": {
"article_id": "...",
"user_id": "...",
"user_name": "...",
"user_email": "...",
"acquired_at": "2026-01-01T00:00:00Z",
"refreshed_at": "2026-01-01T00:00:00Z",
"mine": true
}
}Responses
| Status | Meaning |
|---|---|
200 | The current lock holder. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · lock · ArticleLock
Response fields
| Field | Type | Notes |
|---|---|---|
article_id | string (uuid) | |
user_id | string (uuid) | |
user_name | string | |
user_email | string (email) | |
acquired_at | string (date-time) | |
refreshed_at | string (date-time) | |
mine | boolean | Whether the requesting user holds the lock. |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/articles/YOUR_ARTICLEID/lock" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
articleId required | string (uuid) |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/articles/YOUR_ARTICLEID/lock" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
articleId required | string (uuid) |
Response · 200
{
"seo": {
"article_id": "...",
"score": 0,
"max_score": 0,
"grade": "B",
"focus_keyword": "...",
"data": {},
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The SEO score. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · seo · ArticleSEO
Response fields
| Field | Type | Notes |
|---|---|---|
article_id | string (uuid) | |
score | integer | |
max_score | integer | |
grade | string | Example: B. |
focus_keyword | string | |
data | object | Raw analyser output. |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/articles/YOUR_ARTICLEID/seo" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
articleId required | string (uuid) |
Request · UpsertSEOInput
5 optional fields
| Field | Type | Notes |
|---|---|---|
score optional | integer | |
max_score optional | integer | |
grade optional | string | |
focus_keyword optional | string | |
data optional | object |
{
...
}Response · 200
{
"seo": {
"article_id": "...",
"score": 0,
"max_score": 0,
"grade": "B",
"focus_keyword": "...",
"data": {},
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The stored SEO score. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · seo · ArticleSEO
Response fields
| Field | Type | Notes |
|---|---|---|
article_id | string (uuid) | |
score | integer | |
max_score | integer | |
grade | string | Example: B. |
focus_keyword | string | |
data | object | Raw analyser output. |
updated_at | string (date-time) |
curl -X PUT "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/articles/YOUR_ARTICLEID/seo" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Videos
Video records, streamed via Bunny Stream or linked externally.
Includes drafts. Scope: videos:read.
Query parameters
| Field | Type | Notes |
|---|---|---|
status optional | string | One of: draft, published, archived. |
limit optional | integer | Page size. Out-of-range values fall back to the endpoint default. Defaults to 50. Max 100. |
offset optional | integer | Number of records to skip. Defaults to 0. |
Response · 200
{
"videos": [
{
"id": "...",
"title": "...",
"slug": "...",
"description": "...",
"poster_url": "...",
"bunny_video_id": "...",
"bunny_library_id": "...",
"bunny_status": "pending",
"source_url": "...",
"duration_seconds": 0,
"status": "draft",
"locale": "...",
"published_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
],
"pagination": {
"total": 84,
"limit": 50,
"offset": 0
}
}Responses
| Status | Meaning |
|---|---|
200 | A page of videos. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · videos · Video
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | |
slug | string | |
description | string | |
poster_url | string | Thumbnail image URL. |
bunny_video_id | string | null | GUID from Bunny Stream. |
bunny_library_id | string | null | |
bunny_status | string | null | One of: pending, uploading, processing, ready, failed. |
source_url | string | null | External URL when not on Bunny. |
duration_seconds | number (double) | null | |
status | string | One of: draft, published, archived. |
locale | string | |
published_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/api/videos" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
Attaches an uploaded Bunny video or an external URL. Scope: videos:write.
Request · CreateVideoInput
| Field | Type | Notes |
|---|---|---|
title required | string |
11 optional fields
| Field | Type | Notes |
|---|---|---|
slug optional | string | Matches ^[a-z0-9]+(-[a-z0-9]+)*$. |
description optional | string | |
poster_url optional | string | |
bunny_video_id optional | string | null | |
bunny_library_id optional | string | null | |
bunny_status optional | string | null | One of: pending, uploading, processing, ready, failed. |
source_url optional | string | null | |
duration_seconds optional | number (double) | |
status optional | string | One of: draft, published, archived. Defaults to draft. |
locale optional | string | Defaults to en. |
published_at optional | string (date-time) |
{
"title": "...",
...
}Response · 201
{
"video": {
"id": "...",
"title": "...",
"slug": "...",
"description": "...",
"poster_url": "...",
"bunny_video_id": "...",
"bunny_library_id": "...",
"bunny_status": "pending",
"source_url": "...",
"duration_seconds": 0,
"status": "draft",
"locale": "...",
"published_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
201 | The created video. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
201 payload · video · Video
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | |
slug | string | |
description | string | |
poster_url | string | Thumbnail image URL. |
bunny_video_id | string | null | GUID from Bunny Stream. |
bunny_library_id | string | null | |
bunny_status | string | null | One of: pending, uploading, processing, ready, failed. |
source_url | string | null | External URL when not on Bunny. |
duration_seconds | number (double) | null | |
status | string | One of: draft, published, archived. |
locale | string | |
published_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/api/videos" \
-H "Authorization: Bearer $ANDISHI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"..."}'Scope: videos:read.
Path parameters
| Field | Type | Notes |
|---|---|---|
videoId required | string (uuid) |
Response · 200
{
"video": {
"id": "...",
"title": "...",
"slug": "...",
"description": "...",
"poster_url": "...",
"bunny_video_id": "...",
"bunny_library_id": "...",
"bunny_status": "pending",
"source_url": "...",
"duration_seconds": 0,
"status": "draft",
"locale": "...",
"published_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The video. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · video · Video
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | |
slug | string | |
description | string | |
poster_url | string | Thumbnail image URL. |
bunny_video_id | string | null | GUID from Bunny Stream. |
bunny_library_id | string | null | |
bunny_status | string | null | One of: pending, uploading, processing, ready, failed. |
source_url | string | null | External URL when not on Bunny. |
duration_seconds | number (double) | null | |
status | string | One of: draft, published, archived. |
locale | string | |
published_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/api/videos/YOUR_VIDEOID" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
Editing a published video requires publish rights. Scope: videos:write.
Path parameters
| Field | Type | Notes |
|---|---|---|
videoId required | string (uuid) |
Request · UpdateVideoInput
13 optional fields
| Field | Type | Notes |
|---|---|---|
title optional | string | |
slug optional | string | |
description optional | string | |
poster_url optional | string | |
bunny_video_id optional | string | null | |
clear_bunny optional | boolean | Unsets every Bunny field (wins over the individual ones). |
bunny_library_id optional | string | null | |
bunny_status optional | string | null | One of: pending, uploading, processing, ready, failed. |
source_url optional | string | null | |
duration_seconds optional | number (double) | |
status optional | string | One of: draft, published, archived. |
locale optional | string | |
published_at optional | string (date-time) |
{
...
}Response · 200
{
"video": {
"id": "...",
"title": "...",
"slug": "...",
"description": "...",
"poster_url": "...",
"bunny_video_id": "...",
"bunny_library_id": "...",
"bunny_status": "pending",
"source_url": "...",
"duration_seconds": 0,
"status": "draft",
"locale": "...",
"published_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated video. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · video · Video
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | |
slug | string | |
description | string | |
poster_url | string | Thumbnail image URL. |
bunny_video_id | string | null | GUID from Bunny Stream. |
bunny_library_id | string | null | |
bunny_status | string | null | One of: pending, uploading, processing, ready, failed. |
source_url | string | null | External URL when not on Bunny. |
duration_seconds | number (double) | null | |
status | string | One of: draft, published, archived. |
locale | string | |
published_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/api/videos/YOUR_VIDEOID" \
-H "Authorization: Bearer $ANDISHI_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'Scope: videos:write.
Path parameters
| Field | Type | Notes |
|---|---|---|
videoId required | string (uuid) |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/api/videos/YOUR_VIDEOID" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Query parameters
| Field | Type | Notes |
|---|---|---|
status | string | One of: draft, published, archived. |
limit optional | integer | Page size. Out-of-range values fall back to the endpoint default. Defaults to 50. Max 100. |
offset optional | integer | Number of records to skip. Defaults to 0. |
Response · 200
{
"videos": [
{
"id": "...",
"title": "...",
"slug": "...",
"description": "...",
"poster_url": "...",
"bunny_video_id": "...",
"bunny_library_id": "...",
"bunny_status": "pending",
"source_url": "...",
"duration_seconds": 0,
"status": "draft",
"locale": "...",
"published_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
],
"pagination": {
"total": 84,
"limit": 50,
"offset": 0
}
}Responses
| Status | Meaning |
|---|---|
200 | A page of videos. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · videos · Video
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | |
slug | string | |
description | string | |
poster_url | string | Thumbnail image URL. |
bunny_video_id | string | null | GUID from Bunny Stream. |
bunny_library_id | string | null | |
bunny_status | string | null | One of: pending, uploading, processing, ready, failed. |
source_url | string | null | External URL when not on Bunny. |
duration_seconds | number (double) | null | |
status | string | One of: draft, published, archived. |
locale | string | |
published_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/videos" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Request · CreateVideoInput
| Field | Type | Notes |
|---|---|---|
title required | string |
11 optional fields
| Field | Type | Notes |
|---|---|---|
slug optional | string | Matches ^[a-z0-9]+(-[a-z0-9]+)*$. |
description optional | string | |
poster_url optional | string | |
bunny_video_id optional | string | null | |
bunny_library_id optional | string | null | |
bunny_status optional | string | null | One of: pending, uploading, processing, ready, failed. |
source_url optional | string | null | |
duration_seconds optional | number (double) | |
status optional | string | One of: draft, published, archived. Defaults to draft. |
locale optional | string | Defaults to en. |
published_at optional | string (date-time) |
{
"title": "...",
...
}Response · 201
{
"video": {
"id": "...",
"title": "...",
"slug": "...",
"description": "...",
"poster_url": "...",
"bunny_video_id": "...",
"bunny_library_id": "...",
"bunny_status": "pending",
"source_url": "...",
"duration_seconds": 0,
"status": "draft",
"locale": "...",
"published_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
201 | The created video. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
201 payload · video · Video
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | |
slug | string | |
description | string | |
poster_url | string | Thumbnail image URL. |
bunny_video_id | string | null | GUID from Bunny Stream. |
bunny_library_id | string | null | |
bunny_status | string | null | One of: pending, uploading, processing, ready, failed. |
source_url | string | null | External URL when not on Bunny. |
duration_seconds | number (double) | null | |
status | string | One of: draft, published, archived. |
locale | string | |
published_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/videos" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"title":"..."}'Returns 501 not_configured when no video provider is wired.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Request
1 optional field
| Field | Type | Notes |
|---|---|---|
title optional | string |
{
...
}Response · 200 · BunnyUploadCredentials
{}Responses
| Status | Meaning |
|---|---|
200 | Provider upload credentials. Returns BunnyUploadCredentials. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
501 | No video provider configured. Returns ErrorResponse. |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/videos/upload-credentials" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Best-effort persists the result onto any matching video row.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
bunnyId required | string |
Response · 200 · BunnyStatus
{
"status": "pending",
"duration_seconds": 0
}Responses
| Status | Meaning |
|---|---|
200 | The provider's status. Returns BunnyStatus. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
501 | No video provider configured. Returns ErrorResponse. |
200 payload · status
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/videos/bunny/YOUR_BUNNYID/status" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
videoId required | string (uuid) |
Response · 200
{
"video": {
"id": "...",
"title": "...",
"slug": "...",
"description": "...",
"poster_url": "...",
"bunny_video_id": "...",
"bunny_library_id": "...",
"bunny_status": "pending",
"source_url": "...",
"duration_seconds": 0,
"status": "draft",
"locale": "...",
"published_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The video. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · video · Video
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | |
slug | string | |
description | string | |
poster_url | string | Thumbnail image URL. |
bunny_video_id | string | null | GUID from Bunny Stream. |
bunny_library_id | string | null | |
bunny_status | string | null | One of: pending, uploading, processing, ready, failed. |
source_url | string | null | External URL when not on Bunny. |
duration_seconds | number (double) | null | |
status | string | One of: draft, published, archived. |
locale | string | |
published_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/videos/YOUR_VIDEOID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
videoId required | string (uuid) |
Request · UpdateVideoInput
13 optional fields
| Field | Type | Notes |
|---|---|---|
title optional | string | |
slug optional | string | |
description optional | string | |
poster_url optional | string | |
bunny_video_id optional | string | null | |
clear_bunny optional | boolean | Unsets every Bunny field (wins over the individual ones). |
bunny_library_id optional | string | null | |
bunny_status optional | string | null | One of: pending, uploading, processing, ready, failed. |
source_url optional | string | null | |
duration_seconds optional | number (double) | |
status optional | string | One of: draft, published, archived. |
locale optional | string | |
published_at optional | string (date-time) |
{
...
}Response · 200
{
"video": {
"id": "...",
"title": "...",
"slug": "...",
"description": "...",
"poster_url": "...",
"bunny_video_id": "...",
"bunny_library_id": "...",
"bunny_status": "pending",
"source_url": "...",
"duration_seconds": 0,
"status": "draft",
"locale": "...",
"published_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated video. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · video · Video
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | |
slug | string | |
description | string | |
poster_url | string | Thumbnail image URL. |
bunny_video_id | string | null | GUID from Bunny Stream. |
bunny_library_id | string | null | |
bunny_status | string | null | One of: pending, uploading, processing, ready, failed. |
source_url | string | null | External URL when not on Bunny. |
duration_seconds | number (double) | null | |
status | string | One of: draft, published, archived. |
locale | string | |
published_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/videos/YOUR_VIDEOID" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
videoId required | string (uuid) |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/videos/YOUR_VIDEOID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Categories
The built-in `category` taxonomy.
Not paginated. Scope: categories:read.
Response · 200
{
"categories": [
{
"id": "...",
"name": "Culture",
"slug": "culture",
"description": "...",
"accent_color": "#8b5cf6",
"image_url": "...",
"locale": "...",
"display_order": 0
}
]
}Responses
| Status | Meaning |
|---|---|
200 | Every category. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · categories · Category
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
name | string | Example: Culture. |
slug | string | Example: culture. |
description | string | |
accent_color | string | Example: #8b5cf6. |
image_url | string | |
locale | string | |
display_order | integer |
curl "https://api.andishi.nexuslabsstudio.com/v1/api/categories" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
Scope: categories:write.
Request · CreateCategoryInput
| Field | Type | Notes |
|---|---|---|
name required | string |
6 optional fields
| Field | Type | Notes |
|---|---|---|
slug optional | string | Derived from name when omitted. Matches ^[a-z0-9]+(-[a-z0-9]+)*$. |
description optional | string | |
accent_color optional | string | Defaults to #8b5cf6. |
image_url optional | string | |
locale optional | string | Defaults to en. |
display_order optional | integer | Defaults to 0. |
{
"name": "...",
...
}Response · 201
{
"category": {
"id": "...",
"name": "Culture",
"slug": "culture",
"description": "...",
"accent_color": "#8b5cf6",
"image_url": "...",
"locale": "...",
"display_order": 0
}
}Responses
| Status | Meaning |
|---|---|
201 | The created category. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
409 | The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse. |
201 payload · category · Category
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
name | string | Example: Culture. |
slug | string | Example: culture. |
description | string | |
accent_color | string | Example: #8b5cf6. |
image_url | string | |
locale | string | |
display_order | integer |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/api/categories" \
-H "Authorization: Bearer $ANDISHI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"..."}'Scope: categories:read.
Path parameters
| Field | Type | Notes |
|---|---|---|
termId required | string (uuid) | The category's term UUID. |
Response · 200
{
"category": {
"id": "...",
"name": "Culture",
"slug": "culture",
"description": "...",
"accent_color": "#8b5cf6",
"image_url": "...",
"locale": "...",
"display_order": 0
}
}Responses
| Status | Meaning |
|---|---|
200 | The category. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · category · Category
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
name | string | Example: Culture. |
slug | string | Example: culture. |
description | string | |
accent_color | string | Example: #8b5cf6. |
image_url | string | |
locale | string | |
display_order | integer |
curl "https://api.andishi.nexuslabsstudio.com/v1/api/categories/YOUR_TERMID" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
Scope: categories:write.
Path parameters
| Field | Type | Notes |
|---|---|---|
termId required | string (uuid) | The category's term UUID. |
Request · UpdateCategoryInput
6 optional fields
| Field | Type | Notes |
|---|---|---|
name optional | string | |
slug optional | string | |
description optional | string | |
accent_color optional | string | |
image_url optional | string | |
display_order optional | integer |
{
...
}Response · 200
{
"category": {
"id": "...",
"name": "Culture",
"slug": "culture",
"description": "...",
"accent_color": "#8b5cf6",
"image_url": "...",
"locale": "...",
"display_order": 0
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated category. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
409 | The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse. |
200 payload · category · Category
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
name | string | Example: Culture. |
slug | string | Example: culture. |
description | string | |
accent_color | string | Example: #8b5cf6. |
image_url | string | |
locale | string | |
display_order | integer |
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/api/categories/YOUR_TERMID" \
-H "Authorization: Bearer $ANDISHI_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'Scope: categories:write.
Path parameters
| Field | Type | Notes |
|---|---|---|
termId required | string (uuid) | The category's term UUID. |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/api/categories/YOUR_TERMID" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Response · 200
{
"categories": [
{
"id": "...",
"name": "Culture",
"slug": "culture",
"description": "...",
"accent_color": "#8b5cf6",
"image_url": "...",
"locale": "...",
"display_order": 0
}
]
}Responses
| Status | Meaning |
|---|---|
200 | The categories. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · categories · Category
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
name | string | Example: Culture. |
slug | string | Example: culture. |
description | string | |
accent_color | string | Example: #8b5cf6. |
image_url | string | |
locale | string | |
display_order | integer |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/categories" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Request · CreateCategoryInput
| Field | Type | Notes |
|---|---|---|
name required | string |
6 optional fields
| Field | Type | Notes |
|---|---|---|
slug optional | string | Derived from name when omitted. Matches ^[a-z0-9]+(-[a-z0-9]+)*$. |
description optional | string | |
accent_color optional | string | Defaults to #8b5cf6. |
image_url optional | string | |
locale optional | string | Defaults to en. |
display_order optional | integer | Defaults to 0. |
{
"name": "...",
...
}Response · 201
{
"category": {
"id": "...",
"name": "Culture",
"slug": "culture",
"description": "...",
"accent_color": "#8b5cf6",
"image_url": "...",
"locale": "...",
"display_order": 0
}
}Responses
| Status | Meaning |
|---|---|
201 | The created category. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
409 | The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse. |
201 payload · category · Category
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
name | string | Example: Culture. |
slug | string | Example: culture. |
description | string | |
accent_color | string | Example: #8b5cf6. |
image_url | string | |
locale | string | |
display_order | integer |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/categories" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"name":"..."}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
termId required | string (uuid) |
Response · 200
{
"category": {
"id": "...",
"name": "Culture",
"slug": "culture",
"description": "...",
"accent_color": "#8b5cf6",
"image_url": "...",
"locale": "...",
"display_order": 0
}
}Responses
| Status | Meaning |
|---|---|
200 | The category. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · category · Category
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
name | string | Example: Culture. |
slug | string | Example: culture. |
description | string | |
accent_color | string | Example: #8b5cf6. |
image_url | string | |
locale | string | |
display_order | integer |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/categories/YOUR_TERMID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
termId required | string (uuid) |
Request · UpdateCategoryInput
6 optional fields
| Field | Type | Notes |
|---|---|---|
name optional | string | |
slug optional | string | |
description optional | string | |
accent_color optional | string | |
image_url optional | string | |
display_order optional | integer |
{
...
}Response · 200
{
"category": {
"id": "...",
"name": "Culture",
"slug": "culture",
"description": "...",
"accent_color": "#8b5cf6",
"image_url": "...",
"locale": "...",
"display_order": 0
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated category. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
409 | The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse. |
200 payload · category · Category
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
name | string | Example: Culture. |
slug | string | Example: culture. |
description | string | |
accent_color | string | Example: #8b5cf6. |
image_url | string | |
locale | string | |
display_order | integer |
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/categories/YOUR_TERMID" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
termId required | string (uuid) |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/categories/YOUR_TERMID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Media
The workspace media library.
Scope: media:read.
Query parameters
| Field | Type | Notes |
|---|---|---|
type optional | string | One of: image, video, audio, file. |
limit optional | integer | Page size. Out-of-range values fall back to the endpoint default. Defaults to 50. Max 100. |
offset optional | integer | Number of records to skip. Defaults to 0. |
Response · 200
{
"media": [
{
"id": "...",
"type": "image",
"title": "...",
"url": "...",
"thumbnail_url": "...",
"mime_type": "image/png",
"size_bytes": 0,
"width": 0,
"height": 0,
"folder": "...",
"status": "pending",
"created_at": "2026-01-01T00:00:00Z"
}
],
"pagination": {
"total": 84,
"limit": 50,
"offset": 0
}
}Responses
| Status | Meaning |
|---|---|
200 | A page of media items. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · media · MediaAsset
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
type | string | One of: image, video, audio, file. |
title | string | |
url | string (uri) | |
thumbnail_url | string | |
mime_type | string | Example: image/png. |
size_bytes | integer (int64) | |
width | integer | null | |
height | integer | null | |
folder | string | |
status | string | pending until an upload finalizes. One of: pending, ready. |
created_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/api/media" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
Records an asset that lives at a URL you already control. Uploading bytes to Andishi goes through the studio's upload-session flow. Scope: media:write.
Request · CreateAssetInput
9 optional fields
| Field | Type | Notes |
|---|---|---|
type optional | string | One of: image, video, audio, file. Defaults to image. |
title optional | string | |
url optional | string (uri) | |
thumbnail_url optional | string | |
mime_type optional | string | Rejected when script-executable (text/html, application/javascript, …) or when it does not match type. |
size_bytes optional | integer (int64) | |
folder optional | string | |
width optional | integer | |
height optional | integer |
{
...
}Response · 201
{
"media": {
"id": "...",
"type": "image",
"title": "...",
"url": "...",
"thumbnail_url": "...",
"mime_type": "image/png",
"size_bytes": 0,
"width": 0,
"height": 0,
"folder": "...",
"status": "pending",
"created_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
201 | The registered asset. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
201 payload · media · MediaAsset
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
type | string | One of: image, video, audio, file. |
title | string | |
url | string (uri) | |
thumbnail_url | string | |
mime_type | string | Example: image/png. |
size_bytes | integer (int64) | |
width | integer | null | |
height | integer | null | |
folder | string | |
status | string | pending until an upload finalizes. One of: pending, ready. |
created_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/api/media" \
-H "Authorization: Bearer $ANDISHI_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'Scope: media:read.
Path parameters
| Field | Type | Notes |
|---|---|---|
assetId required | string (uuid) |
Response · 200
{
"media": {
"id": "...",
"type": "image",
"title": "...",
"url": "...",
"thumbnail_url": "...",
"mime_type": "image/png",
"size_bytes": 0,
"width": 0,
"height": 0,
"folder": "...",
"status": "pending",
"created_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The media item. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · media · MediaAsset
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
type | string | One of: image, video, audio, file. |
title | string | |
url | string (uri) | |
thumbnail_url | string | |
mime_type | string | Example: image/png. |
size_bytes | integer (int64) | |
width | integer | null | |
height | integer | null | |
folder | string | |
status | string | pending until an upload finalizes. One of: pending, ready. |
created_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/api/media/YOUR_ASSETID" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
The origin storage.URL() points at. Unauthenticated by design: these are published assets loaded by <img> and <video> from customer sites, where a SameSite=Lax session cookie would never be sent. Keys embed a workspace UUID and an upload timestamp, so they are not enumerable.
Types outside a small inline-safe allowlist are served Content-Disposition: attachment, and every response carries X-Content-Type-Options: nosniff.
Path parameters
| Field | Type | Notes |
|---|---|---|
key required | string | The object's storage key. May contain slashes. |
Responses
| Status | Meaning |
|---|---|
200 | The object's bytes. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl "https://api.andishi.nexuslabsstudio.com/v1/files/YOUR_KEY"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Query parameters
| Field | Type | Notes |
|---|---|---|
type | string | One of: image, video, audio, file. |
limit optional | integer | Page size. Out-of-range values fall back to the endpoint default. Defaults to 50. Max 100. |
offset optional | integer | Number of records to skip. Defaults to 0. |
Response · 200
{
"media": [
{
"id": "...",
"type": "image",
"title": "...",
"url": "...",
"thumbnail_url": "...",
"mime_type": "image/png",
"size_bytes": 0,
"width": 0,
"height": 0,
"folder": "...",
"status": "pending",
"created_at": "2026-01-01T00:00:00Z"
}
],
"pagination": {
"total": 84,
"limit": 50,
"offset": 0
}
}Responses
| Status | Meaning |
|---|---|
200 | A page of media. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · media · MediaAsset
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
type | string | One of: image, video, audio, file. |
title | string | |
url | string (uri) | |
thumbnail_url | string | |
mime_type | string | Example: image/png. |
size_bytes | integer (int64) | |
width | integer | null | |
height | integer | null | |
folder | string | |
status | string | pending until an upload finalizes. One of: pending, ready. |
created_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/media" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Request · CreateAssetInput
9 optional fields
| Field | Type | Notes |
|---|---|---|
type optional | string | One of: image, video, audio, file. Defaults to image. |
title optional | string | |
url optional | string (uri) | |
thumbnail_url optional | string | |
mime_type optional | string | Rejected when script-executable (text/html, application/javascript, …) or when it does not match type. |
size_bytes optional | integer (int64) | |
folder optional | string | |
width optional | integer | |
height optional | integer |
{
...
}Response · 201
{
"media": {
"id": "...",
"type": "image",
"title": "...",
"url": "...",
"thumbnail_url": "...",
"mime_type": "image/png",
"size_bytes": 0,
"width": 0,
"height": 0,
"folder": "...",
"status": "pending",
"created_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
201 | The registered asset. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
201 payload · media · MediaAsset
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
type | string | One of: image, video, audio, file. |
title | string | |
url | string (uri) | |
thumbnail_url | string | |
mime_type | string | Example: image/png. |
size_bytes | integer (int64) | |
width | integer | null | |
height | integer | null | |
folder | string | |
status | string | pending until an upload finalizes. One of: pending, ready. |
created_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/media" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Step one of the two-step upload. Checks the declared size against both the per-file cap and the workspace's plan storage quota before reserving. Finish with POST /media/{assetId}/finalize.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Request · UploadSessionInput
6 optional fields
| Field | Type | Notes |
|---|---|---|
filename optional | string | |
content_type optional | string | |
type optional | string | One of: image, video, audio, file. Defaults to image. |
title optional | string | |
size_bytes optional | integer (int64) | Checked against the workspace upload cap and plan storage quota. |
folder optional | string |
{
...
}Response · 201
{
"upload": {
"asset_id": "...",
"storage_key": "...",
"content_type": "..."
}
}Responses
| Status | Meaning |
|---|---|
201 | The reserved upload. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
413 | The upload exceeds the workspace's limit. Codes: upload_too_large, avatar_too_large, storage_limit_exceeded. Returns ErrorResponse. |
201 payload · upload · UploadSession
Response fields
| Field | Type | Notes |
|---|---|---|
asset_id | string (uuid) | |
storage_key | string | |
content_type | string |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/media/uploads" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
assetId required | string (uuid) |
Response · 200
{
"media": {
"id": "...",
"type": "image",
"title": "...",
"url": "...",
"thumbnail_url": "...",
"mime_type": "image/png",
"size_bytes": 0,
"width": 0,
"height": 0,
"folder": "...",
"status": "pending",
"created_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The media item. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · media · MediaAsset
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
type | string | One of: image, video, audio, file. |
title | string | |
url | string (uri) | |
thumbnail_url | string | |
mime_type | string | Example: image/png. |
size_bytes | integer (int64) | |
width | integer | null | |
height | integer | null | |
folder | string | |
status | string | pending until an upload finalizes. One of: pending, ready. |
created_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/media/YOUR_ASSETID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Also removes the stored object when one exists.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
assetId required | string (uuid) |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/media/YOUR_ASSETID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
With data_base64 the bytes are stored and the asset becomes ready; without it the asset is finalized as an external URL. The real byte count is re-checked against the quota, replacing the reservation.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
assetId required | string (uuid) |
Request · FinalizeUploadInput
5 optional fields
| Field | Type | Notes |
|---|---|---|
title optional | string | |
thumbnail_url optional | string | |
width optional | integer | |
height optional | integer | |
data_base64 optional | string | Inline upload body. When present the bytes are stored and the asset becomes ready; otherwise the asset is finalized as an external URL. |
{
...
}Response · 200
{
"media": {
"id": "...",
"type": "image",
"title": "...",
"url": "...",
"thumbnail_url": "...",
"mime_type": "image/png",
"size_bytes": 0,
"width": 0,
"height": 0,
"folder": "...",
"status": "pending",
"created_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The finalized asset. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
413 | The upload exceeds the workspace's limit. Codes: upload_too_large, avatar_too_large, storage_limit_exceeded. Returns ErrorResponse. |
200 payload · media · MediaAsset
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
type | string | One of: image, video, audio, file. |
title | string | |
url | string (uri) | |
thumbnail_url | string | |
mime_type | string | Example: image/png. |
size_bytes | integer (int64) | |
width | integer | null | |
height | integer | null | |
folder | string | |
status | string | pending until an upload finalizes. One of: pending, ready. |
created_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/media/YOUR_ASSETID/finalize" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Comments
Reader comments with a moderation status.
Includes pending and spam. Scope: comments:read.
Query parameters
| Field | Type | Notes |
|---|---|---|
article_id optional | string (uuid) | |
status optional | string | One of: pending, approved, spam. |
limit optional | integer | Page size. Out-of-range values fall back to the endpoint default. Defaults to 50. Max 100. |
offset optional | integer | Number of records to skip. Defaults to 0. |
Response · 200
{
"comments": [
{
"id": "...",
"article_id": "...",
"parent_id": "...",
"author_name": "...",
"author_email": "...",
"content": "...",
"status": "pending",
"created_at": "2026-01-01T00:00:00Z"
}
],
"pagination": {
"total": 84,
"limit": 50,
"offset": 0
}
}Responses
| Status | Meaning |
|---|---|
200 | A page of comments. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · comments · Comment
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
article_id | string (uuid) | |
parent_id | string (uuid) | null | Set when this is a reply. |
author_name | string | |
author_email | string | |
content | string | |
status | string | One of: pending, approved, spam. |
created_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/api/comments" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
Always stored as pending. Scope: comments:write.
Request · CreateCommentInput
| Field | Type | Notes |
|---|---|---|
article_id required | string (uuid) | |
author_name required | string | |
content required | string |
2 optional fields
| Field | Type | Notes |
|---|---|---|
parent_id optional | string (uuid) | null | |
author_email optional | string (email) |
{
"article_id": "...",
"author_name": "...",
"content": "...",
...
}Response · 201
{
"comment": {
"id": "...",
"article_id": "...",
"parent_id": "...",
"author_name": "...",
"author_email": "...",
"content": "...",
"status": "pending",
"created_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
201 | The created comment. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such article in this workspace. Returns ErrorResponse. |
201 payload · comment · Comment
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
article_id | string (uuid) | |
parent_id | string (uuid) | null | Set when this is a reply. |
author_name | string | |
author_email | string | |
content | string | |
status | string | One of: pending, approved, spam. |
created_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/api/comments" \
-H "Authorization: Bearer $ANDISHI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"article_id":"...","author_name":"...","content":"..."}'Approve, mark spam, or edit the body. Scope: comments:write.
Path parameters
| Field | Type | Notes |
|---|---|---|
commentId required | string (uuid) |
Request · UpdateCommentInput
2 optional fields
| Field | Type | Notes |
|---|---|---|
status optional | string | One of: pending, approved, spam. |
content optional | string |
{
...
}Response · 200
{
"comment": {
"id": "...",
"article_id": "...",
"parent_id": "...",
"author_name": "...",
"author_email": "...",
"content": "...",
"status": "pending",
"created_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated comment. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · comment · Comment
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
article_id | string (uuid) | |
parent_id | string (uuid) | null | Set when this is a reply. |
author_name | string | |
author_email | string | |
content | string | |
status | string | One of: pending, approved, spam. |
created_at | string (date-time) |
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/api/comments/YOUR_COMMENTID" \
-H "Authorization: Bearer $ANDISHI_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'Scope: comments:write.
Path parameters
| Field | Type | Notes |
|---|---|---|
commentId required | string (uuid) |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/api/comments/YOUR_COMMENTID" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Query parameters
| Field | Type | Notes |
|---|---|---|
article_id | string (uuid) | |
status | string | One of: pending, approved, spam. |
limit optional | integer | Page size. Out-of-range values fall back to the endpoint default. Defaults to 50. Max 100. |
offset optional | integer | Number of records to skip. Defaults to 0. |
Response · 200
{
"comments": [
{
"id": "...",
"article_id": "...",
"parent_id": "...",
"author_name": "...",
"author_email": "...",
"content": "...",
"status": "pending",
"created_at": "2026-01-01T00:00:00Z"
}
],
"pagination": {
"total": 84,
"limit": 50,
"offset": 0
}
}Responses
| Status | Meaning |
|---|---|
200 | A page of comments. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · comments · Comment
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
article_id | string (uuid) | |
parent_id | string (uuid) | null | Set when this is a reply. |
author_name | string | |
author_email | string | |
content | string | |
status | string | One of: pending, approved, spam. |
created_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/comments" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Every role may comment, viewers included.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Request · CreateCommentInput
| Field | Type | Notes |
|---|---|---|
article_id required | string (uuid) | |
author_name required | string | |
content required | string |
2 optional fields
| Field | Type | Notes |
|---|---|---|
parent_id optional | string (uuid) | null | |
author_email optional | string (email) |
{
"article_id": "...",
"author_name": "...",
"content": "...",
...
}Response · 201
{
"comment": {
"id": "...",
"article_id": "...",
"parent_id": "...",
"author_name": "...",
"author_email": "...",
"content": "...",
"status": "pending",
"created_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
201 | The created comment. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
201 payload · comment · Comment
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
article_id | string (uuid) | |
parent_id | string (uuid) | null | Set when this is a reply. |
author_name | string | |
author_email | string | |
content | string | |
status | string | One of: pending, approved, spam. |
created_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/comments" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"article_id":"...","author_name":"...","content":"..."}'Owner, admin or editor only.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
commentId required | string (uuid) |
Request · UpdateCommentInput
2 optional fields
| Field | Type | Notes |
|---|---|---|
status optional | string | One of: pending, approved, spam. |
content optional | string |
{
...
}Response · 200
{
"comment": {
"id": "...",
"article_id": "...",
"parent_id": "...",
"author_name": "...",
"author_email": "...",
"content": "...",
"status": "pending",
"created_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated comment. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · comment · Comment
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
article_id | string (uuid) | |
parent_id | string (uuid) | null | Set when this is a reply. |
author_name | string | |
author_email | string | |
content | string | |
status | string | One of: pending, approved, spam. |
created_at | string (date-time) |
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/comments/YOUR_COMMENTID" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
commentId required | string (uuid) |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/comments/YOUR_COMMENTID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Custom Entries
Records of workspace-defined Custom Modules.
Includes drafts. Scope: custom:read.
Path parameters
| Field | Type | Notes |
|---|---|---|
slug required | string | The Custom Module's slug, e.g. events. |
Query parameters
| Field | Type | Notes |
|---|---|---|
limit optional | integer | Page size. Out-of-range values fall back to the endpoint default. Defaults to 50. Max 100. |
offset optional | integer | Number of records to skip. Defaults to 0. |
Response · 200
{
"custom_entries": [
{
"id": "...",
"workspace_id": "...",
"module_id": "...",
"data": {},
"status": "draft",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
],
"pagination": {
"total": 0,
"offset": 0
}
}Responses
| Status | Meaning |
|---|---|
200 | A page of entries. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · custom_entries · CustomEntry
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
workspace_id | string (uuid) | |
module_id | string (uuid) | |
data | object | Values keyed by each field's key in the parent module's schema. Keys the schema does not define are stripped on write. |
status | string | One of: draft, published. Defaults to published. |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/api/custom/YOUR_SLUG/entries" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
data is validated against the module's field schema; unknown keys are stripped. Scope: custom:write.
Path parameters
| Field | Type | Notes |
|---|---|---|
slug required | string | The Custom Module's slug, e.g. events. |
Request · CreateCustomEntryInput
2 optional fields
| Field | Type | Notes |
|---|---|---|
data optional | object | Validated against the module schema: required fields must be present, and number, boolean, url, date, datetime, email, select and media fields must parse. |
status optional | string | One of: draft, published. Defaults to published. |
{
...
}Response · 201
{
"custom_entry": {
"id": "...",
"workspace_id": "...",
"module_id": "...",
"data": {},
"status": "draft",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
201 | The created entry. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
201 payload · custom_entry · CustomEntry
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
workspace_id | string (uuid) | |
module_id | string (uuid) | |
data | object | Values keyed by each field's key in the parent module's schema. Keys the schema does not define are stripped on write. |
status | string | One of: draft, published. Defaults to published. |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/api/custom/YOUR_SLUG/entries" \
-H "Authorization: Bearer $ANDISHI_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'Returns drafts too. Scope: custom:read.
Path parameters
| Field | Type | Notes |
|---|---|---|
slug required | string | |
entryId required | string (uuid) |
Response · 200
{
"custom_entry": {
"id": "...",
"workspace_id": "...",
"module_id": "...",
"data": {},
"status": "draft",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The entry. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · custom_entry · CustomEntry
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
workspace_id | string (uuid) | |
module_id | string (uuid) | |
data | object | Values keyed by each field's key in the parent module's schema. Keys the schema does not define are stripped on write. |
status | string | One of: draft, published. Defaults to published. |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/api/custom/YOUR_SLUG/entries/YOUR_ENTRYID" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
Scope: custom:write.
Path parameters
| Field | Type | Notes |
|---|---|---|
slug required | string | |
entryId required | string (uuid) |
Request · UpdateCustomEntryInput
2 optional fields
| Field | Type | Notes |
|---|---|---|
data optional | object | |
status optional | string | One of: draft, published. |
{
...
}Response · 200
{
"custom_entry": {
"id": "...",
"workspace_id": "...",
"module_id": "...",
"data": {},
"status": "draft",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated entry. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · custom_entry · CustomEntry
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
workspace_id | string (uuid) | |
module_id | string (uuid) | |
data | object | Values keyed by each field's key in the parent module's schema. Keys the schema does not define are stripped on write. |
status | string | One of: draft, published. Defaults to published. |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/api/custom/YOUR_SLUG/entries/YOUR_ENTRYID" \
-H "Authorization: Bearer $ANDISHI_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'Scope: custom:write.
Path parameters
| Field | Type | Notes |
|---|---|---|
slug required | string | |
entryId required | string (uuid) |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/api/custom/YOUR_SLUG/entries/YOUR_ENTRYID" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Response · 200
{
"custom_modules": [
{
"id": "...",
"workspace_id": "...",
"slug": "events",
"name": "Events",
"tagline": "...",
"schema": {
"fields": [
{}
]
},
"enabled": true,
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
]
}Responses
| Status | Meaning |
|---|---|
200 | The workspace's Custom Modules. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · custom_modules · CustomModule
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
workspace_id | string (uuid) | |
slug | string | Example: events. |
name | string | Example: Events. |
tagline | string | |
schema | object | { "fields": [{ key, label, type, required, options }] }. |
enabled | boolean | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/custom-modules" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Owner or admin only.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Request · CreateCustomModuleInput
| Field | Type | Notes |
|---|---|---|
slug required | string | Matches ^[a-z0-9]+(-[a-z0-9]+)*$. |
name required | string |
3 optional fields
| Field | Type | Notes |
|---|---|---|
tagline optional | string | |
schema optional | object | |
enabled optional | boolean | Defaults to true. |
{
"name": "...",
"slug": "...",
...
}Response · 201
{
"custom_module": {
"id": "...",
"workspace_id": "...",
"slug": "events",
"name": "Events",
"tagline": "...",
"schema": {
"fields": [
{
"key": "...",
"label": "...",
"type": "text",
"required": true,
"options": []
}
]
},
"enabled": true,
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
201 | The created module. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
409 | The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse. |
201 payload · custom_module · CustomModule
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
workspace_id | string (uuid) | |
slug | string | Example: events. |
name | string | Example: Events. |
tagline | string | |
schema | object | { "fields": [{ key, label, type, required, options }] }. |
enabled | boolean | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/custom-modules" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"name":"...","slug":"..."}'Owner or admin only.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
moduleId required | string (uuid) |
Request · UpdateCustomModuleInput
4 optional fields
| Field | Type | Notes |
|---|---|---|
name optional | string | |
tagline optional | string | |
schema optional | object | |
enabled optional | boolean |
{
...
}Response · 200
{
"custom_module": {
"id": "...",
"workspace_id": "...",
"slug": "events",
"name": "Events",
"tagline": "...",
"schema": {
"fields": [
{
"key": "...",
"label": "...",
"type": "text",
"required": true,
"options": []
}
]
},
"enabled": true,
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated module. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · custom_module · CustomModule
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
workspace_id | string (uuid) | |
slug | string | Example: events. |
name | string | Example: Events. |
tagline | string | |
schema | object | { "fields": [{ key, label, type, required, options }] }. |
enabled | boolean | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/custom-modules/YOUR_MODULEID" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Owner or admin only.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
moduleId required | string (uuid) |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/custom-modules/YOUR_MODULEID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
slug required | string |
Query parameters
| Field | Type | Notes |
|---|---|---|
limit optional | integer | Page size. Out-of-range values fall back to the endpoint default. Defaults to 50. Max 100. |
offset optional | integer | Number of records to skip. Defaults to 0. |
Response · 200
{
"custom_entries": [
{
"id": "...",
"workspace_id": "...",
"module_id": "...",
"data": {},
"status": "draft",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
],
"pagination": {
"total": 0,
"offset": 0
}
}Responses
| Status | Meaning |
|---|---|
200 | A page of entries, drafts included. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · custom_entries · CustomEntry
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
workspace_id | string (uuid) | |
module_id | string (uuid) | |
data | object | Values keyed by each field's key in the parent module's schema. Keys the schema does not define are stripped on write. |
status | string | One of: draft, published. Defaults to published. |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/custom/YOUR_SLUG/entries" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
slug required | string |
Request · CreateCustomEntryInput
2 optional fields
| Field | Type | Notes |
|---|---|---|
data optional | object | Validated against the module schema: required fields must be present, and number, boolean, url, date, datetime, email, select and media fields must parse. |
status optional | string | One of: draft, published. Defaults to published. |
{
...
}Response · 201
{
"custom_entry": {
"id": "...",
"workspace_id": "...",
"module_id": "...",
"data": {},
"status": "draft",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
201 | The created entry. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
201 payload · custom_entry · CustomEntry
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
workspace_id | string (uuid) | |
module_id | string (uuid) | |
data | object | Values keyed by each field's key in the parent module's schema. Keys the schema does not define are stripped on write. |
status | string | One of: draft, published. Defaults to published. |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/custom/YOUR_SLUG/entries" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
slug required | string | |
entryId required | string (uuid) |
Response · 200
{
"custom_entry": {
"id": "...",
"workspace_id": "...",
"module_id": "...",
"data": {},
"status": "draft",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The entry. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · custom_entry · CustomEntry
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
workspace_id | string (uuid) | |
module_id | string (uuid) | |
data | object | Values keyed by each field's key in the parent module's schema. Keys the schema does not define are stripped on write. |
status | string | One of: draft, published. Defaults to published. |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/custom/YOUR_SLUG/entries/YOUR_ENTRYID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
slug required | string | |
entryId required | string (uuid) |
Request · UpdateCustomEntryInput
2 optional fields
| Field | Type | Notes |
|---|---|---|
data optional | object | |
status optional | string | One of: draft, published. |
{
...
}Response · 200
{
"custom_entry": {
"id": "...",
"workspace_id": "...",
"module_id": "...",
"data": {},
"status": "draft",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated entry. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · custom_entry · CustomEntry
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
workspace_id | string (uuid) | |
module_id | string (uuid) | |
data | object | Values keyed by each field's key in the parent module's schema. Keys the schema does not define are stripped on write. |
status | string | One of: draft, published. Defaults to published. |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/custom/YOUR_SLUG/entries/YOUR_ENTRYID" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
slug required | string | |
entryId required | string (uuid) |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/custom/YOUR_SLUG/entries/YOUR_ENTRYID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Site Content
Sites, pages and visually-edited page sections.
Published snapshots only, never drafts. Scope: pages:read.
Path parameters
| Field | Type | Notes |
|---|---|---|
pageId required | string (uuid) |
Query parameters
| Field | Type | Notes |
|---|---|---|
locale optional | string | Filter by language code. Defaults to the workspace's default locale. Example: en. |
Response · 200
{
"sections": [
{
"id": "...",
"page_id": "...",
"section_key": "...",
"locale": "...",
"data": {},
"version": 0,
"published_at": "2026-01-01T00:00:00Z"
}
]
}Responses
| Status | Meaning |
|---|---|
200 | The page's published sections. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · sections · PublishedSection
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
page_id | string (uuid) | |
section_key | string | |
locale | string | |
data | object | |
version | integer | null | |
published_at | string (date-time) | null |
curl "https://api.andishi.nexuslabsstudio.com/v1/api/pages/YOUR_PAGEID/sections" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
Every value is validated and rich-text HTML sanitized, exactly as for a single-field write. Supply base_version for optimistic concurrency. Scope: pages:write.
Path parameters
| Field | Type | Notes |
|---|---|---|
pageId required | string (uuid) |
Request · PutSectionInput
| Field | Type | Notes |
|---|---|---|
section_key required | string |
3 optional fields
| Field | Type | Notes |
|---|---|---|
data optional | object | Every value goes through the same validation as UpsertFieldInput.value. |
locale optional | string | |
base_version optional | integer | null | Optimistic concurrency. A stale value returns 409 conflict. |
{
"section_key": "...",
...
}Response · 200
{
"section": {
"id": "...",
"page_id": "...",
"section_key": "hero",
"locale": "...",
"data": {},
"version": 0,
"published_version": 0,
"published_at": "2026-01-01T00:00:00Z",
"has_unpublished_changes": true,
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated section. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
409 | The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse. |
200 payload · section · Section
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
page_id | string (uuid) | |
section_key | string | Example: hero. |
locale | string | |
data | object | The draft (working) copy. |
version | integer | |
published_version | integer | null | Null until first publish. |
published_at | string (date-time) | null | |
has_unpublished_changes | boolean | |
updated_at | string (date-time) |
curl -X PUT "https://api.andishi.nexuslabsstudio.com/v1/api/pages/YOUR_PAGEID/sections" \
-H "Authorization: Bearer $ANDISHI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"section_key":"..."}'The granular write the visual editor uses. Rich-text HTML is sanitized before it can reach a live page. Scope: pages:write.
Path parameters
| Field | Type | Notes |
|---|---|---|
pageId required | string (uuid) |
Request · UpsertFieldInput
| Field | Type | Notes |
|---|---|---|
section required | string | Example: hero. |
field required | string | Example: title. |
2 optional fields
| Field | Type | Notes |
|---|---|---|
value optional | string | object | object | Text, {"html": "…"} for rich text, or {"src": "…", "alt": "…"} for an image. Anything else is refused with invalid_field_value. Rich-text HTML is sanitized. |
locale optional | string |
{
"section": "hero",
"field": "title",
...
}Response · 200
{
"section": {
"id": "...",
"page_id": "...",
"section_key": "hero",
"locale": "...",
"data": {},
"version": 0,
"published_version": 0,
"published_at": "2026-01-01T00:00:00Z",
"has_unpublished_changes": true,
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated section. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · section · Section
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
page_id | string (uuid) | |
section_key | string | Example: hero. |
locale | string | |
data | object | The draft (working) copy. |
version | integer | |
published_version | integer | null | Null until first publish. |
published_at | string (date-time) | null | |
has_unpublished_changes | boolean | |
updated_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/api/pages/YOUR_PAGEID/sections/fields" \
-H "Authorization: Bearer $ANDISHI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"section":"hero","field":"title"}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Response · 200
{
"sites": [
{
"id": "...",
"name": "...",
"mode": "instrumented",
"origin": "...",
"status": "active",
"publishable_key": "...",
"publishable_key_created_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
]
}Responses
| Status | Meaning |
|---|---|
200 | The workspace's sites. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · sites · Site
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
name | string | |
mode | string | One of: instrumented, rendered. |
origin | string | null | Must be public HTTPS; private and loopback addresses are refused. |
status | string | Only active sites resolve a publishable key. One of: active, disabled. |
publishable_key | string | null | |
publishable_key_created_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/sites" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Owner, admin or editor only. A workspace may hold exactly one site (409 workspace_site_limit). Any origin must be public HTTPS — localhost and private ranges are refused, resolved at save time.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Request · CreateSiteInput
| Field | Type | Notes |
|---|---|---|
name required | string |
2 optional fields
| Field | Type | Notes |
|---|---|---|
mode optional | string | One of: instrumented, rendered. Defaults to instrumented. |
origin optional | string | null |
{
"name": "...",
...
}Response · 201
{
"site": {
"id": "...",
"name": "...",
"mode": "instrumented",
"origin": "...",
"status": "active",
"publishable_key": "...",
"publishable_key_created_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
201 | The created site, including its publishable key. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
409 | The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse. |
201 payload · site · Site
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
name | string | |
mode | string | One of: instrumented, rendered. |
origin | string | null | Must be public HTTPS; private and loopback addresses are refused. |
status | string | Only active sites resolve a publishable key. One of: active, disabled. |
publishable_key | string | null | |
publishable_key_created_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/sites" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"name":"..."}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
siteId required | string (uuid) |
Response · 200
{
"site": {
"id": "...",
"name": "...",
"mode": "instrumented",
"origin": "...",
"status": "active",
"publishable_key": "...",
"publishable_key_created_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The site. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · site · Site
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
name | string | |
mode | string | One of: instrumented, rendered. |
origin | string | null | Must be public HTTPS; private and loopback addresses are refused. |
status | string | Only active sites resolve a publishable key. One of: active, disabled. |
publishable_key | string | null | |
publishable_key_created_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/sites/YOUR_SITEID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Setting status to disabled stops its publishable key resolving.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
siteId required | string (uuid) |
Request · UpdateSiteInput
3 optional fields
| Field | Type | Notes |
|---|---|---|
name optional | string | |
origin optional | string | null | |
status optional | string | One of: active, disabled. |
{
...
}Response · 200
{
"site": {
"id": "...",
"name": "...",
"mode": "instrumented",
"origin": "...",
"status": "active",
"publishable_key": "...",
"publishable_key_created_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated site. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · site · Site
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
name | string | |
mode | string | One of: instrumented, rendered. |
origin | string | null | Must be public HTTPS; private and loopback addresses are refused. |
status | string | Only active sites resolve a publishable key. One of: active, disabled. |
publishable_key | string | null | |
publishable_key_created_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/sites/YOUR_SITEID" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
siteId required | string (uuid) |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/sites/YOUR_SITEID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Issues a new key and invalidates the old one immediately — sites still shipping the previous value stop resolving, which is the point. Owner, admin or editor only.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
siteId required | string (uuid) |
Response · 200
{
"site": {
"id": "...",
"name": "...",
"mode": "instrumented",
"origin": "...",
"status": "active",
"publishable_key": "...",
"publishable_key_created_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The site with its new key. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · site · Site
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
name | string | |
mode | string | One of: instrumented, rendered. |
origin | string | null | Must be public HTTPS; private and loopback addresses are refused. |
status | string | Only active sites resolve a publishable key. One of: active, disabled. |
publishable_key | string | null | |
publishable_key_created_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/sites/YOUR_SITEID/publishable-key/rotate" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
siteId required | string (uuid) |
Response · 200
{
"pages": [
{
"id": "...",
"site_id": "...",
"slug": "/about",
"title": "...",
"position": 0,
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
]
}Responses
| Status | Meaning |
|---|---|
200 | The pages. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · pages · Page
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
site_id | string (uuid) | |
slug | string | URL path with one leading slash and no trailing slash. Example: /about. |
title | string | |
position | integer | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/sites/YOUR_SITEID/pages" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
siteId required | string (uuid) |
Request · CreatePageInput
| Field | Type | Notes |
|---|---|---|
slug required | string | Example: /about. |
title required | string |
{
"slug": "/about",
"title": "..."
}Response · 201
{
"page": {
"id": "...",
"site_id": "...",
"slug": "/about",
"title": "...",
"position": 0,
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
201 | The created page. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
409 | The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse. |
201 payload · page · Page
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
site_id | string (uuid) | |
slug | string | URL path with one leading slash and no trailing slash. Example: /about. |
title | string | |
position | integer | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/sites/YOUR_SITEID/pages" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"slug":"/about","title":"..."}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
siteId required | string (uuid) | |
pageId required | string (uuid) |
Response · 200
{
"page": {
"id": "...",
"site_id": "...",
"slug": "/about",
"title": "...",
"position": 0,
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The page. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · page · Page
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
site_id | string (uuid) | |
slug | string | URL path with one leading slash and no trailing slash. Example: /about. |
title | string | |
position | integer | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/sites/YOUR_SITEID/pages/YOUR_PAGEID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
siteId required | string (uuid) | |
pageId required | string (uuid) |
Request · UpdatePageInput
3 optional fields
| Field | Type | Notes |
|---|---|---|
slug optional | string | |
title optional | string | |
position optional | integer |
{
...
}Response · 200
{
"page": {
"id": "...",
"site_id": "...",
"slug": "/about",
"title": "...",
"position": 0,
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated page. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
409 | The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse. |
200 payload · page · Page
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
site_id | string (uuid) | |
slug | string | URL path with one leading slash and no trailing slash. Example: /about. |
title | string | |
position | integer | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/sites/YOUR_SITEID/pages/YOUR_PAGEID" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
siteId required | string (uuid) | |
pageId required | string (uuid) |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/sites/YOUR_SITEID/pages/YOUR_PAGEID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Returns both draft and published state for each section.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
pageId required | string (uuid) |
Query parameters
| Field | Type | Notes |
|---|---|---|
locale optional | string | Filter by language code. Defaults to the workspace's default locale. Example: en. |
Response · 200
{
"sections": [
{
"id": "...",
"page_id": "...",
"section_key": "hero",
"locale": "...",
"data": {},
"version": 0,
"published_version": 0,
"published_at": "2026-01-01T00:00:00Z",
"has_unpublished_changes": true,
"updated_at": "2026-01-01T00:00:00Z"
}
]
}Responses
| Status | Meaning |
|---|---|
200 | The sections. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · sections · Section
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
page_id | string (uuid) | |
section_key | string | Example: hero. |
locale | string | |
data | object | The draft (working) copy. |
version | integer | |
published_version | integer | null | Null until first publish. |
published_at | string (date-time) | null | |
has_unpublished_changes | boolean | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/pages/YOUR_PAGEID/sections" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
pageId required | string (uuid) |
Request · PutSectionInput
| Field | Type | Notes |
|---|---|---|
section_key required | string |
3 optional fields
| Field | Type | Notes |
|---|---|---|
data optional | object | Every value goes through the same validation as UpsertFieldInput.value. |
locale optional | string | |
base_version optional | integer | null | Optimistic concurrency. A stale value returns 409 conflict. |
{
"section_key": "...",
...
}Response · 200
{
"section": {
"id": "...",
"page_id": "...",
"section_key": "hero",
"locale": "...",
"data": {},
"version": 0,
"published_version": 0,
"published_at": "2026-01-01T00:00:00Z",
"has_unpublished_changes": true,
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated section. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
409 | The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse. |
200 payload · section · Section
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
page_id | string (uuid) | |
section_key | string | Example: hero. |
locale | string | |
data | object | The draft (working) copy. |
version | integer | |
published_version | integer | null | Null until first publish. |
published_at | string (date-time) | null | |
has_unpublished_changes | boolean | |
updated_at | string (date-time) |
curl -X PUT "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/pages/YOUR_PAGEID/sections" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"section_key":"..."}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
pageId required | string (uuid) |
Request · UpsertFieldInput
| Field | Type | Notes |
|---|---|---|
section required | string | Example: hero. |
field required | string | Example: title. |
2 optional fields
| Field | Type | Notes |
|---|---|---|
value optional | string | object | object | Text, {"html": "…"} for rich text, or {"src": "…", "alt": "…"} for an image. Anything else is refused with invalid_field_value. Rich-text HTML is sanitized. |
locale optional | string |
{
"section": "hero",
"field": "title",
...
}Response · 200
{
"section": {
"id": "...",
"page_id": "...",
"section_key": "hero",
"locale": "...",
"data": {},
"version": 0,
"published_version": 0,
"published_at": "2026-01-01T00:00:00Z",
"has_unpublished_changes": true,
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated section. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · section · Section
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
page_id | string (uuid) | |
section_key | string | Example: hero. |
locale | string | |
data | object | The draft (working) copy. |
version | integer | |
published_version | integer | null | Null until first publish. |
published_at | string (date-time) | null | |
has_unpublished_changes | boolean | |
updated_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/pages/YOUR_PAGEID/sections/fields" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"section":"hero","field":"title"}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
pageId required | string (uuid) | |
sectionKey required | string | Example: hero. |
Query parameters
| Field | Type | Notes |
|---|---|---|
locale optional | string | Filter by language code. Defaults to the workspace's default locale. Example: en. |
Response · 200
{
"section": {
"id": "...",
"page_id": "...",
"section_key": "hero",
"locale": "...",
"data": {},
"version": 0,
"published_version": 0,
"published_at": "2026-01-01T00:00:00Z",
"has_unpublished_changes": true,
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The section. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · section · Section
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
page_id | string (uuid) | |
section_key | string | Example: hero. |
locale | string | |
data | object | The draft (working) copy. |
version | integer | |
published_version | integer | null | Null until first publish. |
published_at | string (date-time) | null | |
has_unpublished_changes | boolean | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/pages/YOUR_PAGEID/sections/YOUR_SECTIONKEY" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
A section keeps its newest 20 snapshots.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
pageId required | string (uuid) | |
sectionKey required | string |
Query parameters
| Field | Type | Notes |
|---|---|---|
locale optional | string | Filter by language code. Defaults to the workspace's default locale. Example: en. |
Response · 200
{
"revisions": [
{
"id": "...",
"section_id": "...",
"version": 0,
"data": {},
"published_by": "...",
"created_at": "2026-01-01T00:00:00Z"
}
]
}Responses
| Status | Meaning |
|---|---|
200 | The revisions. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · revisions · SectionRevision
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
section_id | string (uuid) | |
version | integer | |
data | object | |
published_by | string (uuid) | null | |
created_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/pages/YOUR_PAGEID/sections/YOUR_SECTIONKEY/revisions" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Requires publish rights.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
pageId required | string (uuid) | |
sectionKey required | string | |
version required | integer |
Query parameters
| Field | Type | Notes |
|---|---|---|
locale optional | string | Filter by language code. Defaults to the workspace's default locale. Example: en. |
Response · 200
{
"section": {
"id": "...",
"page_id": "...",
"section_key": "hero",
"locale": "...",
"data": {},
"version": 0,
"published_version": 0,
"published_at": "2026-01-01T00:00:00Z",
"has_unpublished_changes": true,
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The restored section. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · section · Section
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
page_id | string (uuid) | |
section_key | string | Example: hero. |
locale | string | |
data | object | The draft (working) copy. |
version | integer | |
published_version | integer | null | Null until first publish. |
published_at | string (date-time) | null | |
has_unpublished_changes | boolean | |
updated_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/pages/YOUR_PAGEID/sections/YOUR_SECTIONKEY/revisions/YOUR_VERSION/restore" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Requires publish rights. Returns how many sections went live.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
pageId required | string (uuid) |
Response · 200
{
"published": 3
}Responses
| Status | Meaning |
|---|---|
200 | The number of sections published. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · published
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/pages/YOUR_PAGEID/publish" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Tasks
Editorial planning tasks, comments and activity.
Scope: tasks:read.
Query parameters
| Field | Type | Notes |
|---|---|---|
status optional | string | One of: pending, in_review, completed, archived. |
limit optional | integer | Defaults to 100. Max 200. |
offset optional | integer | Number of records to skip. Defaults to 0. |
Response · 200
{
"tasks": [
{
"id": "...",
"title": "...",
"description": "...",
"status": "pending",
"priority": "low",
"assigned_to": [
"..."
],
"created_by": "...",
"post_id": "...",
"due_date": "2026-01-01T00:00:00Z",
"completed": true,
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
],
"pagination": {
"total": 84,
"limit": 50,
"offset": 0
}
}Responses
| Status | Meaning |
|---|---|
200 | A page of tasks. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · tasks · Task
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | |
description | string | |
status | string | One of: pending, in_review, completed, archived. |
priority | string | One of: low, medium, high, critical. |
assigned_to | string (uuid)[] | |
created_by | string (uuid) | null | |
post_id | string (uuid) | null | |
due_date | string (date-time) | null | |
completed | boolean | Derived — true when status is completed. |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/api/tasks" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
Logs a created activity entry. Scope: tasks:write.
Request · CreateTaskInput
| Field | Type | Notes |
|---|---|---|
title required | string |
6 optional fields
| Field | Type | Notes |
|---|---|---|
description optional | string | |
status optional | string | One of: pending, in_review, completed, archived. Defaults to pending. |
priority optional | string | One of: low, medium, high, critical. Defaults to medium. |
assigned_to optional | string (uuid)[] | |
post_id optional | string (uuid) | null | |
due_date optional | string | RFC 3339 or YYYY-MM-DDTHH:MM. |
{
"title": "...",
...
}Response · 201
{
"task": {
"id": "...",
"title": "...",
"description": "...",
"status": "pending",
"priority": "low",
"assigned_to": [
"..."
],
"created_by": "...",
"post_id": "...",
"due_date": "2026-01-01T00:00:00Z",
"completed": true,
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
201 | The created task. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
201 payload · task · Task
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | |
description | string | |
status | string | One of: pending, in_review, completed, archived. |
priority | string | One of: low, medium, high, critical. |
assigned_to | string (uuid)[] | |
created_by | string (uuid) | null | |
post_id | string (uuid) | null | |
due_date | string (date-time) | null | |
completed | boolean | Derived — true when status is completed. |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/api/tasks" \
-H "Authorization: Bearer $ANDISHI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"..."}'Scope: tasks:read.
Path parameters
| Field | Type | Notes |
|---|---|---|
taskId required | string (uuid) |
Response · 200
{
"task": {
"id": "...",
"title": "...",
"description": "...",
"status": "pending",
"priority": "low",
"assigned_to": [
"..."
],
"created_by": "...",
"post_id": "...",
"due_date": "2026-01-01T00:00:00Z",
"completed": true,
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The task. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · task · Task
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | |
description | string | |
status | string | One of: pending, in_review, completed, archived. |
priority | string | One of: low, medium, high, critical. |
assigned_to | string (uuid)[] | |
created_by | string (uuid) | null | |
post_id | string (uuid) | null | |
due_date | string (date-time) | null | |
completed | boolean | Derived — true when status is completed. |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/api/tasks/YOUR_TASKID" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
Status, priority and assignment changes each append an activity entry. Scope: tasks:write.
Path parameters
| Field | Type | Notes |
|---|---|---|
taskId required | string (uuid) |
Request · UpdateTaskInput
9 optional fields
| Field | Type | Notes |
|---|---|---|
title optional | string | |
description optional | string | |
status optional | string | One of: pending, in_review, completed, archived. |
priority optional | string | One of: low, medium, high, critical. |
assigned_to optional | string (uuid)[] | |
post_id optional | string (uuid) | null | |
clear_post optional | boolean | |
due_date optional | string | |
clear_due optional | boolean |
{
...
}Response · 200
{
"task": {
"id": "...",
"title": "...",
"description": "...",
"status": "pending",
"priority": "low",
"assigned_to": [
"..."
],
"created_by": "...",
"post_id": "...",
"due_date": "2026-01-01T00:00:00Z",
"completed": true,
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated task. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · task · Task
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | |
description | string | |
status | string | One of: pending, in_review, completed, archived. |
priority | string | One of: low, medium, high, critical. |
assigned_to | string (uuid)[] | |
created_by | string (uuid) | null | |
post_id | string (uuid) | null | |
due_date | string (date-time) | null | |
completed | boolean | Derived — true when status is completed. |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/api/tasks/YOUR_TASKID" \
-H "Authorization: Bearer $ANDISHI_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'Scope: tasks:write.
Path parameters
| Field | Type | Notes |
|---|---|---|
taskId required | string (uuid) |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/api/tasks/YOUR_TASKID" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
Not paginated. Scope: tasks:read.
Path parameters
| Field | Type | Notes |
|---|---|---|
taskId required | string (uuid) |
Response · 200
{
"comments": [
{
"id": "...",
"task_id": "...",
"user_id": "...",
"content": "...",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z",
"profile": {
"id": "...",
"full_name": "...",
"avatar_url": "...",
"email": "...",
"status": "..."
}
}
]
}Responses
| Status | Meaning |
|---|---|
200 | The task's comments. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · comments · TaskComment
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
task_id | string (uuid) | |
user_id | string (uuid) | null | |
content | string | |
created_at | string (date-time) | |
updated_at | string (date-time) | |
profile | TaskProfile | Joined author summary, present only when user_id is set. |
curl "https://api.andishi.nexuslabsstudio.com/v1/api/tasks/YOUR_TASKID/comments" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
Appends a commented activity entry. Scope: tasks:write.
Path parameters
| Field | Type | Notes |
|---|---|---|
taskId required | string (uuid) |
Request · CreateTaskCommentInput
| Field | Type | Notes |
|---|---|---|
content required | string |
{
"content": "..."
}Response · 201
{
"comment": {
"id": "...",
"task_id": "...",
"user_id": "...",
"content": "...",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z",
"profile": {
"id": "...",
"full_name": "...",
"avatar_url": "...",
"email": "...",
"status": "..."
}
}
}Responses
| Status | Meaning |
|---|---|
201 | The created comment. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
201 payload · comment · TaskComment
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
task_id | string (uuid) | |
user_id | string (uuid) | null | |
content | string | |
created_at | string (date-time) | |
updated_at | string (date-time) | |
profile | TaskProfile | Joined author summary, present only when user_id is set. |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/api/tasks/YOUR_TASKID/comments" \
-H "Authorization: Bearer $ANDISHI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"content":"..."}'Returns the 30 most recent entries. Scope: tasks:read.
Path parameters
| Field | Type | Notes |
|---|---|---|
taskId required | string (uuid) |
Response · 200
{
"activity": [
{
"id": "...",
"task_id": "...",
"user_id": "...",
"action": "created",
"old_value": "...",
"new_value": "...",
"created_at": "2026-01-01T00:00:00Z",
"profile": {
"id": "...",
"full_name": "...",
"avatar_url": "...",
"email": "...",
"status": "..."
}
}
]
}Responses
| Status | Meaning |
|---|---|
200 | The activity log. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · activity · TaskActivity
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
task_id | string (uuid) | |
user_id | string (uuid) | null | |
action | string | One of: created, status_changed, priority_changed, assigned, commented. |
old_value | string | null | |
new_value | string | null | |
created_at | string (date-time) | |
profile | TaskProfile | Joined author summary, present only when user_id is set. |
curl "https://api.andishi.nexuslabsstudio.com/v1/api/tasks/YOUR_TASKID/activity" \ -H "Authorization: Bearer $ANDISHI_API_KEY"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Query parameters
| Field | Type | Notes |
|---|---|---|
status | string | One of: pending, in_review, completed, archived. |
limit | integer | Defaults to 100. Max 200. |
offset optional | integer | Number of records to skip. Defaults to 0. |
Response · 200
{
"tasks": [
{
"id": "...",
"title": "...",
"description": "...",
"status": "pending",
"priority": "low",
"assigned_to": [
"..."
],
"created_by": "...",
"post_id": "...",
"due_date": "2026-01-01T00:00:00Z",
"completed": true,
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
],
"pagination": {
"total": 84,
"limit": 50,
"offset": 0
}
}Responses
| Status | Meaning |
|---|---|
200 | A page of tasks. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · tasks · Task
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | |
description | string | |
status | string | One of: pending, in_review, completed, archived. |
priority | string | One of: low, medium, high, critical. |
assigned_to | string (uuid)[] | |
created_by | string (uuid) | null | |
post_id | string (uuid) | null | |
due_date | string (date-time) | null | |
completed | boolean | Derived — true when status is completed. |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/tasks" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Request · CreateTaskInput
| Field | Type | Notes |
|---|---|---|
title required | string |
6 optional fields
| Field | Type | Notes |
|---|---|---|
description optional | string | |
status optional | string | One of: pending, in_review, completed, archived. Defaults to pending. |
priority optional | string | One of: low, medium, high, critical. Defaults to medium. |
assigned_to optional | string (uuid)[] | |
post_id optional | string (uuid) | null | |
due_date optional | string | RFC 3339 or YYYY-MM-DDTHH:MM. |
{
"title": "...",
...
}Response · 201
{
"task": {
"id": "...",
"title": "...",
"description": "...",
"status": "pending",
"priority": "low",
"assigned_to": [
"..."
],
"created_by": "...",
"post_id": "...",
"due_date": "2026-01-01T00:00:00Z",
"completed": true,
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
201 | The created task. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
201 payload · task · Task
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | |
description | string | |
status | string | One of: pending, in_review, completed, archived. |
priority | string | One of: low, medium, high, critical. |
assigned_to | string (uuid)[] | |
created_by | string (uuid) | null | |
post_id | string (uuid) | null | |
due_date | string (date-time) | null | |
completed | boolean | Derived — true when status is completed. |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/tasks" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"title":"..."}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
taskId required | string (uuid) |
Response · 200
{
"task": {
"id": "...",
"title": "...",
"description": "...",
"status": "pending",
"priority": "low",
"assigned_to": [
"..."
],
"created_by": "...",
"post_id": "...",
"due_date": "2026-01-01T00:00:00Z",
"completed": true,
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The task. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · task · Task
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | |
description | string | |
status | string | One of: pending, in_review, completed, archived. |
priority | string | One of: low, medium, high, critical. |
assigned_to | string (uuid)[] | |
created_by | string (uuid) | null | |
post_id | string (uuid) | null | |
due_date | string (date-time) | null | |
completed | boolean | Derived — true when status is completed. |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/tasks/YOUR_TASKID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
taskId required | string (uuid) |
Request · UpdateTaskInput
9 optional fields
| Field | Type | Notes |
|---|---|---|
title optional | string | |
description optional | string | |
status optional | string | One of: pending, in_review, completed, archived. |
priority optional | string | One of: low, medium, high, critical. |
assigned_to optional | string (uuid)[] | |
post_id optional | string (uuid) | null | |
clear_post optional | boolean | |
due_date optional | string | |
clear_due optional | boolean |
{
...
}Response · 200
{
"task": {
"id": "...",
"title": "...",
"description": "...",
"status": "pending",
"priority": "low",
"assigned_to": [
"..."
],
"created_by": "...",
"post_id": "...",
"due_date": "2026-01-01T00:00:00Z",
"completed": true,
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated task. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · task · Task
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
title | string | |
description | string | |
status | string | One of: pending, in_review, completed, archived. |
priority | string | One of: low, medium, high, critical. |
assigned_to | string (uuid)[] | |
created_by | string (uuid) | null | |
post_id | string (uuid) | null | |
due_date | string (date-time) | null | |
completed | boolean | Derived — true when status is completed. |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/tasks/YOUR_TASKID" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
taskId required | string (uuid) |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/tasks/YOUR_TASKID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
taskId required | string (uuid) |
Response · 200
{
"comments": [
{
"id": "...",
"task_id": "...",
"user_id": "...",
"content": "...",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z",
"profile": {
"id": "...",
"full_name": "...",
"avatar_url": "...",
"email": "...",
"status": "..."
}
}
]
}Responses
| Status | Meaning |
|---|---|
200 | The comments. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · comments · TaskComment
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
task_id | string (uuid) | |
user_id | string (uuid) | null | |
content | string | |
created_at | string (date-time) | |
updated_at | string (date-time) | |
profile | TaskProfile | Joined author summary, present only when user_id is set. |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/tasks/YOUR_TASKID/comments" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
taskId required | string (uuid) |
Request · CreateTaskCommentInput
| Field | Type | Notes |
|---|---|---|
content required | string |
{
"content": "..."
}Response · 201
{
"comment": {
"id": "...",
"task_id": "...",
"user_id": "...",
"content": "...",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z",
"profile": {
"id": "...",
"full_name": "...",
"avatar_url": "...",
"email": "...",
"status": "..."
}
}
}Responses
| Status | Meaning |
|---|---|
201 | The created comment. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
201 payload · comment · TaskComment
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
task_id | string (uuid) | |
user_id | string (uuid) | null | |
content | string | |
created_at | string (date-time) | |
updated_at | string (date-time) | |
profile | TaskProfile | Joined author summary, present only when user_id is set. |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/tasks/YOUR_TASKID/comments" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"content":"..."}'The 30 most recent entries.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
taskId required | string (uuid) |
Response · 200
{
"activity": [
{
"id": "...",
"task_id": "...",
"user_id": "...",
"action": "created",
"old_value": "...",
"new_value": "...",
"created_at": "2026-01-01T00:00:00Z",
"profile": {
"id": "...",
"full_name": "...",
"avatar_url": "...",
"email": "...",
"status": "..."
}
}
]
}Responses
| Status | Meaning |
|---|---|
200 | The activity log. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · activity · TaskActivity
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
task_id | string (uuid) | |
user_id | string (uuid) | null | |
action | string | One of: created, status_changed, priority_changed, assigned, commented. |
old_value | string | null | |
new_value | string | null | |
created_at | string (date-time) | |
profile | TaskProfile | Joined author summary, present only when user_id is set. |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/tasks/YOUR_TASKID/activity" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Workspaces
Workspace configuration, members and invitations (studio surface).
Resolves the active workspace from the JWT, or from the X-Andishi-Workspace header when supplied.
Response · 200 · Bootstrap
{
"user": {
"id": "...",
"email": "...",
"full_name": "...",
"avatar_url": "...",
"status": "...",
"is_super_admin": true
},
"workspace": {
"id": "...",
"slug": "...",
"name": "...",
"status": "active",
"plan_id": "free",
"default_locale": "en",
"timezone": "UTC",
"branding": {},
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
},
"membership": {
"workspace_id": "...",
"user_id": "...",
"role": "owner",
"status": "active",
"created_at": "2026-01-01T00:00:00Z"
},
"workspaces": [
{
"workspace_id": "...",
"slug": "...",
"name": "...",
"role": "owner",
"plan_id": "..."
}
],
"modules": [
{
"id": "writing",
"name": "...",
"tagline": "...",
"category": "...",
"min_plan": "free",
"enabled": true,
"allowed": true,
"sort_order": 0
}
],
"custom_modules": [
{
"id": "...",
"workspace_id": "...",
"slug": "events",
"name": "Events",
"tagline": "...",
"schema": {
"fields": [
{}
]
},
"enabled": true,
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
],
"theme": "purple",
"needs_onboarding": true
}Responses
| Status | Meaning |
|---|---|
200 | The bootstrap payload. Returns Bootstrap. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl "https://api.andishi.nexuslabsstudio.com/v1/me/bootstrap" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Response · 200
{
"workspaces": [
{
"workspace_id": "...",
"slug": "...",
"name": "...",
"role": "owner",
"plan_id": "..."
}
]
}Responses
| Status | Meaning |
|---|---|
200 | The user's memberships. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
200 payload · workspaces · MembershipSummary
Response fields
| Field | Type | Notes |
|---|---|---|
workspace_id | string (uuid) | |
slug | string | |
name | string | |
role | WorkspaceRole | Ordered most to least privileged. owner is set at creation and never granted by invitation. One of: owner, admin, editor, author, contributor, viewer. |
plan_id | string |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
The creator becomes owner. Starts on the free plan and seeds the default Custom Modules. Limited to 5 per hour per user. Platform super-admins cannot create workspaces.
Request · CreateWorkspaceInput
| Field | Type | Notes |
|---|---|---|
name required | string | |
slug required | string | Matches ^[a-z0-9]+(-[a-z0-9]+)*$. |
2 optional fields
| Field | Type | Notes |
|---|---|---|
modules optional | object | Module id → enabled. Defaults to everything the free plan allows. |
theme optional | string | Defaults to purple. |
{
"name": "...",
"slug": "...",
...
}Response · 201
{
"workspace": {
"id": "...",
"slug": "...",
"name": "...",
"status": "active",
"plan_id": "free",
"default_locale": "en",
"timezone": "UTC",
"branding": {},
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
201 | The created workspace. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
409 | The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse. |
429 | Rate limited. /v1/public/* allows 120 requests/minute per client IP with a burst of 240; POST /v1/public/comments and POST /v1/public/subscribers each carry a tighter 5/minute per-IP limit of their own. Back off and retry. Returns ErrorResponse. |
201 payload · workspace · Workspace
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
slug | string | |
name | string | |
status | string | One of: active, suspended. |
plan_id | string | One of: free, basic, pro. |
default_locale | string | Defaults to en. |
timezone | string | Defaults to UTC. |
branding | object | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"name":"...","slug":"..."}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Response · 200
{
"workspace": {
"id": "...",
"slug": "...",
"name": "...",
"status": "active",
"plan_id": "free",
"default_locale": "en",
"timezone": "UTC",
"branding": {},
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The workspace. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · workspace · Workspace
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
slug | string | |
name | string | |
status | string | One of: active, suspended. |
plan_id | string | One of: free, basic, pro. |
default_locale | string | Defaults to en. |
timezone | string | Defaults to UTC. |
branding | object | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Owner or admin only.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Request · UpdateWorkspaceInput
4 optional fields
| Field | Type | Notes |
|---|---|---|
name optional | string | |
timezone optional | string | |
default_locale optional | string | |
branding optional | object |
{
...
}Response · 200
{
"workspace": {
"id": "...",
"slug": "...",
"name": "...",
"status": "active",
"plan_id": "free",
"default_locale": "en",
"timezone": "UTC",
"branding": {},
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated workspace. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · workspace · Workspace
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
slug | string | |
name | string | |
status | string | One of: active, suspended. |
plan_id | string | One of: free, basic, pro. |
default_locale | string | Defaults to en. |
timezone | string | Defaults to UTC. |
branding | object | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Response · 200
{
"modules": [
{
"id": "writing",
"name": "...",
"tagline": "...",
"category": "...",
"min_plan": "free",
"enabled": true,
"allowed": true,
"sort_order": 0
}
]
}Responses
| Status | Meaning |
|---|---|
200 | The module catalog for this workspace. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · modules · ModuleDescriptor
Response fields
| Field | Type | Notes |
|---|---|---|
id | string | Example: writing. |
name | string | |
tagline | string | |
category | string | |
min_plan | string | One of: free, basic, pro. |
enabled | boolean | Turned on for this workspace. |
allowed | boolean | Permitted by the workspace's plan. |
sort_order | integer |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/modules" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Owner or admin only. Enabling a module the plan does not carry fails with 403 module_not_allowed.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Request
1 optional field
| Field | Type | Notes |
|---|---|---|
modules optional | object |
{
...
}Response · 200
{
"modules": [
{
"id": "writing",
"name": "...",
"tagline": "...",
"category": "...",
"min_plan": "free",
"enabled": true,
"allowed": true,
"sort_order": 0
}
]
}Responses
| Status | Meaning |
|---|---|
200 | The refreshed module catalog. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · modules · ModuleDescriptor
Response fields
| Field | Type | Notes |
|---|---|---|
id | string | Example: writing. |
name | string | |
tagline | string | |
category | string | |
min_plan | string | One of: free, basic, pro. |
enabled | boolean | Turned on for this workspace. |
allowed | boolean | Permitted by the workspace's plan. |
sort_order | integer |
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/modules" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Response · 200
{
"settings": [
{
"workspace_id": "...",
"key": "...",
"value": {}
}
]
}Responses
| Status | Meaning |
|---|---|
200 | Every setting row. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · settings · WorkspaceSetting
Response fields
| Field | Type | Notes |
|---|---|---|
workspace_id | string (uuid) | |
key | string | |
value | object |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/settings" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
key required | string | Example: workspace. |
Response · 200
{
"setting": {
"workspace_id": "...",
"key": "...",
"value": {}
}
}Responses
| Status | Meaning |
|---|---|
200 | The setting. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · setting · WorkspaceSetting
Response fields
| Field | Type | Notes |
|---|---|---|
workspace_id | string (uuid) | |
key | string | |
value | object |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/settings/YOUR_KEY" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Owner or admin only.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
key required | string | Example: workspace. |
Request
1 optional field
| Field | Type | Notes |
|---|---|---|
value optional | object |
{
...
}Response · 200
{
"setting": {
"workspace_id": "...",
"key": "...",
"value": {}
}
}Responses
| Status | Meaning |
|---|---|
200 | The stored setting. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · setting · WorkspaceSetting
Response fields
| Field | Type | Notes |
|---|---|---|
workspace_id | string (uuid) | |
key | string | |
value | object |
curl -X PUT "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/settings/YOUR_KEY" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Response · 200
{
"members": [
{
"user_id": "...",
"email": "...",
"full_name": "...",
"avatar_url": "...",
"role": "owner",
"status": "...",
"created_at": "2026-01-01T00:00:00Z"
}
]
}Responses
| Status | Meaning |
|---|---|
200 | The members. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · members · MemberView
Response fields
| Field | Type | Notes |
|---|---|---|
user_id | string (uuid) | |
email | string (email) | |
full_name | string | |
avatar_url | string | |
role | WorkspaceRole | Ordered most to least privileged. owner is set at creation and never granted by invitation. One of: owner, admin, editor, author, contributor, viewer. |
status | string | |
created_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/members" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Owner or admin only. The owner role cannot be granted or changed this way (403 cannot_modify_owner).
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
userId required | string (uuid) |
Request · UpdateMemberInput
| Field | Type | Notes |
|---|---|---|
role required | string | One of: admin, editor, author, contributor, viewer. |
{
"role": "admin"
}Response · 200
{
"members": [
{
"user_id": "...",
"email": "...",
"full_name": "...",
"avatar_url": "...",
"role": "owner",
"status": "...",
"created_at": "2026-01-01T00:00:00Z"
}
]
}Responses
| Status | Meaning |
|---|---|
200 | The refreshed member list. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · members · MemberView
Response fields
| Field | Type | Notes |
|---|---|---|
user_id | string (uuid) | |
email | string (email) | |
full_name | string | |
avatar_url | string | |
role | WorkspaceRole | Ordered most to least privileged. owner is set at creation and never granted by invitation. One of: owner, admin, editor, author, contributor, viewer. |
status | string | |
created_at | string (date-time) |
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/members/YOUR_USERID" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"role":"admin"}'Admins may remove anyone but the owner; members may remove themselves.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
userId required | string (uuid) |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/members/YOUR_USERID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Owner or admin only.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Response · 200
{
"invitations": [
{
"id": "...",
"workspace_id": "...",
"email": "...",
"role": "owner",
"invited_by": "...",
"expires_at": "2026-01-01T00:00:00Z",
"accepted_at": "2026-01-01T00:00:00Z",
"revoked_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z"
}
]
}Responses
| Status | Meaning |
|---|---|
200 | The invitations. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · invitations · Invitation
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
workspace_id | string (uuid) | |
email | string (email) | |
role | WorkspaceRole | Ordered most to least privileged. owner is set at creation and never granted by invitation. One of: owner, admin, editor, author, contributor, viewer. |
invited_by | string (uuid) | |
expires_at | string (date-time) | Seven days after creation. |
accepted_at | string (date-time) | null | |
revoked_at | string (date-time) | null | |
created_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/invitations" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Owner or admin only. Emails a link valid for seven days. Limited to 10 per minute per workspace, and refused once the plan's seat limit is reached.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Request · InviteInput
| Field | Type | Notes |
|---|---|---|
email required | string (email) | |
role required | string | One of: admin, editor, author, contributor, viewer. |
{
"email": "...",
"role": "admin"
}Response · 201
{
"invitation": {
"id": "...",
"workspace_id": "...",
"email": "...",
"role": "owner",
"invited_by": "...",
"expires_at": "2026-01-01T00:00:00Z",
"accepted_at": "2026-01-01T00:00:00Z",
"revoked_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z"
},
"dev_invite_token": "..."
}Responses
| Status | Meaning |
|---|---|
201 | The invitation. In --dev mode only, also carries dev_invite_token so a local flow can be completed without email. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
409 | The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse. |
429 | Rate limited. /v1/public/* allows 120 requests/minute per client IP with a burst of 240; POST /v1/public/comments and POST /v1/public/subscribers each carry a tighter 5/minute per-IP limit of their own. Back off and retry. Returns ErrorResponse. |
201 payload · invitation · Invitation
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
workspace_id | string (uuid) | |
email | string (email) | |
role | WorkspaceRole | Ordered most to least privileged. owner is set at creation and never granted by invitation. One of: owner, admin, editor, author, contributor, viewer. |
invited_by | string (uuid) | |
expires_at | string (date-time) | Seven days after creation. |
accepted_at | string (date-time) | null | |
revoked_at | string (date-time) | null | |
created_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/invitations" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"email":"...","role":"admin"}'Owner or admin only.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
inviteId required | string (uuid) |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/invitations/YOUR_INVITEID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
The signed-in user's email must match the invited address (403 invite_email_mismatch). Refused when the plan's seat limit is already reached.
Request · AcceptInvitationInput
| Field | Type | Notes |
|---|---|---|
token required | string |
{
"token": "..."
}Response · 200
{
"workspace": {
"id": "...",
"slug": "...",
"name": "...",
"status": "active",
"plan_id": "free",
"default_locale": "en",
"timezone": "UTC",
"branding": {},
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
},
"membership": {
"workspace_id": "...",
"user_id": "...",
"role": "owner",
"status": "active",
"created_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The joined workspace and the new membership. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | The invitation is unknown, expired or already used. Returns ErrorResponse. |
409 | The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse. |
200 payload · workspace · Workspace
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
slug | string | |
name | string | |
status | string | One of: active, suspended. |
plan_id | string | One of: free, basic, pro. |
default_locale | string | Defaults to en. |
timezone | string | Defaults to UTC. |
branding | object | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/invitations/accept" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"token":"..."}'Account
Signup, login, profile and password management (studio surface).
Rate limited per IP. Sends a verification email.
Request · SignupInput
| Field | Type | Notes |
|---|---|---|
email required | string (email) | |
password required | string (password) |
2 optional fields
| Field | Type | Notes |
|---|---|---|
full_name optional | string | |
next optional | string | Relative path to land on once the address is verified, carried into the verification email link. Non-relative values are ignored. |
{
"email": "...",
"password": "...",
...
}Response · 201 · AuthResult
{
"token": "...",
"user": {
"id": "...",
"email": "...",
"full_name": "...",
"avatar_url": "...",
"status": "active",
"verified_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
201 | The new account and its session. Returns AuthResult. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
409 | The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse. |
429 | Rate limited. /v1/public/* allows 120 requests/minute per client IP with a burst of 240; POST /v1/public/comments and POST /v1/public/subscribers each carry a tighter 5/minute per-IP limit of their own. Back off and retry. Returns ErrorResponse. |
201 payload · token
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/account/signup" \
-H "Content-Type: application/json" \
-d '{"email":"...","password":"..."}'Rate limited per IP. Repeated failures lock the account temporarily (423 account_locked).
Request · LoginInput
| Field | Type | Notes |
|---|---|---|
email required | string (email) | |
password required | string (password) |
{
"email": "...",
"password": "..."
}Response · 200 · AuthResult
{
"token": "...",
"user": {
"id": "...",
"email": "...",
"full_name": "...",
"avatar_url": "...",
"status": "active",
"verified_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The session. Returns AuthResult. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
423 | Too many failed attempts; the account is temporarily locked. Returns ErrorResponse. |
429 | Rate limited. /v1/public/* allows 120 requests/minute per client IP with a burst of 240; POST /v1/public/comments and POST /v1/public/subscribers each carry a tighter 5/minute per-IP limit of their own. Back off and retry. Returns ErrorResponse. |
200 payload · token
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/account/login" \
-H "Content-Type: application/json" \
-d '{"email":"...","password":"..."}'Revokes the current token until it would have expired anyway.
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/account/logout" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Always succeeds, whether or not the address exists.
Request · PasswordResetRequestInput
| Field | Type | Notes |
|---|---|---|
email required | string (email) |
{
"email": "..."
}Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
429 | Rate limited. /v1/public/* allows 120 requests/minute per client IP with a burst of 240; POST /v1/public/comments and POST /v1/public/subscribers each carry a tighter 5/minute per-IP limit of their own. Back off and retry. Returns ErrorResponse. |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/account/password-reset/request" \
-H "Content-Type: application/json" \
-d '{"email":"..."}'Request · PasswordResetConfirmInput
| Field | Type | Notes |
|---|---|---|
token required | string | |
password required | string (password) |
{
"token": "...",
"password": "..."
}Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
429 | Rate limited. /v1/public/* allows 120 requests/minute per client IP with a burst of 240; POST /v1/public/comments and POST /v1/public/subscribers each carry a tighter 5/minute per-IP limit of their own. Back off and retry. Returns ErrorResponse. |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/account/password-reset/confirm" \
-H "Content-Type: application/json" \
-d '{"token":"...","password":"..."}'Request · VerifyEmailInput
| Field | Type | Notes |
|---|---|---|
token required | string |
{
"token": "..."
}Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
429 | Rate limited. /v1/public/* allows 120 requests/minute per client IP with a burst of 240; POST /v1/public/comments and POST /v1/public/subscribers each carry a tighter 5/minute per-IP limit of their own. Back off and retry. Returns ErrorResponse. |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/account/verify" \
-H "Content-Type: application/json" \
-d '{"token":"..."}'Needs a session but stays reachable while unverified. Limited to one send per minute per user.
Request · ResendVerificationInput
1 optional field
| Field | Type | Notes |
|---|---|---|
next optional | string | Relative path to land on once the address is verified, carried into the verification email link. Non-relative values are ignored. |
{
...
}Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
429 | Rate limited. /v1/public/* allows 120 requests/minute per client IP with a burst of 240; POST /v1/public/comments and POST /v1/public/subscribers each carry a tighter 5/minute per-IP limit of their own. Back off and retry. Returns ErrorResponse. |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/account/resend-verification" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Reachable while unverified, so the studio can render that state.
Response · 200
{
"user": {
"id": "...",
"email": "...",
"full_name": "...",
"avatar_url": "...",
"status": "active",
"verified_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The current user. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · user · User
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
email | string (email) | |
full_name | string | |
avatar_url | string | |
status | string | One of: active, inactive. |
verified_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/account/me" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Request · UpdateProfileInput
| Field | Type | Notes |
|---|---|---|
full_name required | string | Max length 100. |
{
"full_name": "..."
}Response · 200
{
"user": {
"id": "...",
"email": "...",
"full_name": "...",
"avatar_url": "...",
"status": "active",
"verified_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated user. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · user · User
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
email | string (email) | |
full_name | string | |
avatar_url | string | |
status | string | One of: active, inactive. |
verified_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/account/me" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"full_name":"..."}'Images only, 2 MB maximum.
Request
2 optional fields
| Field | Type | Notes |
|---|---|---|
data_base64 optional | string | |
content_type optional | string | Example: image/png. |
{
...
}Response · 200
{
"user": {
"id": "...",
"email": "...",
"full_name": "...",
"avatar_url": "...",
"status": "active",
"verified_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated user. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
413 | The upload exceeds the workspace's limit. Codes: upload_too_large, avatar_too_large, storage_limit_exceeded. Returns ErrorResponse. |
501 | No object store is configured. Returns ErrorResponse. |
200 payload · user · User
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
email | string (email) | |
full_name | string | |
avatar_url | string | |
status | string | One of: active, inactive. |
verified_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/account/me/avatar" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Response · 200
{
"user": {
"id": "...",
"email": "...",
"full_name": "...",
"avatar_url": "...",
"status": "active",
"verified_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated user. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · user · User
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
email | string (email) | |
full_name | string | |
avatar_url | string | |
status | string | One of: active, inactive. |
verified_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/account/me/avatar" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Signs out every other session on success.
Request · ChangePasswordInput
| Field | Type | Notes |
|---|---|---|
current_password required | string (password) | |
new_password required | string (password) |
{
"current_password": "...",
"new_password": "..."
}Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/account/change-password" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"current_password":"...","new_password":"..."}'API Keys
Minting and revoking secret keys (studio surface).
Owner or admin only. Never returns the secret itself.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Response · 200
{
"api_keys": [
{
"id": "...",
"name": "...",
"key_prefix": "andishi_live_51fA9",
"scopes": [
"articles:read"
],
"last_used_at": "2026-01-01T00:00:00Z",
"revoked_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z"
}
]
}Responses
| Status | Meaning |
|---|---|
200 | The workspace's keys. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · api_keys · APIKey
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
name | string | |
key_prefix | string | First 17 characters, for identifying the key in a list. Example: andishi_live_51fA9. |
scopes | Scope[] | |
last_used_at | string (date-time) | null | |
revoked_at | string (date-time) | null | |
created_at | string (date-time) |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/api-keys" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Owner or admin only. The `secret` is returned in this one response and never again — only a hash and the visible prefix are stored, so support cannot recover it. Unrecognised scopes are silently dropped; if none survive the request fails with 400 invalid_scope.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Request · CreateAPIKeyInput
| Field | Type | Notes |
|---|---|---|
name required | string | |
scopes required | Scope[] | Unknown scopes are dropped; an empty result is invalid_scope. |
{
"name": "...",
"scopes": [
"articles:read"
]
}Response · 201 · CreatedAPIKey
{
"api_key": {
"id": "...",
"name": "...",
"key_prefix": "andishi_live_51fA9",
"scopes": [
"articles:read"
],
"last_used_at": "2026-01-01T00:00:00Z",
"revoked_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z"
},
"secret": "andishi_live_51fA9c…"
}Responses
| Status | Meaning |
|---|---|
201 | The key metadata and, once only, the secret. Returns CreatedAPIKey. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
201 payload · api_key · APIKey
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
name | string | |
key_prefix | string | First 17 characters, for identifying the key in a list. Example: andishi_live_51fA9. |
scopes | Scope[] | |
last_used_at | string (date-time) | null | |
revoked_at | string (date-time) | null | |
created_at | string (date-time) |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/api-keys" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"name":"...","scopes":[]}'Owner or admin only. Takes effect on the next request; there is no cache to wait out. Keys cannot be edited — change scopes by minting a replacement and revoking this one.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
keyId required | string (uuid) |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/api-keys/YOUR_KEYID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Webhooks
Outbound event subscriptions (studio surface).
Secrets are omitted from this listing.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Response · 200
{
"webhooks": [
{
"id": "...",
"url": "...",
"events": [
"articles.published"
],
"is_active": true,
"description": "...",
"last_triggered_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z",
"secret": "..."
}
]
}Responses
| Status | Meaning |
|---|---|
200 | The webhooks. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · webhooks · Webhook
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
url | string (uri) | |
events | WebhookEvent[] | |
is_active | boolean | |
description | string | |
last_triggered_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) | |
secret | string | Returned only on create and on an update that rotates it. Sign verification uses HMAC-SHA256(secret, rawBody) compared against the X-Andishi-Signature header. |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/webhooks" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Owner or admin only. The URL must be public — the same validation the delivery worker re-runs on every hop, so a bad URL fails at save time rather than silently at delivery. The signing secret is returned here.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Request · CreateWebhookInput
| Field | Type | Notes |
|---|---|---|
url required | string (uri) | Must be a public HTTPS address; private ranges are refused. |
2 optional fields
| Field | Type | Notes |
|---|---|---|
events optional | WebhookEvent[] | |
description optional | string |
{
"url": "...",
...
}Response · 201
{
"webhook": {
"id": "...",
"url": "...",
"events": [
"articles.published"
],
"is_active": true,
"description": "...",
"last_triggered_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z",
"secret": "..."
}
}Responses
| Status | Meaning |
|---|---|
201 | The created webhook, including its signing secret. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
201 payload · webhook · Webhook
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
url | string (uri) | |
events | WebhookEvent[] | |
is_active | boolean | |
description | string | |
last_triggered_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) | |
secret | string | Returned only on create and on an update that rotates it. Sign verification uses HMAC-SHA256(secret, rawBody) compared against the X-Andishi-Signature header. |
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/webhooks" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"url":"..."}'Owner or admin only. The response carries secret only when rotate_secret was set.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
webhookId required | string (uuid) |
Request · UpdateWebhookInput
5 optional fields
| Field | Type | Notes |
|---|---|---|
url optional | string (uri) | |
events optional | WebhookEvent[] | |
description optional | string | |
is_active optional | boolean | |
rotate_secret optional | boolean | Issues a new signing secret and returns it in the response. |
{
...
}Response · 200
{
"webhook": {
"id": "...",
"url": "...",
"events": [
"articles.published"
],
"is_active": true,
"description": "...",
"last_triggered_at": "2026-01-01T00:00:00Z",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z",
"secret": "..."
}
}Responses
| Status | Meaning |
|---|---|
200 | The updated webhook. |
400 | The request body is missing a required field or failed validation. Common codes: invalid_request, title_required, name_required, slug_required, content_required, email_required, id_required, url_required, invalid_slug, invalid_status, invalid_role, invalid_email, invalid_url, invalid_scope, invalid_field_value, comment_too_long, password_too_short. Returns ErrorResponse. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
200 payload · webhook · Webhook
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
url | string (uri) | |
events | WebhookEvent[] | |
is_active | boolean | |
description | string | |
last_triggered_at | string (date-time) | null | |
created_at | string (date-time) | |
updated_at | string (date-time) | |
secret | string | Returned only on create and on an update that rotates it. Sign verification uses HMAC-SHA256(secret, rawBody) compared against the X-Andishi-Signature header. |
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/webhooks/YOUR_WEBHOOKID" \
-H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{}'Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
webhookId required | string (uuid) |
Responses
| Status | Meaning |
|---|---|
204 | Success. No response body. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
404 | No such resource in this workspace. Public read routes also return this for a draft, or for a Custom Module that is disabled, so a reader can never distinguish "disabled" from "never existed". Returns ErrorResponse. |
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/webhooks/YOUR_WEBHOOKID" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
Owner or admin only. Returns the 50 most recent.
Path parameters
| Field | Type | Notes |
|---|---|---|
id required | string (uuid) | Workspace UUID. |
Query parameters
| Field | Type | Notes |
|---|---|---|
webhook_id | string (uuid) |
Response · 200
{
"deliveries": [
{
"id": "...",
"workspace_id": "...",
"webhook_id": "...",
"outbox_id": 0,
"topic": "...",
"status": "...",
"attempts": 0,
"max_attempts": 0,
"next_attempt_at": "2026-01-01T00:00:00Z",
"last_status_code": 0,
"last_error": "...",
"created_at": "2026-01-01T00:00:00Z",
"completed_at": "2026-01-01T00:00:00Z"
}
]
}Responses
| Status | Meaning |
|---|---|
200 | The deliveries. |
401 | No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse. |
403 | Authenticated, but not permitted. Codes: missing_scope (the API key lacks the route's scope), forbidden (the member's role is too low), module_disabled, module_not_allowed, not_workspace_member, workspace_suspended, cannot_publish, cannot_edit_live, email_not_verified, account_inactive. Returns ErrorResponse. |
200 payload · deliveries · WebhookDelivery
Response fields
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
workspace_id | string (uuid) | |
webhook_id | string (uuid) | |
outbox_id | integer (int64) | null | |
topic | string | |
status | string | |
attempts | integer | |
max_attempts | integer | |
next_attempt_at | string (date-time) | |
last_status_code | integer | null | |
last_error | string | |
created_at | string (date-time) | |
completed_at | string (date-time) | null |
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/webhook-deliveries" \ -H "Authorization: Bearer $ANDISHI_SESSION_JWT"