This is the secret-key surface, which supports full CRUD from any server in any language, drafts included. Switch to React SDK for browser-side reads.

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 DevelopersNew 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
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 asHeader Authorization: Bearer andishi_live_...Query param ?key=pk_...
AccessFull CRUD, everything the key's scopes allow, drafts includedPublished content, one site, read only
Safe in a browser?No, never expose this key client-sideYes, it is designed for it
Rate limitNone enforced at this layer120 requests/minute, burst up to 240

Sending the secret key

curl
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

GET /v1/api/articles?limit=2
{
  "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

400 example
{
  "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:

FieldTypeNotes
401 statusNo credential, or it is wrong, revoked or expired.
403 statusAuthenticated but not permitted: missing scope, role too low, or the module is disabled.
404 statusNo such resource here. Public reads also return this for a draft, so a reader cannot probe for unpublished content.
409 statusCollides with existing state: slug_taken, email_taken, or a section version conflict.
429 statusRate 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.

GET/v1/api/articlesList articles
Secret key · articles:readlistArticles

Includes drafts. Scope: articles:read.

Query parameters

FieldTypeNotes
status optionalstringOne of: draft, in_review, scheduled, published, archived.
locale optionalstringFilter by language code. Defaults to the workspace's default locale. Example: en.
limit optionalintegerPage size. Out-of-range values fall back to the endpoint default. Defaults to 50. Max 100.
offset optionalintegerNumber of records to skip. Defaults to 0.

Response · 200

Response
{
  "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

StatusMeaning
200A page of articles.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
title stringExample: Elections 2027: what to watch.
slug stringExample: elections-2027-what-to-watch.
subtitle stringDeck / standfirst.
excerpt stringAlias of subtitle, for template convenience.
body stringSanitized rich-text HTML.
status stringOne of: draft, in_review, scheduled, published, archived.
locale stringExample: 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 stringTarget keyword for the SEO analyser.
read_time_minutes integerEstimated 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
curl "https://api.andishi.nexuslabsstudio.com/v1/api/articles" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
POST/v1/api/articlesCreate an article
Secret key · articles:writecreateArticle

Creating directly at published or scheduled needs publish rights. Scope: articles:write.

Request · CreateArticleInput

FieldTypeNotes
title requiredstring
15 optional fields
FieldTypeNotes
slug optionalstringLowercase, digits and dashes. Derived from title when omitted. Matches ^[a-z0-9]+(-[a-z0-9]+)*$.
subtitle optionalstring
body optionalstringHTML. Sanitized server-side.
image_url optionalstring
locale optionalstring
category_id optionalstring (uuid) | null
author_id optionalstring (uuid) | nullDefaults to the authenticated user.
tags optionalstring[]
status optionalstringReaching 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 optionalboolean
is_breaking optionalboolean
is_opinion optionalboolean
focus_keyword optionalstring
read_time_minutes optionalintegerOverrides the estimate when > 0.
published_at optionalstring (date-time)RFC 3339. Set automatically when status becomes published.
Request
{
  "title": "...",
  ...
}

Response · 201

Response
{
  "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

StatusMeaning
201The created article.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
409The 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
FieldTypeNotes
id string (uuid)
title stringExample: Elections 2027: what to watch.
slug stringExample: elections-2027-what-to-watch.
subtitle stringDeck / standfirst.
excerpt stringAlias of subtitle, for template convenience.
body stringSanitized rich-text HTML.
status stringOne of: draft, in_review, scheduled, published, archived.
locale stringExample: 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 stringTarget keyword for the SEO analyser.
read_time_minutes integerEstimated 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
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/api/articles" \
  -H "Authorization: Bearer $ANDISHI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"..."}'
GET/v1/api/articles/{articleId}Fetch one article
Secret key · articles:readgetArticle

Returns drafts too. Scope: articles:read.

Path parameters

FieldTypeNotes
articleId requiredstring (uuid)

Response · 200

Response
{
  "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

StatusMeaning
200The article.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
title stringExample: Elections 2027: what to watch.
slug stringExample: elections-2027-what-to-watch.
subtitle stringDeck / standfirst.
excerpt stringAlias of subtitle, for template convenience.
body stringSanitized rich-text HTML.
status stringOne of: draft, in_review, scheduled, published, archived.
locale stringExample: 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 stringTarget keyword for the SEO analyser.
read_time_minutes integerEstimated 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
curl "https://api.andishi.nexuslabsstudio.com/v1/api/articles/YOUR_ARTICLEID" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
PATCH/v1/api/articles/{articleId}Update an article
Secret key · articles:writeupdateArticle

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

FieldTypeNotes
articleId requiredstring (uuid)

Request · UpdateArticleInput

18 optional fields
FieldTypeNotes
title optionalstring
slug optionalstringMatches ^[a-z0-9]+(-[a-z0-9]+)*$.
subtitle optionalstring
body optionalstring
image_url optionalstring
locale optionalstring
category_id optionalstring (uuid) | null
clear_category optionalbooleanSet true to unset the category (wins over category_id).
author_id optionalstring (uuid)
tags optionalstring[]
status optionalstringOne of: draft, in_review, scheduled, published, archived.
is_featured optionalboolean
is_breaking optionalboolean
is_opinion optionalboolean
focus_keyword optionalstring
read_time_minutes optionalinteger
published_at optionalstringRFC 3339, or an empty string to clear it.
autosave optionalbooleanMarks an editor-issued background save. Suppresses revision snapshotting so keystroke batches do not evict real checkpoints. Defaults to false.
Request
{
  ...
}

Response · 200

Response
{
  "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

StatusMeaning
200The updated article.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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.
409The 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
FieldTypeNotes
id string (uuid)
title stringExample: Elections 2027: what to watch.
slug stringExample: elections-2027-what-to-watch.
subtitle stringDeck / standfirst.
excerpt stringAlias of subtitle, for template convenience.
body stringSanitized rich-text HTML.
status stringOne of: draft, in_review, scheduled, published, archived.
locale stringExample: 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 stringTarget keyword for the SEO analyser.
read_time_minutes integerEstimated 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
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 '{}'
DELETE/v1/api/articles/{articleId}Delete an article
Secret key · articles:writedeleteArticle

Soft delete; trashed rows are purged after 30 days. Deleting live content requires publish rights. Scope: articles:write.

Path parameters

FieldTypeNotes
articleId requiredstring (uuid)

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/api/articles/YOUR_ARTICLEID" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
GET/v1/workspaces/{id}/articlesList articles (studio)
SessionlistWorkspaceArticles

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Query parameters

FieldTypeNotes
status stringOne of: draft, in_review, scheduled, published, archived.
locale optionalstringFilter by language code. Defaults to the workspace's default locale. Example: en.
limit optionalintegerPage size. Out-of-range values fall back to the endpoint default. Defaults to 50. Max 100.
offset optionalintegerNumber of records to skip. Defaults to 0.

Response · 200

Response
{
  "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

StatusMeaning
200A page of articles.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
title stringExample: Elections 2027: what to watch.
slug stringExample: elections-2027-what-to-watch.
subtitle stringDeck / standfirst.
excerpt stringAlias of subtitle, for template convenience.
body stringSanitized rich-text HTML.
status stringOne of: draft, in_review, scheduled, published, archived.
locale stringExample: 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 stringTarget keyword for the SEO analyser.
read_time_minutes integerEstimated 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
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/articles" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/workspaces/{id}/articlesCreate an article (studio)
SessioncreateWorkspaceArticle

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Request · CreateArticleInput

FieldTypeNotes
title requiredstring
15 optional fields
FieldTypeNotes
slug optionalstringLowercase, digits and dashes. Derived from title when omitted. Matches ^[a-z0-9]+(-[a-z0-9]+)*$.
subtitle optionalstring
body optionalstringHTML. Sanitized server-side.
image_url optionalstring
locale optionalstring
category_id optionalstring (uuid) | null
author_id optionalstring (uuid) | nullDefaults to the authenticated user.
tags optionalstring[]
status optionalstringReaching 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 optionalboolean
is_breaking optionalboolean
is_opinion optionalboolean
focus_keyword optionalstring
read_time_minutes optionalintegerOverrides the estimate when > 0.
published_at optionalstring (date-time)RFC 3339. Set automatically when status becomes published.
Request
{
  "title": "...",
  ...
}

Response · 201

Response
{
  "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

StatusMeaning
201The created article.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
409The 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
FieldTypeNotes
id string (uuid)
title stringExample: Elections 2027: what to watch.
slug stringExample: elections-2027-what-to-watch.
subtitle stringDeck / standfirst.
excerpt stringAlias of subtitle, for template convenience.
body stringSanitized rich-text HTML.
status stringOne of: draft, in_review, scheduled, published, archived.
locale stringExample: 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 stringTarget keyword for the SEO analyser.
read_time_minutes integerEstimated 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
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":"..."}'
GET/v1/workspaces/{id}/articles/{articleId}Fetch an article (studio)
SessiongetWorkspaceArticle

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
articleId requiredstring (uuid)

Response · 200

Response
{
  "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

StatusMeaning
200The article.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
title stringExample: Elections 2027: what to watch.
slug stringExample: elections-2027-what-to-watch.
subtitle stringDeck / standfirst.
excerpt stringAlias of subtitle, for template convenience.
body stringSanitized rich-text HTML.
status stringOne of: draft, in_review, scheduled, published, archived.
locale stringExample: 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 stringTarget keyword for the SEO analyser.
read_time_minutes integerEstimated 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
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/articles/YOUR_ARTICLEID" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
PATCH/v1/workspaces/{id}/articles/{articleId}Update an article (studio)
SessionupdateWorkspaceArticle

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
articleId requiredstring (uuid)

Request · UpdateArticleInput

18 optional fields
FieldTypeNotes
title optionalstring
slug optionalstringMatches ^[a-z0-9]+(-[a-z0-9]+)*$.
subtitle optionalstring
body optionalstring
image_url optionalstring
locale optionalstring
category_id optionalstring (uuid) | null
clear_category optionalbooleanSet true to unset the category (wins over category_id).
author_id optionalstring (uuid)
tags optionalstring[]
status optionalstringOne of: draft, in_review, scheduled, published, archived.
is_featured optionalboolean
is_breaking optionalboolean
is_opinion optionalboolean
focus_keyword optionalstring
read_time_minutes optionalinteger
published_at optionalstringRFC 3339, or an empty string to clear it.
autosave optionalbooleanMarks an editor-issued background save. Suppresses revision snapshotting so keystroke batches do not evict real checkpoints. Defaults to false.
Request
{
  ...
}

Response · 200

Response
{
  "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

StatusMeaning
200The updated article.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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.
409The 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
FieldTypeNotes
id string (uuid)
title stringExample: Elections 2027: what to watch.
slug stringExample: elections-2027-what-to-watch.
subtitle stringDeck / standfirst.
excerpt stringAlias of subtitle, for template convenience.
body stringSanitized rich-text HTML.
status stringOne of: draft, in_review, scheduled, published, archived.
locale stringExample: 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 stringTarget keyword for the SEO analyser.
read_time_minutes integerEstimated 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
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 '{}'
DELETE/v1/workspaces/{id}/articles/{articleId}Delete an article (studio)
SessiondeleteWorkspaceArticle

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
articleId requiredstring (uuid)

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/articles/YOUR_ARTICLEID" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/workspaces/{id}/articles/{articleId}/lockAcquire or refresh the editing lock
SessionacquireArticleLock

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

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
articleId requiredstring (uuid)

Response · 200

Response
{
  "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

StatusMeaning
200The current lock holder.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
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 booleanWhether the requesting user holds the lock.
curl
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/articles/YOUR_ARTICLEID/lock" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
DELETE/v1/workspaces/{id}/articles/{articleId}/lockRelease the editing lock
SessionreleaseArticleLock

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
articleId requiredstring (uuid)

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/articles/YOUR_ARTICLEID/lock" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
GET/v1/workspaces/{id}/articles/{articleId}/seoRead the stored SEO analysis
SessiongetArticleSEO

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
articleId requiredstring (uuid)

Response · 200

Response
{
  "seo": {
    "article_id": "...",
    "score": 0,
    "max_score": 0,
    "grade": "B",
    "focus_keyword": "...",
    "data": {},
    "updated_at": "2026-01-01T00:00:00Z"
  }
}

Responses

StatusMeaning
200The SEO score.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
article_id string (uuid)
score integer
max_score integer
grade stringExample: B.
focus_keyword string
data objectRaw analyser output.
updated_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/articles/YOUR_ARTICLEID/seo" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
PUT/v1/workspaces/{id}/articles/{articleId}/seoStore an SEO analysis result
SessionputArticleSEO

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
articleId requiredstring (uuid)

Request · UpsertSEOInput

5 optional fields
FieldTypeNotes
score optionalinteger
max_score optionalinteger
grade optionalstring
focus_keyword optionalstring
data optionalobject
Request
{
  ...
}

Response · 200

Response
{
  "seo": {
    "article_id": "...",
    "score": 0,
    "max_score": 0,
    "grade": "B",
    "focus_keyword": "...",
    "data": {},
    "updated_at": "2026-01-01T00:00:00Z"
  }
}

Responses

StatusMeaning
200The stored SEO score.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
article_id string (uuid)
score integer
max_score integer
grade stringExample: B.
focus_keyword string
data objectRaw analyser output.
updated_at string (date-time)
curl
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.

GET/v1/api/videosList videos
Secret key · videos:readlistVideos

Includes drafts. Scope: videos:read.

Query parameters

FieldTypeNotes
status optionalstringOne of: draft, published, archived.
limit optionalintegerPage size. Out-of-range values fall back to the endpoint default. Defaults to 50. Max 100.
offset optionalintegerNumber of records to skip. Defaults to 0.

Response · 200

Response
{
  "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

StatusMeaning
200A page of videos.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
title string
slug string
description string
poster_url stringThumbnail image URL.
bunny_video_id string | nullGUID from Bunny Stream.
bunny_library_id string | null
bunny_status string | nullOne of: pending, uploading, processing, ready, failed.
source_url string | nullExternal URL when not on Bunny.
duration_seconds number (double) | null
status stringOne of: draft, published, archived.
locale string
published_at string (date-time) | null
created_at string (date-time)
updated_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/api/videos" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
POST/v1/api/videosCreate a video record
Secret key · videos:writecreateVideo

Attaches an uploaded Bunny video or an external URL. Scope: videos:write.

Request · CreateVideoInput

FieldTypeNotes
title requiredstring
11 optional fields
FieldTypeNotes
slug optionalstringMatches ^[a-z0-9]+(-[a-z0-9]+)*$.
description optionalstring
poster_url optionalstring
bunny_video_id optionalstring | null
bunny_library_id optionalstring | null
bunny_status optionalstring | nullOne of: pending, uploading, processing, ready, failed.
source_url optionalstring | null
duration_seconds optionalnumber (double)
status optionalstringOne of: draft, published, archived. Defaults to draft.
locale optionalstringDefaults to en.
published_at optionalstring (date-time)
Request
{
  "title": "...",
  ...
}

Response · 201

Response
{
  "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

StatusMeaning
201The created video.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
title string
slug string
description string
poster_url stringThumbnail image URL.
bunny_video_id string | nullGUID from Bunny Stream.
bunny_library_id string | null
bunny_status string | nullOne of: pending, uploading, processing, ready, failed.
source_url string | nullExternal URL when not on Bunny.
duration_seconds number (double) | null
status stringOne of: draft, published, archived.
locale string
published_at string (date-time) | null
created_at string (date-time)
updated_at string (date-time)
curl
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/api/videos" \
  -H "Authorization: Bearer $ANDISHI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"..."}'
GET/v1/api/videos/{videoId}Fetch one video
Secret key · videos:readgetVideo

Scope: videos:read.

Path parameters

FieldTypeNotes
videoId requiredstring (uuid)

Response · 200

Response
{
  "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

StatusMeaning
200The video.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
title string
slug string
description string
poster_url stringThumbnail image URL.
bunny_video_id string | nullGUID from Bunny Stream.
bunny_library_id string | null
bunny_status string | nullOne of: pending, uploading, processing, ready, failed.
source_url string | nullExternal URL when not on Bunny.
duration_seconds number (double) | null
status stringOne of: draft, published, archived.
locale string
published_at string (date-time) | null
created_at string (date-time)
updated_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/api/videos/YOUR_VIDEOID" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
PATCH/v1/api/videos/{videoId}Update a video
Secret key · videos:writeupdateVideo

Editing a published video requires publish rights. Scope: videos:write.

Path parameters

FieldTypeNotes
videoId requiredstring (uuid)

Request · UpdateVideoInput

13 optional fields
FieldTypeNotes
title optionalstring
slug optionalstring
description optionalstring
poster_url optionalstring
bunny_video_id optionalstring | null
clear_bunny optionalbooleanUnsets every Bunny field (wins over the individual ones).
bunny_library_id optionalstring | null
bunny_status optionalstring | nullOne of: pending, uploading, processing, ready, failed.
source_url optionalstring | null
duration_seconds optionalnumber (double)
status optionalstringOne of: draft, published, archived.
locale optionalstring
published_at optionalstring (date-time)
Request
{
  ...
}

Response · 200

Response
{
  "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

StatusMeaning
200The updated video.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
title string
slug string
description string
poster_url stringThumbnail image URL.
bunny_video_id string | nullGUID from Bunny Stream.
bunny_library_id string | null
bunny_status string | nullOne of: pending, uploading, processing, ready, failed.
source_url string | nullExternal URL when not on Bunny.
duration_seconds number (double) | null
status stringOne of: draft, published, archived.
locale string
published_at string (date-time) | null
created_at string (date-time)
updated_at string (date-time)
curl
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 '{}'
DELETE/v1/api/videos/{videoId}Delete a video
Secret key · videos:writedeleteVideo

Scope: videos:write.

Path parameters

FieldTypeNotes
videoId requiredstring (uuid)

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/api/videos/YOUR_VIDEOID" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
GET/v1/workspaces/{id}/videosList videos (studio)
SessionlistWorkspaceVideos

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Query parameters

FieldTypeNotes
status stringOne of: draft, published, archived.
limit optionalintegerPage size. Out-of-range values fall back to the endpoint default. Defaults to 50. Max 100.
offset optionalintegerNumber of records to skip. Defaults to 0.

Response · 200

Response
{
  "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

StatusMeaning
200A page of videos.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
title string
slug string
description string
poster_url stringThumbnail image URL.
bunny_video_id string | nullGUID from Bunny Stream.
bunny_library_id string | null
bunny_status string | nullOne of: pending, uploading, processing, ready, failed.
source_url string | nullExternal URL when not on Bunny.
duration_seconds number (double) | null
status stringOne of: draft, published, archived.
locale string
published_at string (date-time) | null
created_at string (date-time)
updated_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/videos" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/workspaces/{id}/videosCreate a video (studio)
SessioncreateWorkspaceVideo

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Request · CreateVideoInput

FieldTypeNotes
title requiredstring
11 optional fields
FieldTypeNotes
slug optionalstringMatches ^[a-z0-9]+(-[a-z0-9]+)*$.
description optionalstring
poster_url optionalstring
bunny_video_id optionalstring | null
bunny_library_id optionalstring | null
bunny_status optionalstring | nullOne of: pending, uploading, processing, ready, failed.
source_url optionalstring | null
duration_seconds optionalnumber (double)
status optionalstringOne of: draft, published, archived. Defaults to draft.
locale optionalstringDefaults to en.
published_at optionalstring (date-time)
Request
{
  "title": "...",
  ...
}

Response · 201

Response
{
  "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

StatusMeaning
201The created video.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
title string
slug string
description string
poster_url stringThumbnail image URL.
bunny_video_id string | nullGUID from Bunny Stream.
bunny_library_id string | null
bunny_status string | nullOne of: pending, uploading, processing, ready, failed.
source_url string | nullExternal URL when not on Bunny.
duration_seconds number (double) | null
status stringOne of: draft, published, archived.
locale string
published_at string (date-time) | null
created_at string (date-time)
updated_at string (date-time)
curl
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":"..."}'
POST/v1/workspaces/{id}/videos/upload-credentialsMint direct-upload credentials for Bunny Stream
SessioncreateVideoUploadCredentials

Returns 501 not_configured when no video provider is wired.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Request

1 optional field
FieldTypeNotes
title optionalstring
Request
{
  ...
}

Response · 200 · BunnyUploadCredentials

Response
{}

Responses

StatusMeaning
200Provider upload credentials. Returns BunnyUploadCredentials.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
501No video provider configured. Returns ErrorResponse.
curl
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 '{}'
GET/v1/workspaces/{id}/videos/bunny/{bunnyId}/statusPoll a Bunny upload's transcode status
SessiongetBunnyVideoStatus

Best-effort persists the result onto any matching video row.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
bunnyId requiredstring

Response · 200 · BunnyStatus

Response
{
  "status": "pending",
  "duration_seconds": 0
}

Responses

StatusMeaning
200The provider's status. Returns BunnyStatus.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
501No video provider configured. Returns ErrorResponse.

200 payload · status

curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/videos/bunny/YOUR_BUNNYID/status" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
GET/v1/workspaces/{id}/videos/{videoId}Fetch a video (studio)
SessiongetWorkspaceVideo

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
videoId requiredstring (uuid)

Response · 200

Response
{
  "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

StatusMeaning
200The video.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
title string
slug string
description string
poster_url stringThumbnail image URL.
bunny_video_id string | nullGUID from Bunny Stream.
bunny_library_id string | null
bunny_status string | nullOne of: pending, uploading, processing, ready, failed.
source_url string | nullExternal URL when not on Bunny.
duration_seconds number (double) | null
status stringOne of: draft, published, archived.
locale string
published_at string (date-time) | null
created_at string (date-time)
updated_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/videos/YOUR_VIDEOID" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
PATCH/v1/workspaces/{id}/videos/{videoId}Update a video (studio)
SessionupdateWorkspaceVideo

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
videoId requiredstring (uuid)

Request · UpdateVideoInput

13 optional fields
FieldTypeNotes
title optionalstring
slug optionalstring
description optionalstring
poster_url optionalstring
bunny_video_id optionalstring | null
clear_bunny optionalbooleanUnsets every Bunny field (wins over the individual ones).
bunny_library_id optionalstring | null
bunny_status optionalstring | nullOne of: pending, uploading, processing, ready, failed.
source_url optionalstring | null
duration_seconds optionalnumber (double)
status optionalstringOne of: draft, published, archived.
locale optionalstring
published_at optionalstring (date-time)
Request
{
  ...
}

Response · 200

Response
{
  "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

StatusMeaning
200The updated video.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
title string
slug string
description string
poster_url stringThumbnail image URL.
bunny_video_id string | nullGUID from Bunny Stream.
bunny_library_id string | null
bunny_status string | nullOne of: pending, uploading, processing, ready, failed.
source_url string | nullExternal URL when not on Bunny.
duration_seconds number (double) | null
status stringOne of: draft, published, archived.
locale string
published_at string (date-time) | null
created_at string (date-time)
updated_at string (date-time)
curl
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 '{}'
DELETE/v1/workspaces/{id}/videos/{videoId}Delete a video (studio)
SessiondeleteWorkspaceVideo

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
videoId requiredstring (uuid)

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
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.

GET/v1/api/categoriesList categories
Secret key · categories:readlistCategories

Not paginated. Scope: categories:read.

Response · 200

Response
{
  "categories": [
    {
      "id": "...",
      "name": "Culture",
      "slug": "culture",
      "description": "...",
      "accent_color": "#8b5cf6",
      "image_url": "...",
      "locale": "...",
      "display_order": 0
    }
  ]
}

Responses

StatusMeaning
200Every category.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
name stringExample: Culture.
slug stringExample: culture.
description string
accent_color stringExample: #8b5cf6.
image_url string
locale string
display_order integer
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/api/categories" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
POST/v1/api/categoriesCreate a category
Secret key · categories:writecreateCategory

Scope: categories:write.

Request · CreateCategoryInput

FieldTypeNotes
name requiredstring
6 optional fields
FieldTypeNotes
slug optionalstringDerived from name when omitted. Matches ^[a-z0-9]+(-[a-z0-9]+)*$.
description optionalstring
accent_color optionalstringDefaults to #8b5cf6.
image_url optionalstring
locale optionalstringDefaults to en.
display_order optionalintegerDefaults to 0.
Request
{
  "name": "...",
  ...
}

Response · 201

Response
{
  "category": {
    "id": "...",
    "name": "Culture",
    "slug": "culture",
    "description": "...",
    "accent_color": "#8b5cf6",
    "image_url": "...",
    "locale": "...",
    "display_order": 0
  }
}

Responses

StatusMeaning
201The created category.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
409The 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
FieldTypeNotes
id string (uuid)
name stringExample: Culture.
slug stringExample: culture.
description string
accent_color stringExample: #8b5cf6.
image_url string
locale string
display_order integer
curl
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/api/categories" \
  -H "Authorization: Bearer $ANDISHI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"..."}'
GET/v1/api/categories/{termId}Fetch one category
Secret key · categories:readgetCategory

Scope: categories:read.

Path parameters

FieldTypeNotes
termId requiredstring (uuid)The category's term UUID.

Response · 200

Response
{
  "category": {
    "id": "...",
    "name": "Culture",
    "slug": "culture",
    "description": "...",
    "accent_color": "#8b5cf6",
    "image_url": "...",
    "locale": "...",
    "display_order": 0
  }
}

Responses

StatusMeaning
200The category.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
name stringExample: Culture.
slug stringExample: culture.
description string
accent_color stringExample: #8b5cf6.
image_url string
locale string
display_order integer
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/api/categories/YOUR_TERMID" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
PATCH/v1/api/categories/{termId}Update a category
Secret key · categories:writeupdateCategory

Scope: categories:write.

Path parameters

FieldTypeNotes
termId requiredstring (uuid)The category's term UUID.

Request · UpdateCategoryInput

6 optional fields
FieldTypeNotes
name optionalstring
slug optionalstring
description optionalstring
accent_color optionalstring
image_url optionalstring
display_order optionalinteger
Request
{
  ...
}

Response · 200

Response
{
  "category": {
    "id": "...",
    "name": "Culture",
    "slug": "culture",
    "description": "...",
    "accent_color": "#8b5cf6",
    "image_url": "...",
    "locale": "...",
    "display_order": 0
  }
}

Responses

StatusMeaning
200The updated category.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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.
409The 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
FieldTypeNotes
id string (uuid)
name stringExample: Culture.
slug stringExample: culture.
description string
accent_color stringExample: #8b5cf6.
image_url string
locale string
display_order integer
curl
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 '{}'
DELETE/v1/api/categories/{termId}Delete a category
Secret key · categories:writedeleteCategory

Scope: categories:write.

Path parameters

FieldTypeNotes
termId requiredstring (uuid)The category's term UUID.

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/api/categories/YOUR_TERMID" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
GET/v1/workspaces/{id}/categoriesList categories (studio)
SessionlistWorkspaceCategories

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Response · 200

Response
{
  "categories": [
    {
      "id": "...",
      "name": "Culture",
      "slug": "culture",
      "description": "...",
      "accent_color": "#8b5cf6",
      "image_url": "...",
      "locale": "...",
      "display_order": 0
    }
  ]
}

Responses

StatusMeaning
200The categories.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
name stringExample: Culture.
slug stringExample: culture.
description string
accent_color stringExample: #8b5cf6.
image_url string
locale string
display_order integer
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/categories" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/workspaces/{id}/categoriesCreate a category (studio)
SessioncreateWorkspaceCategory

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Request · CreateCategoryInput

FieldTypeNotes
name requiredstring
6 optional fields
FieldTypeNotes
slug optionalstringDerived from name when omitted. Matches ^[a-z0-9]+(-[a-z0-9]+)*$.
description optionalstring
accent_color optionalstringDefaults to #8b5cf6.
image_url optionalstring
locale optionalstringDefaults to en.
display_order optionalintegerDefaults to 0.
Request
{
  "name": "...",
  ...
}

Response · 201

Response
{
  "category": {
    "id": "...",
    "name": "Culture",
    "slug": "culture",
    "description": "...",
    "accent_color": "#8b5cf6",
    "image_url": "...",
    "locale": "...",
    "display_order": 0
  }
}

Responses

StatusMeaning
201The created category.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
409The 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
FieldTypeNotes
id string (uuid)
name stringExample: Culture.
slug stringExample: culture.
description string
accent_color stringExample: #8b5cf6.
image_url string
locale string
display_order integer
curl
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":"..."}'
GET/v1/workspaces/{id}/categories/{termId}Fetch a category (studio)
SessiongetWorkspaceCategory

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
termId requiredstring (uuid)

Response · 200

Response
{
  "category": {
    "id": "...",
    "name": "Culture",
    "slug": "culture",
    "description": "...",
    "accent_color": "#8b5cf6",
    "image_url": "...",
    "locale": "...",
    "display_order": 0
  }
}

Responses

StatusMeaning
200The category.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
name stringExample: Culture.
slug stringExample: culture.
description string
accent_color stringExample: #8b5cf6.
image_url string
locale string
display_order integer
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/categories/YOUR_TERMID" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
PATCH/v1/workspaces/{id}/categories/{termId}Update a category (studio)
SessionupdateWorkspaceCategory

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
termId requiredstring (uuid)

Request · UpdateCategoryInput

6 optional fields
FieldTypeNotes
name optionalstring
slug optionalstring
description optionalstring
accent_color optionalstring
image_url optionalstring
display_order optionalinteger
Request
{
  ...
}

Response · 200

Response
{
  "category": {
    "id": "...",
    "name": "Culture",
    "slug": "culture",
    "description": "...",
    "accent_color": "#8b5cf6",
    "image_url": "...",
    "locale": "...",
    "display_order": 0
  }
}

Responses

StatusMeaning
200The updated category.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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.
409The 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
FieldTypeNotes
id string (uuid)
name stringExample: Culture.
slug stringExample: culture.
description string
accent_color stringExample: #8b5cf6.
image_url string
locale string
display_order integer
curl
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 '{}'
DELETE/v1/workspaces/{id}/categories/{termId}Delete a category (studio)
SessiondeleteWorkspaceCategory

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
termId requiredstring (uuid)

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
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.

GET/v1/api/mediaList media library items
Secret key · media:readlistMedia

Scope: media:read.

Query parameters

FieldTypeNotes
type optionalstringOne of: image, video, audio, file.
limit optionalintegerPage size. Out-of-range values fall back to the endpoint default. Defaults to 50. Max 100.
offset optionalintegerNumber of records to skip. Defaults to 0.

Response · 200

Response
{
  "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

StatusMeaning
200A page of media items.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
type stringOne of: image, video, audio, file.
title string
url string (uri)
thumbnail_url string
mime_type stringExample: image/png.
size_bytes integer (int64)
width integer | null
height integer | null
folder string
status stringpending until an upload finalizes. One of: pending, ready.
created_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/api/media" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
POST/v1/api/mediaRegister an already-hosted asset
Secret key · media:writecreateMediaAsset

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
FieldTypeNotes
type optionalstringOne of: image, video, audio, file. Defaults to image.
title optionalstring
url optionalstring (uri)
thumbnail_url optionalstring
mime_type optionalstringRejected when script-executable (text/html, application/javascript, …) or when it does not match type.
size_bytes optionalinteger (int64)
folder optionalstring
width optionalinteger
height optionalinteger
Request
{
  ...
}

Response · 201

Response
{
  "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

StatusMeaning
201The registered asset.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
type stringOne of: image, video, audio, file.
title string
url string (uri)
thumbnail_url string
mime_type stringExample: image/png.
size_bytes integer (int64)
width integer | null
height integer | null
folder string
status stringpending until an upload finalizes. One of: pending, ready.
created_at string (date-time)
curl
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/api/media" \
  -H "Authorization: Bearer $ANDISHI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
GET/v1/api/media/{assetId}Fetch one media item
Secret key · media:readgetMediaAsset

Scope: media:read.

Path parameters

FieldTypeNotes
assetId requiredstring (uuid)

Response · 200

Response
{
  "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

StatusMeaning
200The media item.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
type stringOne of: image, video, audio, file.
title string
url string (uri)
thumbnail_url string
mime_type stringExample: image/png.
size_bytes integer (int64)
width integer | null
height integer | null
folder string
status stringpending until an upload finalizes. One of: pending, ready.
created_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/api/media/YOUR_ASSETID" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
GET/v1/files/{key}Stream a stored media object
No authserveStoredObject

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

FieldTypeNotes
key requiredstringThe object's storage key. May contain slashes.

Responses

StatusMeaning
200The object's bytes.
404No 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
curl "https://api.andishi.nexuslabsstudio.com/v1/files/YOUR_KEY"
GET/v1/workspaces/{id}/mediaList media (studio)
SessionlistWorkspaceMedia

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Query parameters

FieldTypeNotes
type stringOne of: image, video, audio, file.
limit optionalintegerPage size. Out-of-range values fall back to the endpoint default. Defaults to 50. Max 100.
offset optionalintegerNumber of records to skip. Defaults to 0.

Response · 200

Response
{
  "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

StatusMeaning
200A page of media.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
type stringOne of: image, video, audio, file.
title string
url string (uri)
thumbnail_url string
mime_type stringExample: image/png.
size_bytes integer (int64)
width integer | null
height integer | null
folder string
status stringpending until an upload finalizes. One of: pending, ready.
created_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/media" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/workspaces/{id}/mediaRegister an asset (studio)
SessioncreateWorkspaceMedia

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Request · CreateAssetInput

9 optional fields
FieldTypeNotes
type optionalstringOne of: image, video, audio, file. Defaults to image.
title optionalstring
url optionalstring (uri)
thumbnail_url optionalstring
mime_type optionalstringRejected when script-executable (text/html, application/javascript, …) or when it does not match type.
size_bytes optionalinteger (int64)
folder optionalstring
width optionalinteger
height optionalinteger
Request
{
  ...
}

Response · 201

Response
{
  "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

StatusMeaning
201The registered asset.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
type stringOne of: image, video, audio, file.
title string
url string (uri)
thumbnail_url string
mime_type stringExample: image/png.
size_bytes integer (int64)
width integer | null
height integer | null
folder string
status stringpending until an upload finalizes. One of: pending, ready.
created_at string (date-time)
curl
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 '{}'
POST/v1/workspaces/{id}/media/uploadsReserve a pending asset and storage key
SessioncreateMediaUploadSession

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

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Request · UploadSessionInput

6 optional fields
FieldTypeNotes
filename optionalstring
content_type optionalstring
type optionalstringOne of: image, video, audio, file. Defaults to image.
title optionalstring
size_bytes optionalinteger (int64)Checked against the workspace upload cap and plan storage quota.
folder optionalstring
Request
{
  ...
}

Response · 201

Response
{
  "upload": {
    "asset_id": "...",
    "storage_key": "...",
    "content_type": "..."
  }
}

Responses

StatusMeaning
201The reserved upload.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
413The upload exceeds the workspace's limit. Codes: upload_too_large, avatar_too_large, storage_limit_exceeded. Returns ErrorResponse.

201 payload · upload · UploadSession

Response fields
FieldTypeNotes
asset_id string (uuid)
storage_key string
content_type string
curl
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 '{}'
GET/v1/workspaces/{id}/media/{assetId}Fetch a media item (studio)
SessiongetWorkspaceMedia

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
assetId requiredstring (uuid)

Response · 200

Response
{
  "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

StatusMeaning
200The media item.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
type stringOne of: image, video, audio, file.
title string
url string (uri)
thumbnail_url string
mime_type stringExample: image/png.
size_bytes integer (int64)
width integer | null
height integer | null
folder string
status stringpending until an upload finalizes. One of: pending, ready.
created_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/media/YOUR_ASSETID" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
DELETE/v1/workspaces/{id}/media/{assetId}Delete a media item
SessiondeleteWorkspaceMedia

Also removes the stored object when one exists.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
assetId requiredstring (uuid)

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/media/YOUR_ASSETID" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/workspaces/{id}/media/{assetId}/finalizeFinalize a reserved upload
SessionfinalizeMediaUpload

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

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
assetId requiredstring (uuid)

Request · FinalizeUploadInput

5 optional fields
FieldTypeNotes
title optionalstring
thumbnail_url optionalstring
width optionalinteger
height optionalinteger
data_base64 optionalstringInline upload body. When present the bytes are stored and the asset becomes ready; otherwise the asset is finalized as an external URL.
Request
{
  ...
}

Response · 200

Response
{
  "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

StatusMeaning
200The finalized asset.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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.
413The upload exceeds the workspace's limit. Codes: upload_too_large, avatar_too_large, storage_limit_exceeded. Returns ErrorResponse.

200 payload · media · MediaAsset

Response fields
FieldTypeNotes
id string (uuid)
type stringOne of: image, video, audio, file.
title string
url string (uri)
thumbnail_url string
mime_type stringExample: image/png.
size_bytes integer (int64)
width integer | null
height integer | null
folder string
status stringpending until an upload finalizes. One of: pending, ready.
created_at string (date-time)
curl
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.

GET/v1/api/commentsList comments
Secret key · comments:readlistComments

Includes pending and spam. Scope: comments:read.

Query parameters

FieldTypeNotes
article_id optionalstring (uuid)
status optionalstringOne of: pending, approved, spam.
limit optionalintegerPage size. Out-of-range values fall back to the endpoint default. Defaults to 50. Max 100.
offset optionalintegerNumber of records to skip. Defaults to 0.

Response · 200

Response
{
  "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

StatusMeaning
200A page of comments.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
article_id string (uuid)
parent_id string (uuid) | nullSet when this is a reply.
author_name string
author_email string
content string
status stringOne of: pending, approved, spam.
created_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/api/comments" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
POST/v1/api/commentsCreate a comment
Secret key · comments:writecreateComment

Always stored as pending. Scope: comments:write.

Request · CreateCommentInput

FieldTypeNotes
article_id requiredstring (uuid)
author_name requiredstring
content requiredstring
2 optional fields
FieldTypeNotes
parent_id optionalstring (uuid) | null
author_email optionalstring (email)
Request
{
  "article_id": "...",
  "author_name": "...",
  "content": "...",
  ...
}

Response · 201

Response
{
  "comment": {
    "id": "...",
    "article_id": "...",
    "parent_id": "...",
    "author_name": "...",
    "author_email": "...",
    "content": "...",
    "status": "pending",
    "created_at": "2026-01-01T00:00:00Z"
  }
}

Responses

StatusMeaning
201The created comment.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No such article in this workspace. Returns ErrorResponse.

201 payload · comment · Comment

Response fields
FieldTypeNotes
id string (uuid)
article_id string (uuid)
parent_id string (uuid) | nullSet when this is a reply.
author_name string
author_email string
content string
status stringOne of: pending, approved, spam.
created_at string (date-time)
curl
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":"..."}'
PATCH/v1/api/comments/{commentId}Moderate or edit a comment
Secret key · comments:writeupdateComment

Approve, mark spam, or edit the body. Scope: comments:write.

Path parameters

FieldTypeNotes
commentId requiredstring (uuid)

Request · UpdateCommentInput

2 optional fields
FieldTypeNotes
status optionalstringOne of: pending, approved, spam.
content optionalstring
Request
{
  ...
}

Response · 200

Response
{
  "comment": {
    "id": "...",
    "article_id": "...",
    "parent_id": "...",
    "author_name": "...",
    "author_email": "...",
    "content": "...",
    "status": "pending",
    "created_at": "2026-01-01T00:00:00Z"
  }
}

Responses

StatusMeaning
200The updated comment.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
article_id string (uuid)
parent_id string (uuid) | nullSet when this is a reply.
author_name string
author_email string
content string
status stringOne of: pending, approved, spam.
created_at string (date-time)
curl
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 '{}'
DELETE/v1/api/comments/{commentId}Delete a comment
Secret key · comments:writedeleteComment

Scope: comments:write.

Path parameters

FieldTypeNotes
commentId requiredstring (uuid)

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/api/comments/YOUR_COMMENTID" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
GET/v1/workspaces/{id}/commentsList comments (studio)
SessionlistWorkspaceComments

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Query parameters

FieldTypeNotes
article_id string (uuid)
status stringOne of: pending, approved, spam.
limit optionalintegerPage size. Out-of-range values fall back to the endpoint default. Defaults to 50. Max 100.
offset optionalintegerNumber of records to skip. Defaults to 0.

Response · 200

Response
{
  "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

StatusMeaning
200A page of comments.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
article_id string (uuid)
parent_id string (uuid) | nullSet when this is a reply.
author_name string
author_email string
content string
status stringOne of: pending, approved, spam.
created_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/comments" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/workspaces/{id}/commentsPost a comment from the studio
SessioncreateWorkspaceComment

Every role may comment, viewers included.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Request · CreateCommentInput

FieldTypeNotes
article_id requiredstring (uuid)
author_name requiredstring
content requiredstring
2 optional fields
FieldTypeNotes
parent_id optionalstring (uuid) | null
author_email optionalstring (email)
Request
{
  "article_id": "...",
  "author_name": "...",
  "content": "...",
  ...
}

Response · 201

Response
{
  "comment": {
    "id": "...",
    "article_id": "...",
    "parent_id": "...",
    "author_name": "...",
    "author_email": "...",
    "content": "...",
    "status": "pending",
    "created_at": "2026-01-01T00:00:00Z"
  }
}

Responses

StatusMeaning
201The created comment.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
article_id string (uuid)
parent_id string (uuid) | nullSet when this is a reply.
author_name string
author_email string
content string
status stringOne of: pending, approved, spam.
created_at string (date-time)
curl
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":"..."}'
PATCH/v1/workspaces/{id}/comments/{commentId}Moderate or edit a comment (studio)
SessionmoderateWorkspaceComment

Owner, admin or editor only.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
commentId requiredstring (uuid)

Request · UpdateCommentInput

2 optional fields
FieldTypeNotes
status optionalstringOne of: pending, approved, spam.
content optionalstring
Request
{
  ...
}

Response · 200

Response
{
  "comment": {
    "id": "...",
    "article_id": "...",
    "parent_id": "...",
    "author_name": "...",
    "author_email": "...",
    "content": "...",
    "status": "pending",
    "created_at": "2026-01-01T00:00:00Z"
  }
}

Responses

StatusMeaning
200The updated comment.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
article_id string (uuid)
parent_id string (uuid) | nullSet when this is a reply.
author_name string
author_email string
content string
status stringOne of: pending, approved, spam.
created_at string (date-time)
curl
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 '{}'
DELETE/v1/workspaces/{id}/comments/{commentId}Delete a comment (studio)
SessiondeleteWorkspaceComment

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
commentId requiredstring (uuid)

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
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.

GET/v1/api/custom/{slug}/entriesList Custom Module entries
Secret key · custom:readlistCustomEntries

Includes drafts. Scope: custom:read.

Path parameters

FieldTypeNotes
slug requiredstringThe Custom Module's slug, e.g. events.

Query parameters

FieldTypeNotes
limit optionalintegerPage size. Out-of-range values fall back to the endpoint default. Defaults to 50. Max 100.
offset optionalintegerNumber of records to skip. Defaults to 0.

Response · 200

Response
{
  "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

StatusMeaning
200A page of entries.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
workspace_id string (uuid)
module_id string (uuid)
data objectValues keyed by each field's key in the parent module's schema. Keys the schema does not define are stripped on write.
status stringOne of: draft, published. Defaults to published.
created_at string (date-time)
updated_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/api/custom/YOUR_SLUG/entries" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
POST/v1/api/custom/{slug}/entriesCreate a Custom Module entry
Secret key · custom:writecreateCustomEntry

data is validated against the module's field schema; unknown keys are stripped. Scope: custom:write.

Path parameters

FieldTypeNotes
slug requiredstringThe Custom Module's slug, e.g. events.

Request · CreateCustomEntryInput

2 optional fields
FieldTypeNotes
data optionalobjectValidated against the module schema: required fields must be present, and number, boolean, url, date, datetime, email, select and media fields must parse.
status optionalstringOne of: draft, published. Defaults to published.
Request
{
  ...
}

Response · 201

Response
{
  "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

StatusMeaning
201The created entry.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
workspace_id string (uuid)
module_id string (uuid)
data objectValues keyed by each field's key in the parent module's schema. Keys the schema does not define are stripped on write.
status stringOne of: draft, published. Defaults to published.
created_at string (date-time)
updated_at string (date-time)
curl
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 '{}'
GET/v1/api/custom/{slug}/entries/{entryId}Fetch one Custom Module entry
Secret key · custom:readgetCustomEntry

Returns drafts too. Scope: custom:read.

Path parameters

FieldTypeNotes
slug requiredstring
entryId requiredstring (uuid)

Response · 200

Response
{
  "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

StatusMeaning
200The entry.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
workspace_id string (uuid)
module_id string (uuid)
data objectValues keyed by each field's key in the parent module's schema. Keys the schema does not define are stripped on write.
status stringOne of: draft, published. Defaults to published.
created_at string (date-time)
updated_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/api/custom/YOUR_SLUG/entries/YOUR_ENTRYID" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
PATCH/v1/api/custom/{slug}/entries/{entryId}Update a Custom Module entry
Secret key · custom:writeupdateCustomEntry

Scope: custom:write.

Path parameters

FieldTypeNotes
slug requiredstring
entryId requiredstring (uuid)

Request · UpdateCustomEntryInput

2 optional fields
FieldTypeNotes
data optionalobject
status optionalstringOne of: draft, published.
Request
{
  ...
}

Response · 200

Response
{
  "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

StatusMeaning
200The updated entry.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
workspace_id string (uuid)
module_id string (uuid)
data objectValues keyed by each field's key in the parent module's schema. Keys the schema does not define are stripped on write.
status stringOne of: draft, published. Defaults to published.
created_at string (date-time)
updated_at string (date-time)
curl
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 '{}'
DELETE/v1/api/custom/{slug}/entries/{entryId}Delete a Custom Module entry
Secret key · custom:writedeleteCustomEntry

Scope: custom:write.

Path parameters

FieldTypeNotes
slug requiredstring
entryId requiredstring (uuid)

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/api/custom/YOUR_SLUG/entries/YOUR_ENTRYID" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
GET/v1/workspaces/{id}/custom-modulesList Custom Modules
SessionlistCustomModules

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Response · 200

Response
{
  "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

StatusMeaning
200The workspace's Custom Modules.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
workspace_id string (uuid)
slug stringExample: events.
name stringExample: Events.
tagline string
schema object{ "fields": [{ key, label, type, required, options }] }.
enabled boolean
created_at string (date-time)
updated_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/custom-modules" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/workspaces/{id}/custom-modulesDefine a Custom Module
SessioncreateCustomModule

Owner or admin only.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Request · CreateCustomModuleInput

FieldTypeNotes
slug requiredstringMatches ^[a-z0-9]+(-[a-z0-9]+)*$.
name requiredstring
3 optional fields
FieldTypeNotes
tagline optionalstring
schema optionalobject
enabled optionalbooleanDefaults to true.
Request
{
  "name": "...",
  "slug": "...",
  ...
}

Response · 201

Response
{
  "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

StatusMeaning
201The created module.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
409The 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
FieldTypeNotes
id string (uuid)
workspace_id string (uuid)
slug stringExample: events.
name stringExample: Events.
tagline string
schema object{ "fields": [{ key, label, type, required, options }] }.
enabled boolean
created_at string (date-time)
updated_at string (date-time)
curl
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":"..."}'
PATCH/v1/workspaces/{id}/custom-modules/{moduleId}Update a Custom Module's schema or state
SessionupdateCustomModule

Owner or admin only.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
moduleId requiredstring (uuid)

Request · UpdateCustomModuleInput

4 optional fields
FieldTypeNotes
name optionalstring
tagline optionalstring
schema optionalobject
enabled optionalboolean
Request
{
  ...
}

Response · 200

Response
{
  "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

StatusMeaning
200The updated module.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
workspace_id string (uuid)
slug stringExample: events.
name stringExample: Events.
tagline string
schema object{ "fields": [{ key, label, type, required, options }] }.
enabled boolean
created_at string (date-time)
updated_at string (date-time)
curl
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 '{}'
DELETE/v1/workspaces/{id}/custom-modules/{moduleId}Delete a Custom Module
SessiondeleteCustomModule

Owner or admin only.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
moduleId requiredstring (uuid)

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/custom-modules/YOUR_MODULEID" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
GET/v1/workspaces/{id}/custom/{slug}/entriesList Custom Module entries (studio)
SessionlistWorkspaceCustomEntries

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
slug requiredstring

Query parameters

FieldTypeNotes
limit optionalintegerPage size. Out-of-range values fall back to the endpoint default. Defaults to 50. Max 100.
offset optionalintegerNumber of records to skip. Defaults to 0.

Response · 200

Response
{
  "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

StatusMeaning
200A page of entries, drafts included.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
workspace_id string (uuid)
module_id string (uuid)
data objectValues keyed by each field's key in the parent module's schema. Keys the schema does not define are stripped on write.
status stringOne of: draft, published. Defaults to published.
created_at string (date-time)
updated_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/custom/YOUR_SLUG/entries" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/workspaces/{id}/custom/{slug}/entriesCreate a Custom Module entry (studio)
SessioncreateWorkspaceCustomEntry

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
slug requiredstring

Request · CreateCustomEntryInput

2 optional fields
FieldTypeNotes
data optionalobjectValidated against the module schema: required fields must be present, and number, boolean, url, date, datetime, email, select and media fields must parse.
status optionalstringOne of: draft, published. Defaults to published.
Request
{
  ...
}

Response · 201

Response
{
  "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

StatusMeaning
201The created entry.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
workspace_id string (uuid)
module_id string (uuid)
data objectValues keyed by each field's key in the parent module's schema. Keys the schema does not define are stripped on write.
status stringOne of: draft, published. Defaults to published.
created_at string (date-time)
updated_at string (date-time)
curl
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 '{}'
GET/v1/workspaces/{id}/custom/{slug}/entries/{entryId}Fetch a Custom Module entry (studio)
SessiongetWorkspaceCustomEntry

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
slug requiredstring
entryId requiredstring (uuid)

Response · 200

Response
{
  "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

StatusMeaning
200The entry.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
workspace_id string (uuid)
module_id string (uuid)
data objectValues keyed by each field's key in the parent module's schema. Keys the schema does not define are stripped on write.
status stringOne of: draft, published. Defaults to published.
created_at string (date-time)
updated_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/custom/YOUR_SLUG/entries/YOUR_ENTRYID" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
PATCH/v1/workspaces/{id}/custom/{slug}/entries/{entryId}Update a Custom Module entry (studio)
SessionupdateWorkspaceCustomEntry

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
slug requiredstring
entryId requiredstring (uuid)

Request · UpdateCustomEntryInput

2 optional fields
FieldTypeNotes
data optionalobject
status optionalstringOne of: draft, published.
Request
{
  ...
}

Response · 200

Response
{
  "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

StatusMeaning
200The updated entry.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
workspace_id string (uuid)
module_id string (uuid)
data objectValues keyed by each field's key in the parent module's schema. Keys the schema does not define are stripped on write.
status stringOne of: draft, published. Defaults to published.
created_at string (date-time)
updated_at string (date-time)
curl
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 '{}'
DELETE/v1/workspaces/{id}/custom/{slug}/entries/{entryId}Delete a Custom Module entry (studio)
SessiondeleteWorkspaceCustomEntry

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
slug requiredstring
entryId requiredstring (uuid)

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
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.

GET/v1/api/pages/{pageId}/sectionsRead a page's published sections
Secret key · pages:readlistPublishedSections

Published snapshots only, never drafts. Scope: pages:read.

Path parameters

FieldTypeNotes
pageId requiredstring (uuid)

Query parameters

FieldTypeNotes
locale optionalstringFilter by language code. Defaults to the workspace's default locale. Example: en.

Response · 200

Response
{
  "sections": [
    {
      "id": "...",
      "page_id": "...",
      "section_key": "...",
      "locale": "...",
      "data": {},
      "version": 0,
      "published_at": "2026-01-01T00:00:00Z"
    }
  ]
}

Responses

StatusMeaning
200The page's published sections.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
page_id string (uuid)
section_key string
locale string
data object
version integer | null
published_at string (date-time) | null
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/api/pages/YOUR_PAGEID/sections" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
PUT/v1/api/pages/{pageId}/sectionsReplace a whole section's draft data
Secret key · pages:writeputSection

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

FieldTypeNotes
pageId requiredstring (uuid)

Request · PutSectionInput

FieldTypeNotes
section_key requiredstring
3 optional fields
FieldTypeNotes
data optionalobjectEvery value goes through the same validation as UpsertFieldInput.value.
locale optionalstring
base_version optionalinteger | nullOptimistic concurrency. A stale value returns 409 conflict.
Request
{
  "section_key": "...",
  ...
}

Response · 200

Response
{
  "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

StatusMeaning
200The updated section.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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.
409The 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
FieldTypeNotes
id string (uuid)
page_id string (uuid)
section_key stringExample: hero.
locale string
data objectThe draft (working) copy.
version integer
published_version integer | nullNull until first publish.
published_at string (date-time) | null
has_unpublished_changes boolean
updated_at string (date-time)
curl
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":"..."}'
POST/v1/api/pages/{pageId}/sections/fieldsWrite one field of one section
Secret key · pages:writeupsertSectionField

The granular write the visual editor uses. Rich-text HTML is sanitized before it can reach a live page. Scope: pages:write.

Path parameters

FieldTypeNotes
pageId requiredstring (uuid)

Request · UpsertFieldInput

FieldTypeNotes
section requiredstringExample: hero.
field requiredstringExample: title.
2 optional fields
FieldTypeNotes
value optionalstring | object | objectText, {"html": "…"} for rich text, or {"src": "…", "alt": "…"} for an image. Anything else is refused with invalid_field_value. Rich-text HTML is sanitized.
locale optionalstring
Request
{
  "section": "hero",
  "field": "title",
  ...
}

Response · 200

Response
{
  "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

StatusMeaning
200The updated section.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
page_id string (uuid)
section_key stringExample: hero.
locale string
data objectThe draft (working) copy.
version integer
published_version integer | nullNull until first publish.
published_at string (date-time) | null
has_unpublished_changes boolean
updated_at string (date-time)
curl
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"}'
GET/v1/workspaces/{id}/sitesList sites
SessionlistSites

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Response · 200

Response
{
  "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

StatusMeaning
200The workspace's sites.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
name string
mode stringOne of: instrumented, rendered.
origin string | nullMust be public HTTPS; private and loopback addresses are refused.
status stringOnly 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
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/sites" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/workspaces/{id}/sitesCreate a site
SessioncreateSite

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

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Request · CreateSiteInput

FieldTypeNotes
name requiredstring
2 optional fields
FieldTypeNotes
mode optionalstringOne of: instrumented, rendered. Defaults to instrumented.
origin optionalstring | null
Request
{
  "name": "...",
  ...
}

Response · 201

Response
{
  "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

StatusMeaning
201The created site, including its publishable key.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
409The 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
FieldTypeNotes
id string (uuid)
name string
mode stringOne of: instrumented, rendered.
origin string | nullMust be public HTTPS; private and loopback addresses are refused.
status stringOnly 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
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":"..."}'
GET/v1/workspaces/{id}/sites/{siteId}Fetch a site
SessiongetSite

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
siteId requiredstring (uuid)

Response · 200

Response
{
  "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

StatusMeaning
200The site.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
name string
mode stringOne of: instrumented, rendered.
origin string | nullMust be public HTTPS; private and loopback addresses are refused.
status stringOnly 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
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/sites/YOUR_SITEID" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
PATCH/v1/workspaces/{id}/sites/{siteId}Update a site
SessionupdateSite

Setting status to disabled stops its publishable key resolving.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
siteId requiredstring (uuid)

Request · UpdateSiteInput

3 optional fields
FieldTypeNotes
name optionalstring
origin optionalstring | null
status optionalstringOne of: active, disabled.
Request
{
  ...
}

Response · 200

Response
{
  "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

StatusMeaning
200The updated site.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
name string
mode stringOne of: instrumented, rendered.
origin string | nullMust be public HTTPS; private and loopback addresses are refused.
status stringOnly 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
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 '{}'
DELETE/v1/workspaces/{id}/sites/{siteId}Delete a site
SessiondeleteSite

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
siteId requiredstring (uuid)

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/sites/YOUR_SITEID" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/workspaces/{id}/sites/{siteId}/publishable-key/rotateRotate a site's publishable key
SessionrotatePublishableKey

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

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
siteId requiredstring (uuid)

Response · 200

Response
{
  "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

StatusMeaning
200The site with its new key.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
name string
mode stringOne of: instrumented, rendered.
origin string | nullMust be public HTTPS; private and loopback addresses are refused.
status stringOnly 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
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/sites/YOUR_SITEID/publishable-key/rotate" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
GET/v1/workspaces/{id}/sites/{siteId}/pagesList a site's pages
SessionlistPages

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
siteId requiredstring (uuid)

Response · 200

Response
{
  "pages": [
    {
      "id": "...",
      "site_id": "...",
      "slug": "/about",
      "title": "...",
      "position": 0,
      "created_at": "2026-01-01T00:00:00Z",
      "updated_at": "2026-01-01T00:00:00Z"
    }
  ]
}

Responses

StatusMeaning
200The pages.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
site_id string (uuid)
slug stringURL 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
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/sites/YOUR_SITEID/pages" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/workspaces/{id}/sites/{siteId}/pagesCreate a page
SessioncreatePage

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
siteId requiredstring (uuid)

Request · CreatePageInput

FieldTypeNotes
slug requiredstringExample: /about.
title requiredstring
Request
{
  "slug": "/about",
  "title": "..."
}

Response · 201

Response
{
  "page": {
    "id": "...",
    "site_id": "...",
    "slug": "/about",
    "title": "...",
    "position": 0,
    "created_at": "2026-01-01T00:00:00Z",
    "updated_at": "2026-01-01T00:00:00Z"
  }
}

Responses

StatusMeaning
201The created page.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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.
409The 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
FieldTypeNotes
id string (uuid)
site_id string (uuid)
slug stringURL 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
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":"..."}'
GET/v1/workspaces/{id}/sites/{siteId}/pages/{pageId}Fetch a page
SessiongetPage

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
siteId requiredstring (uuid)
pageId requiredstring (uuid)

Response · 200

Response
{
  "page": {
    "id": "...",
    "site_id": "...",
    "slug": "/about",
    "title": "...",
    "position": 0,
    "created_at": "2026-01-01T00:00:00Z",
    "updated_at": "2026-01-01T00:00:00Z"
  }
}

Responses

StatusMeaning
200The page.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
site_id string (uuid)
slug stringURL 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
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/sites/YOUR_SITEID/pages/YOUR_PAGEID" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
PATCH/v1/workspaces/{id}/sites/{siteId}/pages/{pageId}Update a page
SessionupdatePage

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
siteId requiredstring (uuid)
pageId requiredstring (uuid)

Request · UpdatePageInput

3 optional fields
FieldTypeNotes
slug optionalstring
title optionalstring
position optionalinteger
Request
{
  ...
}

Response · 200

Response
{
  "page": {
    "id": "...",
    "site_id": "...",
    "slug": "/about",
    "title": "...",
    "position": 0,
    "created_at": "2026-01-01T00:00:00Z",
    "updated_at": "2026-01-01T00:00:00Z"
  }
}

Responses

StatusMeaning
200The updated page.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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.
409The 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
FieldTypeNotes
id string (uuid)
site_id string (uuid)
slug stringURL 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
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 '{}'
DELETE/v1/workspaces/{id}/sites/{siteId}/pages/{pageId}Delete a page
SessiondeletePage

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
siteId requiredstring (uuid)
pageId requiredstring (uuid)

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/sites/YOUR_SITEID/pages/YOUR_PAGEID" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
GET/v1/workspaces/{id}/pages/{pageId}/sectionsList a page's sections
SessionlistSections

Returns both draft and published state for each section.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
pageId requiredstring (uuid)

Query parameters

FieldTypeNotes
locale optionalstringFilter by language code. Defaults to the workspace's default locale. Example: en.

Response · 200

Response
{
  "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

StatusMeaning
200The sections.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
page_id string (uuid)
section_key stringExample: hero.
locale string
data objectThe draft (working) copy.
version integer
published_version integer | nullNull until first publish.
published_at string (date-time) | null
has_unpublished_changes boolean
updated_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/pages/YOUR_PAGEID/sections" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
PUT/v1/workspaces/{id}/pages/{pageId}/sectionsReplace a section's draft data (studio)
SessionputWorkspaceSection

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
pageId requiredstring (uuid)

Request · PutSectionInput

FieldTypeNotes
section_key requiredstring
3 optional fields
FieldTypeNotes
data optionalobjectEvery value goes through the same validation as UpsertFieldInput.value.
locale optionalstring
base_version optionalinteger | nullOptimistic concurrency. A stale value returns 409 conflict.
Request
{
  "section_key": "...",
  ...
}

Response · 200

Response
{
  "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

StatusMeaning
200The updated section.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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.
409The 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
FieldTypeNotes
id string (uuid)
page_id string (uuid)
section_key stringExample: hero.
locale string
data objectThe draft (working) copy.
version integer
published_version integer | nullNull until first publish.
published_at string (date-time) | null
has_unpublished_changes boolean
updated_at string (date-time)
curl
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":"..."}'
POST/v1/workspaces/{id}/pages/{pageId}/sections/fieldsWrite one field of one section (studio)
SessionupsertWorkspaceSectionField

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
pageId requiredstring (uuid)

Request · UpsertFieldInput

FieldTypeNotes
section requiredstringExample: hero.
field requiredstringExample: title.
2 optional fields
FieldTypeNotes
value optionalstring | object | objectText, {"html": "…"} for rich text, or {"src": "…", "alt": "…"} for an image. Anything else is refused with invalid_field_value. Rich-text HTML is sanitized.
locale optionalstring
Request
{
  "section": "hero",
  "field": "title",
  ...
}

Response · 200

Response
{
  "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

StatusMeaning
200The updated section.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
page_id string (uuid)
section_key stringExample: hero.
locale string
data objectThe draft (working) copy.
version integer
published_version integer | nullNull until first publish.
published_at string (date-time) | null
has_unpublished_changes boolean
updated_at string (date-time)
curl
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"}'
GET/v1/workspaces/{id}/pages/{pageId}/sections/{sectionKey}Fetch one section
SessiongetSection

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
pageId requiredstring (uuid)
sectionKey requiredstringExample: hero.

Query parameters

FieldTypeNotes
locale optionalstringFilter by language code. Defaults to the workspace's default locale. Example: en.

Response · 200

Response
{
  "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

StatusMeaning
200The section.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
page_id string (uuid)
section_key stringExample: hero.
locale string
data objectThe draft (working) copy.
version integer
published_version integer | nullNull until first publish.
published_at string (date-time) | null
has_unpublished_changes boolean
updated_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/pages/YOUR_PAGEID/sections/YOUR_SECTIONKEY" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
GET/v1/workspaces/{id}/pages/{pageId}/sections/{sectionKey}/revisionsList a section's revisions
SessionlistSectionRevisions

A section keeps its newest 20 snapshots.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
pageId requiredstring (uuid)
sectionKey requiredstring

Query parameters

FieldTypeNotes
locale optionalstringFilter by language code. Defaults to the workspace's default locale. Example: en.

Response · 200

Response
{
  "revisions": [
    {
      "id": "...",
      "section_id": "...",
      "version": 0,
      "data": {},
      "published_by": "...",
      "created_at": "2026-01-01T00:00:00Z"
    }
  ]
}

Responses

StatusMeaning
200The revisions.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
section_id string (uuid)
version integer
data object
published_by string (uuid) | null
created_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/pages/YOUR_PAGEID/sections/YOUR_SECTIONKEY/revisions" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/workspaces/{id}/pages/{pageId}/sections/{sectionKey}/revisions/{version}/restoreRestore a section to an earlier revision
SessionrestoreSectionRevision

Requires publish rights.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
pageId requiredstring (uuid)
sectionKey requiredstring
version requiredinteger

Query parameters

FieldTypeNotes
locale optionalstringFilter by language code. Defaults to the workspace's default locale. Example: en.

Response · 200

Response
{
  "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

StatusMeaning
200The restored section.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
page_id string (uuid)
section_key stringExample: hero.
locale string
data objectThe draft (working) copy.
version integer
published_version integer | nullNull until first publish.
published_at string (date-time) | null
has_unpublished_changes boolean
updated_at string (date-time)
curl
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"
POST/v1/workspaces/{id}/pages/{pageId}/publishPublish every changed section on a page
SessionpublishPage

Requires publish rights. Returns how many sections went live.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
pageId requiredstring (uuid)

Response · 200

Response
{
  "published": 3
}

Responses

StatusMeaning
200The number of sections published.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
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.

GET/v1/api/tasksList planning tasks
Secret key · tasks:readlistTasks

Scope: tasks:read.

Query parameters

FieldTypeNotes
status optionalstringOne of: pending, in_review, completed, archived.
limit optionalintegerDefaults to 100. Max 200.
offset optionalintegerNumber of records to skip. Defaults to 0.

Response · 200

Response
{
  "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

StatusMeaning
200A page of tasks.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
title string
description string
status stringOne of: pending, in_review, completed, archived.
priority stringOne 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 booleanDerived — true when status is completed.
created_at string (date-time)
updated_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/api/tasks" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
POST/v1/api/tasksCreate a task
Secret key · tasks:writecreateTask

Logs a created activity entry. Scope: tasks:write.

Request · CreateTaskInput

FieldTypeNotes
title requiredstring
6 optional fields
FieldTypeNotes
description optionalstring
status optionalstringOne of: pending, in_review, completed, archived. Defaults to pending.
priority optionalstringOne of: low, medium, high, critical. Defaults to medium.
assigned_to optionalstring (uuid)[]
post_id optionalstring (uuid) | null
due_date optionalstringRFC 3339 or YYYY-MM-DDTHH:MM.
Request
{
  "title": "...",
  ...
}

Response · 201

Response
{
  "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

StatusMeaning
201The created task.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
title string
description string
status stringOne of: pending, in_review, completed, archived.
priority stringOne 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 booleanDerived — true when status is completed.
created_at string (date-time)
updated_at string (date-time)
curl
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/api/tasks" \
  -H "Authorization: Bearer $ANDISHI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"..."}'
GET/v1/api/tasks/{taskId}Fetch one task
Secret key · tasks:readgetTask

Scope: tasks:read.

Path parameters

FieldTypeNotes
taskId requiredstring (uuid)

Response · 200

Response
{
  "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

StatusMeaning
200The task.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
title string
description string
status stringOne of: pending, in_review, completed, archived.
priority stringOne 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 booleanDerived — true when status is completed.
created_at string (date-time)
updated_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/api/tasks/YOUR_TASKID" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
PATCH/v1/api/tasks/{taskId}Update a task
Secret key · tasks:writeupdateTask

Status, priority and assignment changes each append an activity entry. Scope: tasks:write.

Path parameters

FieldTypeNotes
taskId requiredstring (uuid)

Request · UpdateTaskInput

9 optional fields
FieldTypeNotes
title optionalstring
description optionalstring
status optionalstringOne of: pending, in_review, completed, archived.
priority optionalstringOne of: low, medium, high, critical.
assigned_to optionalstring (uuid)[]
post_id optionalstring (uuid) | null
clear_post optionalboolean
due_date optionalstring
clear_due optionalboolean
Request
{
  ...
}

Response · 200

Response
{
  "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

StatusMeaning
200The updated task.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
title string
description string
status stringOne of: pending, in_review, completed, archived.
priority stringOne 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 booleanDerived — true when status is completed.
created_at string (date-time)
updated_at string (date-time)
curl
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 '{}'
DELETE/v1/api/tasks/{taskId}Delete a task
Secret key · tasks:writedeleteTask

Scope: tasks:write.

Path parameters

FieldTypeNotes
taskId requiredstring (uuid)

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/api/tasks/YOUR_TASKID" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
GET/v1/api/tasks/{taskId}/commentsList a task's comments
Secret key · tasks:readlistTaskComments

Not paginated. Scope: tasks:read.

Path parameters

FieldTypeNotes
taskId requiredstring (uuid)

Response · 200

Response
{
  "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

StatusMeaning
200The task's comments.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
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 TaskProfileJoined author summary, present only when user_id is set.
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/api/tasks/YOUR_TASKID/comments" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
POST/v1/api/tasks/{taskId}/commentsComment on a task
Secret key · tasks:writecreateTaskComment

Appends a commented activity entry. Scope: tasks:write.

Path parameters

FieldTypeNotes
taskId requiredstring (uuid)

Request · CreateTaskCommentInput

FieldTypeNotes
content requiredstring
Request
{
  "content": "..."
}

Response · 201

Response
{
  "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

StatusMeaning
201The created comment.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
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 TaskProfileJoined author summary, present only when user_id is set.
curl
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":"..."}'
GET/v1/api/tasks/{taskId}/activityList a task's activity log
Secret key · tasks:readlistTaskActivity

Returns the 30 most recent entries. Scope: tasks:read.

Path parameters

FieldTypeNotes
taskId requiredstring (uuid)

Response · 200

Response
{
  "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

StatusMeaning
200The activity log.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
task_id string (uuid)
user_id string (uuid) | null
action stringOne of: created, status_changed, priority_changed, assigned, commented.
old_value string | null
new_value string | null
created_at string (date-time)
profile TaskProfileJoined author summary, present only when user_id is set.
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/api/tasks/YOUR_TASKID/activity" \
  -H "Authorization: Bearer $ANDISHI_API_KEY"
GET/v1/workspaces/{id}/tasksList tasks (studio)
SessionlistWorkspaceTasks

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Query parameters

FieldTypeNotes
status stringOne of: pending, in_review, completed, archived.
limit integerDefaults to 100. Max 200.
offset optionalintegerNumber of records to skip. Defaults to 0.

Response · 200

Response
{
  "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

StatusMeaning
200A page of tasks.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
title string
description string
status stringOne of: pending, in_review, completed, archived.
priority stringOne 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 booleanDerived — true when status is completed.
created_at string (date-time)
updated_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/tasks" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/workspaces/{id}/tasksCreate a task (studio)
SessioncreateWorkspaceTask

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Request · CreateTaskInput

FieldTypeNotes
title requiredstring
6 optional fields
FieldTypeNotes
description optionalstring
status optionalstringOne of: pending, in_review, completed, archived. Defaults to pending.
priority optionalstringOne of: low, medium, high, critical. Defaults to medium.
assigned_to optionalstring (uuid)[]
post_id optionalstring (uuid) | null
due_date optionalstringRFC 3339 or YYYY-MM-DDTHH:MM.
Request
{
  "title": "...",
  ...
}

Response · 201

Response
{
  "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

StatusMeaning
201The created task.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
title string
description string
status stringOne of: pending, in_review, completed, archived.
priority stringOne 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 booleanDerived — true when status is completed.
created_at string (date-time)
updated_at string (date-time)
curl
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":"..."}'
GET/v1/workspaces/{id}/tasks/{taskId}Fetch a task (studio)
SessiongetWorkspaceTask

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
taskId requiredstring (uuid)

Response · 200

Response
{
  "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

StatusMeaning
200The task.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
title string
description string
status stringOne of: pending, in_review, completed, archived.
priority stringOne 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 booleanDerived — true when status is completed.
created_at string (date-time)
updated_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/tasks/YOUR_TASKID" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
PATCH/v1/workspaces/{id}/tasks/{taskId}Update a task (studio)
SessionupdateWorkspaceTask

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
taskId requiredstring (uuid)

Request · UpdateTaskInput

9 optional fields
FieldTypeNotes
title optionalstring
description optionalstring
status optionalstringOne of: pending, in_review, completed, archived.
priority optionalstringOne of: low, medium, high, critical.
assigned_to optionalstring (uuid)[]
post_id optionalstring (uuid) | null
clear_post optionalboolean
due_date optionalstring
clear_due optionalboolean
Request
{
  ...
}

Response · 200

Response
{
  "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

StatusMeaning
200The updated task.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
title string
description string
status stringOne of: pending, in_review, completed, archived.
priority stringOne 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 booleanDerived — true when status is completed.
created_at string (date-time)
updated_at string (date-time)
curl
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 '{}'
DELETE/v1/workspaces/{id}/tasks/{taskId}Delete a task (studio)
SessiondeleteWorkspaceTask

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
taskId requiredstring (uuid)

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/tasks/YOUR_TASKID" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
GET/v1/workspaces/{id}/tasks/{taskId}/commentsList a task's comments (studio)
SessionlistWorkspaceTaskComments

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
taskId requiredstring (uuid)

Response · 200

Response
{
  "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

StatusMeaning
200The comments.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
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 TaskProfileJoined author summary, present only when user_id is set.
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/tasks/YOUR_TASKID/comments" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/workspaces/{id}/tasks/{taskId}/commentsComment on a task (studio)
SessioncreateWorkspaceTaskComment

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
taskId requiredstring (uuid)

Request · CreateTaskCommentInput

FieldTypeNotes
content requiredstring
Request
{
  "content": "..."
}

Response · 201

Response
{
  "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

StatusMeaning
201The created comment.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
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 TaskProfileJoined author summary, present only when user_id is set.
curl
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":"..."}'
GET/v1/workspaces/{id}/tasks/{taskId}/activityList a task's activity log (studio)
SessionlistWorkspaceTaskActivity

The 30 most recent entries.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
taskId requiredstring (uuid)

Response · 200

Response
{
  "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

StatusMeaning
200The activity log.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
task_id string (uuid)
user_id string (uuid) | null
action stringOne of: created, status_changed, priority_changed, assigned, commented.
old_value string | null
new_value string | null
created_at string (date-time)
profile TaskProfileJoined author summary, present only when user_id is set.
curl
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).

GET/v1/me/bootstrapEverything the studio needs on first paint
Sessionbootstrap

Resolves the active workspace from the JWT, or from the X-Andishi-Workspace header when supplied.

Response · 200 · Bootstrap

Response
{
  "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

StatusMeaning
200The bootstrap payload. Returns Bootstrap.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
curl "https://api.andishi.nexuslabsstudio.com/v1/me/bootstrap" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
GET/v1/workspacesWorkspaces the signed-in user belongs to
SessionlistMyWorkspaces

Response · 200

Response
{
  "workspaces": [
    {
      "workspace_id": "...",
      "slug": "...",
      "name": "...",
      "role": "owner",
      "plan_id": "..."
    }
  ]
}

Responses

StatusMeaning
200The user's memberships.
401No 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
FieldTypeNotes
workspace_id string (uuid)
slug string
name string
role WorkspaceRoleOrdered 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
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/workspacesCreate a workspace
SessioncreateWorkspace

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

FieldTypeNotes
name requiredstring
slug requiredstringMatches ^[a-z0-9]+(-[a-z0-9]+)*$.
2 optional fields
FieldTypeNotes
modules optionalobjectModule id → enabled. Defaults to everything the free plan allows.
theme optionalstringDefaults to purple.
Request
{
  "name": "...",
  "slug": "...",
  ...
}

Response · 201

Response
{
  "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

StatusMeaning
201The created workspace.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
409The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse.
429Rate 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
FieldTypeNotes
id string (uuid)
slug string
name string
status stringOne of: active, suspended.
plan_id stringOne of: free, basic, pro.
default_locale stringDefaults to en.
timezone stringDefaults to UTC.
branding object
created_at string (date-time)
updated_at string (date-time)
curl
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/workspaces" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
  -H "Content-Type: application/json" \
  -d '{"name":"...","slug":"..."}'
GET/v1/workspaces/{id}Fetch a workspace
SessiongetWorkspace

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Response · 200

Response
{
  "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

StatusMeaning
200The workspace.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
slug string
name string
status stringOne of: active, suspended.
plan_id stringOne of: free, basic, pro.
default_locale stringDefaults to en.
timezone stringDefaults to UTC.
branding object
created_at string (date-time)
updated_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
PATCH/v1/workspaces/{id}Update workspace settings
SessionupdateWorkspace

Owner or admin only.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Request · UpdateWorkspaceInput

4 optional fields
FieldTypeNotes
name optionalstring
timezone optionalstring
default_locale optionalstring
branding optionalobject
Request
{
  ...
}

Response · 200

Response
{
  "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

StatusMeaning
200The updated workspace.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
id string (uuid)
slug string
name string
status stringOne of: active, suspended.
plan_id stringOne of: free, basic, pro.
default_locale stringDefaults to en.
timezone stringDefaults to UTC.
branding object
created_at string (date-time)
updated_at string (date-time)
curl
curl -X PATCH "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
  -H "Content-Type: application/json" \
  -d '{}'
GET/v1/workspaces/{id}/modulesList product modules with their enabled/allowed state
SessionlistWorkspaceModules

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Response · 200

Response
{
  "modules": [
    {
      "id": "writing",
      "name": "...",
      "tagline": "...",
      "category": "...",
      "min_plan": "free",
      "enabled": true,
      "allowed": true,
      "sort_order": 0
    }
  ]
}

Responses

StatusMeaning
200The module catalog for this workspace.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id stringExample: writing.
name string
tagline string
category string
min_plan stringOne of: free, basic, pro.
enabled booleanTurned on for this workspace.
allowed booleanPermitted by the workspace's plan.
sort_order integer
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/modules" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
PATCH/v1/workspaces/{id}/modulesTurn modules on or off
SessionupdateWorkspaceModules

Owner or admin only. Enabling a module the plan does not carry fails with 403 module_not_allowed.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Request

1 optional field
FieldTypeNotes
modules optionalobject
Request
{
  ...
}

Response · 200

Response
{
  "modules": [
    {
      "id": "writing",
      "name": "...",
      "tagline": "...",
      "category": "...",
      "min_plan": "free",
      "enabled": true,
      "allowed": true,
      "sort_order": 0
    }
  ]
}

Responses

StatusMeaning
200The refreshed module catalog.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id stringExample: writing.
name string
tagline string
category string
min_plan stringOne of: free, basic, pro.
enabled booleanTurned on for this workspace.
allowed booleanPermitted by the workspace's plan.
sort_order integer
curl
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 '{}'
GET/v1/workspaces/{id}/settingsList workspace settings
SessionlistWorkspaceSettings

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Response · 200

Response
{
  "settings": [
    {
      "workspace_id": "...",
      "key": "...",
      "value": {}
    }
  ]
}

Responses

StatusMeaning
200Every setting row.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
workspace_id string (uuid)
key string
value object
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/settings" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
GET/v1/workspaces/{id}/settings/{key}Fetch one setting
SessiongetWorkspaceSetting

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
key requiredstringExample: workspace.

Response · 200

Response
{
  "setting": {
    "workspace_id": "...",
    "key": "...",
    "value": {}
  }
}

Responses

StatusMeaning
200The setting.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
workspace_id string (uuid)
key string
value object
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/settings/YOUR_KEY" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
PUT/v1/workspaces/{id}/settings/{key}Create or replace one setting
SessionupsertWorkspaceSetting

Owner or admin only.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
key requiredstringExample: workspace.

Request

1 optional field
FieldTypeNotes
value optionalobject
Request
{
  ...
}

Response · 200

Response
{
  "setting": {
    "workspace_id": "...",
    "key": "...",
    "value": {}
  }
}

Responses

StatusMeaning
200The stored setting.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
workspace_id string (uuid)
key string
value object
curl
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 '{}'
GET/v1/workspaces/{id}/membersList workspace members
SessionlistWorkspaceMembers

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Response · 200

Response
{
  "members": [
    {
      "user_id": "...",
      "email": "...",
      "full_name": "...",
      "avatar_url": "...",
      "role": "owner",
      "status": "...",
      "created_at": "2026-01-01T00:00:00Z"
    }
  ]
}

Responses

StatusMeaning
200The members.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
user_id string (uuid)
email string (email)
full_name string
avatar_url string
role WorkspaceRoleOrdered 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
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/members" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
PATCH/v1/workspaces/{id}/members/{userId}Change a member's role
SessionupdateWorkspaceMember

Owner or admin only. The owner role cannot be granted or changed this way (403 cannot_modify_owner).

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
userId requiredstring (uuid)

Request · UpdateMemberInput

FieldTypeNotes
role requiredstringOne of: admin, editor, author, contributor, viewer.
Request
{
  "role": "admin"
}

Response · 200

Response
{
  "members": [
    {
      "user_id": "...",
      "email": "...",
      "full_name": "...",
      "avatar_url": "...",
      "role": "owner",
      "status": "...",
      "created_at": "2026-01-01T00:00:00Z"
    }
  ]
}

Responses

StatusMeaning
200The refreshed member list.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
user_id string (uuid)
email string (email)
full_name string
avatar_url string
role WorkspaceRoleOrdered 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
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"}'
DELETE/v1/workspaces/{id}/members/{userId}Remove a member
SessionremoveWorkspaceMember

Admins may remove anyone but the owner; members may remove themselves.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
userId requiredstring (uuid)

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/members/YOUR_USERID" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
GET/v1/workspaces/{id}/invitationsList invitations
SessionlistInvitations

Owner or admin only.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Response · 200

Response
{
  "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

StatusMeaning
200The invitations.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
workspace_id string (uuid)
email string (email)
role WorkspaceRoleOrdered 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
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/invitations" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/workspaces/{id}/invitationsInvite someone to the workspace
SessioncreateInvitation

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

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Request · InviteInput

FieldTypeNotes
email requiredstring (email)
role requiredstringOne of: admin, editor, author, contributor, viewer.
Request
{
  "email": "...",
  "role": "admin"
}

Response · 201

Response
{
  "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

StatusMeaning
201The invitation. In --dev mode only, also carries dev_invite_token so a local flow can be completed without email.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
409The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse.
429Rate 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
FieldTypeNotes
id string (uuid)
workspace_id string (uuid)
email string (email)
role WorkspaceRoleOrdered 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
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"}'
DELETE/v1/workspaces/{id}/invitations/{inviteId}Revoke an invitation
SessionrevokeInvitation

Owner or admin only.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
inviteId requiredstring (uuid)

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/invitations/YOUR_INVITEID" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/invitations/acceptAccept an invitation
SessionacceptInvitation

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

FieldTypeNotes
token requiredstring
Request
{
  "token": "..."
}

Response · 200

Response
{
  "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

StatusMeaning
200The joined workspace and the new membership.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404The invitation is unknown, expired or already used. Returns ErrorResponse.
409The 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
FieldTypeNotes
id string (uuid)
slug string
name string
status stringOne of: active, suspended.
plan_id stringOne of: free, basic, pro.
default_locale stringDefaults to en.
timezone stringDefaults to UTC.
branding object
created_at string (date-time)
updated_at string (date-time)
curl
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).

POST/v1/account/signupCreate an account
No authsignup

Rate limited per IP. Sends a verification email.

Request · SignupInput

FieldTypeNotes
email requiredstring (email)
password requiredstring (password)
2 optional fields
FieldTypeNotes
full_name optionalstring
next optionalstringRelative path to land on once the address is verified, carried into the verification email link. Non-relative values are ignored.
Request
{
  "email": "...",
  "password": "...",
  ...
}

Response · 201 · AuthResult

Response
{
  "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

StatusMeaning
201The new account and its session. Returns AuthResult.
400The 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.
409The write collides with existing state. Codes: slug_taken, email_taken, conflict (section version conflict), already_member, member_limit_reached. Returns ErrorResponse.
429Rate 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
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/account/signup" \
  -H "Content-Type: application/json" \
  -d '{"email":"...","password":"..."}'
POST/v1/account/loginSign in
No authlogin

Rate limited per IP. Repeated failures lock the account temporarily (423 account_locked).

Request · LoginInput

FieldTypeNotes
email requiredstring (email)
password requiredstring (password)
Request
{
  "email": "...",
  "password": "..."
}

Response · 200 · AuthResult

Response
{
  "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

StatusMeaning
200The session. Returns AuthResult.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
423Too many failed attempts; the account is temporarily locked. Returns ErrorResponse.
429Rate 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
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/account/login" \
  -H "Content-Type: application/json" \
  -d '{"email":"...","password":"..."}'
POST/v1/account/logoutSign out
Sessionlogout

Revokes the current token until it would have expired anyway.

Responses

StatusMeaning
204Success. No response body.
curl
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/account/logout" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/account/password-reset/requestRequest a password reset email
No authrequestPasswordReset

Always succeeds, whether or not the address exists.

Request · PasswordResetRequestInput

FieldTypeNotes
email requiredstring (email)
Request
{
  "email": "..."
}

Responses

StatusMeaning
204Success. No response body.
400The 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.
429Rate 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
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/account/password-reset/request" \
  -H "Content-Type: application/json" \
  -d '{"email":"..."}'
POST/v1/account/password-reset/confirmSet a new password from a reset token
No authconfirmPasswordReset

Request · PasswordResetConfirmInput

FieldTypeNotes
token requiredstring
password requiredstring (password)
Request
{
  "token": "...",
  "password": "..."
}

Responses

StatusMeaning
204Success. No response body.
400The 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.
429Rate 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
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/account/password-reset/confirm" \
  -H "Content-Type: application/json" \
  -d '{"token":"...","password":"..."}'
POST/v1/account/verifyVerify an email address
No authverifyEmail

Request · VerifyEmailInput

FieldTypeNotes
token requiredstring
Request
{
  "token": "..."
}

Responses

StatusMeaning
204Success. No response body.
400The 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.
429Rate 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
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/account/verify" \
  -H "Content-Type: application/json" \
  -d '{"token":"..."}'
POST/v1/account/resend-verificationResend the verification email
SessionresendVerification

Needs a session but stays reachable while unverified. Limited to one send per minute per user.

Request · ResendVerificationInput

1 optional field
FieldTypeNotes
next optionalstringRelative path to land on once the address is verified, carried into the verification email link. Non-relative values are ignored.
Request
{
  ...
}

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
429Rate 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
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/account/resend-verification" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
  -H "Content-Type: application/json" \
  -d '{}'
GET/v1/account/meThe signed-in user
SessiongetCurrentUser

Reachable while unverified, so the studio can render that state.

Response · 200

Response
{
  "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

StatusMeaning
200The current user.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
404No 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
FieldTypeNotes
id string (uuid)
email string (email)
full_name string
avatar_url string
status stringOne of: active, inactive.
verified_at string (date-time) | null
created_at string (date-time)
updated_at string (date-time)
curl
curl "https://api.andishi.nexuslabsstudio.com/v1/account/me" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
PATCH/v1/account/meUpdate the signed-in user's profile
SessionupdateProfile

Request · UpdateProfileInput

FieldTypeNotes
full_name requiredstringMax length 100.
Request
{
  "full_name": "..."
}

Response · 200

Response
{
  "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

StatusMeaning
200The updated user.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
404No 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
FieldTypeNotes
id string (uuid)
email string (email)
full_name string
avatar_url string
status stringOne of: active, inactive.
verified_at string (date-time) | null
created_at string (date-time)
updated_at string (date-time)
curl
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":"..."}'
POST/v1/account/me/avatarUpload an avatar
SessionuploadAvatar

Images only, 2 MB maximum.

Request

2 optional fields
FieldTypeNotes
data_base64 optionalstring
content_type optionalstringExample: image/png.
Request
{
  ...
}

Response · 200

Response
{
  "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

StatusMeaning
200The updated user.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
413The upload exceeds the workspace's limit. Codes: upload_too_large, avatar_too_large, storage_limit_exceeded. Returns ErrorResponse.
501No object store is configured. Returns ErrorResponse.

200 payload · user · User

Response fields
FieldTypeNotes
id string (uuid)
email string (email)
full_name string
avatar_url string
status stringOne of: active, inactive.
verified_at string (date-time) | null
created_at string (date-time)
updated_at string (date-time)
curl
curl -X POST "https://api.andishi.nexuslabsstudio.com/v1/account/me/avatar" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT" \
  -H "Content-Type: application/json" \
  -d '{}'
DELETE/v1/account/me/avatarRemove the avatar
SessiondeleteAvatar

Response · 200

Response
{
  "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

StatusMeaning
200The updated user.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
404No 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
FieldTypeNotes
id string (uuid)
email string (email)
full_name string
avatar_url string
status stringOne of: active, inactive.
verified_at string (date-time) | null
created_at string (date-time)
updated_at string (date-time)
curl
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/account/me/avatar" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/account/change-passwordChange the signed-in user's password
SessionchangePassword

Signs out every other session on success.

Request · ChangePasswordInput

FieldTypeNotes
current_password requiredstring (password)
new_password requiredstring (password)
Request
{
  "current_password": "...",
  "new_password": "..."
}

Responses

StatusMeaning
204Success. No response body.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
curl
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).

GET/v1/workspaces/{id}/api-keysList secret API keys
SessionlistAPIKeys

Owner or admin only. Never returns the secret itself.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Response · 200

Response
{
  "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

StatusMeaning
200The workspace's keys.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
name string
key_prefix stringFirst 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
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/api-keys" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/workspaces/{id}/api-keysMint a secret API key
SessioncreateAPIKey

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

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Request · CreateAPIKeyInput

FieldTypeNotes
name requiredstring
scopes requiredScope[]Unknown scopes are dropped; an empty result is invalid_scope.
Request
{
  "name": "...",
  "scopes": [
    "articles:read"
  ]
}

Response · 201 · CreatedAPIKey

Response
{
  "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

StatusMeaning
201The key metadata and, once only, the secret. Returns CreatedAPIKey.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
id string (uuid)
name string
key_prefix stringFirst 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
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":[]}'
DELETE/v1/workspaces/{id}/api-keys/{keyId}Revoke a secret API key
SessionrevokeAPIKey

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

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
keyId requiredstring (uuid)

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
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).

GET/v1/workspaces/{id}/webhooksList webhooks
SessionlistWebhooks

Secrets are omitted from this listing.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Response · 200

Response
{
  "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

StatusMeaning
200The webhooks.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
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 stringReturned 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
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/webhooks" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
POST/v1/workspaces/{id}/webhooksCreate a webhook
SessioncreateWebhook

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

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Request · CreateWebhookInput

FieldTypeNotes
url requiredstring (uri)Must be a public HTTPS address; private ranges are refused.
2 optional fields
FieldTypeNotes
events optionalWebhookEvent[]
description optionalstring
Request
{
  "url": "...",
  ...
}

Response · 201

Response
{
  "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

StatusMeaning
201The created webhook, including its signing secret.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
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 stringReturned 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
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":"..."}'
PATCH/v1/workspaces/{id}/webhooks/{webhookId}Update a webhook
SessionupdateWebhook

Owner or admin only. The response carries secret only when rotate_secret was set.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
webhookId requiredstring (uuid)

Request · UpdateWebhookInput

5 optional fields
FieldTypeNotes
url optionalstring (uri)
events optionalWebhookEvent[]
description optionalstring
is_active optionalboolean
rotate_secret optionalbooleanIssues a new signing secret and returns it in the response.
Request
{
  ...
}

Response · 200

Response
{
  "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

StatusMeaning
200The updated webhook.
400The 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.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
FieldTypeNotes
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 stringReturned 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
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 '{}'
DELETE/v1/workspaces/{id}/webhooks/{webhookId}Delete a webhook
SessiondeleteWebhook

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.
webhookId requiredstring (uuid)

Responses

StatusMeaning
204Success. No response body.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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.
404No 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
curl -X DELETE "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/webhooks/YOUR_WEBHOOKID" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"
GET/v1/workspaces/{id}/webhook-deliveriesList recent delivery attempts
SessionlistWebhookDeliveries

Owner or admin only. Returns the 50 most recent.

Path parameters

FieldTypeNotes
id requiredstring (uuid)Workspace UUID.

Query parameters

FieldTypeNotes
webhook_id string (uuid)

Response · 200

Response
{
  "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

StatusMeaning
200The deliveries.
401No credential, or the credential is wrong, revoked or expired. Codes: invalid_api_key, missing_token, invalid_token, unauthorized, invalid_credentials. Returns ErrorResponse.
403Authenticated, 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
FieldTypeNotes
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
curl "https://api.andishi.nexuslabsstudio.com/v1/workspaces/YOUR_ID/webhook-deliveries" \
  -H "Authorization: Bearer $ANDISHI_SESSION_JWT"