Stitchflow
Ghost logo

Ghost User Management API Guide

API workflow

How to automate user lifecycle operations through APIs with caveats that matter in production.

UpdatedMar 11, 2026

Summary and recommendation

Ghost's Admin API provides full read/write access to staff users under a single Admin API Key - there is no granular scope system; each key is all-or-nothing. Authentication uses a short-lived JWT (max 5-minute expiry) signed with HMAC-SHA256 using the hex-decoded secret from the key pair.

All request and response bodies wrap resources in a named array (e.g., `{"users":[...]}`) even for single-resource operations, which is a consistent but non-standard envelope pattern.

The API exposes endpoints for listing, reading, updating, and deleting staff users, plus a separate `/invites/` resource for provisioning. Staff users cannot be created directly - the only creation path is `POST /ghost/api/admin/invites/`, which triggers an email the recipient must manually accept before a user record is created.

This human-in-the-loop requirement is a hard constraint for any fully automated provisioning pipeline.

Ghost has no native SCIM 2.0 or SSO support on any plan. Integrating Ghost into an identity graph requires building against the Admin API directly, using webhook events (`user.added`, `user.edited`, `user.deleted`) for near-real-time sync, or falling back to scheduled polling of `GET /users/`.

Stitchflow's MCP server with 60+ deep IT/identity integrations can abstract this complexity, but the invite-acceptance gap remains a platform-level constraint that no integration layer can fully automate.

API quick reference

Has user APIYes
Auth methodJWT (Admin API Key) — a key ID and secret are combined to sign a short-lived JWT Bearer token; no OAuth 2.0 flow.
Base URLOfficial docs
SCIM availableNo
SCIM plan requiredN/A

Authentication

Auth method: JWT (Admin API Key) - a key ID and secret are combined to sign a short-lived JWT Bearer token; no OAuth 2.0 flow.

Setup steps

  1. In Ghost Admin, navigate to Settings → Integrations → Add custom integration.
  2. Copy the Admin API Key (format: {key_id}:{secret}).
  3. Split the key on : to obtain key_id and secret.
  4. Sign a JWT: header {alg:'HS256',typ:'JWT',kid:key_id}, payload {iat:<now_unix>,exp:<now+5min>,aud:'/admin/'}, signed with Buffer.from(secret,'hex') using HMAC-SHA256.
  5. Send requests with header Authorization: Ghost <jwt_token>.

Required scopes

Scope Description Required for
Admin API Key (full access) Admin API keys grant full read/write access to all Admin API resources including staff users. There is no granular scope system; access is all-or-nothing per key. All user-management operations (list, read, invite, update, delete staff users)

User object / data model

Field Type Description On create On update Notes
id string Unique internal Ghost user ID. auto-generated immutable Used in URL path for single-user operations.
name string Staff member's display name. required optional
slug string URL-safe identifier derived from name. auto-generated optional Must be unique across users.
email string Staff member's email address; used for login. required (via invite) optional Must be unique. New staff are created via invite endpoint, not direct POST.
profile_image string (URL) URL to the user's avatar image. optional optional
cover_image string (URL) URL to the user's cover/banner image. optional optional
bio string Short biography displayed on author pages. optional optional
website string (URL) User's personal website URL. optional optional
location string User's location string. optional optional
facebook string Facebook profile URL or username. optional optional
twitter string Twitter/X handle. optional optional
accessibility string (JSON) Serialized UI accessibility/preference settings. optional optional Internal Ghost Admin UI preferences.
status string (enum) User account status: active, inactive. auto-set to active on acceptance optional Setting to inactive suspends login access.
meta_title string SEO meta title for the author page. optional optional
meta_description string SEO meta description for the author page. optional optional
tour string (JSON) Tracks which onboarding tour steps have been completed. auto-generated optional
last_seen string (ISO 8601) Timestamp of last Admin UI login. null system-managed Read-only in practice.
created_at string (ISO 8601) Timestamp when the user record was created. auto-generated immutable
updated_at string (ISO 8601) Timestamp of last update. auto-generated auto-updated
roles array of role objects Assigned roles: Owner, Administrator, Editor, Author, Contributor. set via invite role field optional (PUT roles sub-resource) Only one role per user. Owner role cannot be assigned via API.

Core endpoints

List staff users

  • Method: GET
  • URL: https://{site}/ghost/api/admin/users/?limit=all&include=roles
  • Watch out for: Default limit is 15. Use limit=all to retrieve all users in one request. Does NOT return Members (subscribers), only staff.

Request example

GET /ghost/api/admin/users/?limit=all&include=roles
Authorization: Ghost <jwt>

Response example

{
  "users": [
    {"id":"1","name":"Jane","email":"jane@example.com",
     "status":"active","roles":[{"name":"Administrator"}]}
  ],
  "meta":{"pagination":{"page":1,"limit":"all","total":1}}
}

Read single staff user by ID

  • Method: GET
  • URL: https://{site}/ghost/api/admin/users/{id}/?include=roles
  • Watch out for: Response is always wrapped in a users array even for single-resource reads.

Request example

GET /ghost/api/admin/users/6123abc/?include=roles
Authorization: Ghost <jwt>

Response example

{
  "users": [
    {"id":"6123abc","name":"Jane","email":"jane@example.com",
     "status":"active","roles":[{"name":"Editor"}]}
  ]
}

Read current authenticated user (me)

  • Method: GET
  • URL: https://{site}/ghost/api/admin/users/me/?include=roles
  • Watch out for: Admin API keys are associated with an integration pseudo-user, not a real staff account. me returns that integration user.

Request example

GET /ghost/api/admin/users/me/
Authorization: Ghost <jwt>

Response example

{
  "users": [
    {"id":"6123abc","name":"API Integration",
     "roles":[{"name":"Administrator"}]}
  ]
}

Update staff user

  • Method: PUT
  • URL: https://{site}/ghost/api/admin/users/{id}/
  • Watch out for: Request body must wrap the user object in a users array. Must include id inside the body object. Omitting updated_at may cause a version conflict error on some Ghost versions.

Request example

PUT /ghost/api/admin/users/6123abc/
Authorization: Ghost <jwt>
Content-Type: application/json

{"users":[{"id":"6123abc","name":"Jane Doe","bio":"Editor"}]}

Response example

{
  "users": [
    {"id":"6123abc","name":"Jane Doe",
     "bio":"Editor","updated_at":"2025-01-10T12:00:00.000Z"}
  ]
}

Invite new staff user

  • Method: POST
  • URL: https://{site}/ghost/api/admin/invites/
  • Watch out for: Staff users cannot be created directly via POST /users/. The only creation path is sending an invite. The invited user must accept via email link before a user record is created. role_id must be a valid UUID from GET /roles/.

Request example

POST /ghost/api/admin/invites/
Authorization: Ghost <jwt>
Content-Type: application/json

{"invites":[{"email":"new@example.com","role_id":"<role_uuid>"}]}

Response example

{
  "invites": [
    {"id":"inv_abc","email":"new@example.com",
     "status":"sent","created_at":"2025-01-10T12:00:00.000Z"}
  ]
}

List pending invites

  • Method: GET
  • URL: https://{site}/ghost/api/admin/invites/
  • Watch out for: Invites expire after 7 days. Expired invites remain in the list with status expired until deleted.

Request example

GET /ghost/api/admin/invites/
Authorization: Ghost <jwt>

Response example

{
  "invites": [
    {"id":"inv_abc","email":"new@example.com",
     "status":"sent","expires":"2025-01-17T12:00:00.000Z"}
  ]
}

Delete staff user

  • Method: DELETE
  • URL: https://{site}/ghost/api/admin/users/{id}/
  • Watch out for: Deleting a user permanently removes them and reassigns their posts to the Owner. The Owner user cannot be deleted via API.

Request example

DELETE /ghost/api/admin/users/6123abc/
Authorization: Ghost <jwt>

Response example

HTTP 204 No Content

List roles

  • Method: GET
  • URL: https://{site}/ghost/api/admin/roles/
  • Watch out for: Role UUIDs are instance-specific and must be fetched dynamically; do not hardcode them across Ghost installations.

Request example

GET /ghost/api/admin/roles/
Authorization: Ghost <jwt>

Response example

{
  "roles": [
    {"id":"uuid-admin","name":"Administrator"},
    {"id":"uuid-editor","name":"Editor"},
    {"id":"uuid-author","name":"Author"}
  ]
}

Rate limits, pagination, and events

  • Rate limits: Ghost does not publish explicit rate-limit tiers in official documentation. Self-hosted instances have no enforced API rate limits by default. Ghost Pro (managed hosting) may apply undocumented infrastructure-level throttling.
  • Rate-limit headers: No
  • Retry-After header: No
  • Rate-limit notes: No official rate-limit headers or documented thresholds found in Ghost Admin API docs as of 2025.
  • Pagination method: offset
  • Default page size: 15
  • Max page size: 15
  • Pagination pointer: page / limit
Plan Limit Concurrent
Self-hosted (open source) No enforced limit (server capacity dependent) 0
Ghost Pro (all plans) Not publicly documented 0
  • Webhooks available: Yes
  • Webhook notes: Ghost Admin API supports webhooks configurable via Settings → Integrations or via POST /webhooks/. Webhooks fire on content and member lifecycle events. Staff user lifecycle events (user.added, user.edited, user.deleted) are available.
  • Alternative event strategy: Polling GET /users/ on a schedule if webhooks are not feasible.
  • Webhook events: user.added, user.edited, user.deleted, member.added, member.edited, member.deleted, post.published, post.unpublished, post.added, post.edited, post.deleted, site.changed

SCIM API status

  • SCIM available: No
  • SCIM version: Not documented
  • Plan required: N/A
  • Endpoint: Not documented

Limitations:

  • Ghost has no native SCIM 2.0 support on any plan.
  • No native SSO (SAML/OIDC) support; third-party solutions (e.g., miniOrange) are required.
  • Staff user provisioning must be done via the Admin API invite flow.

Common scenarios

Provision a new staff editor: fetch role UUIDs dynamically from GET /ghost/api/admin/roles/ - they are instance-specific and must never be hardcoded - then POST /ghost/api/admin/invites/ with the target email and role UUID.

Automate only to this point; the invitee must accept via email link before a user record exists in /users/. Invites expire after 7 days and remain in the list with status expired until explicitly deleted.

Suspend a staff user without deleting their content: PUT /ghost/api/admin/users/{id}/ with {"users":[{"id":"{id}","status":"inactive"}]}. This blocks login while preserving posts. The Owner account cannot be set to inactive via API.

Audit full staff access: GET /ghost/api/admin/users/?limit=all&include=roles retrieves all active staff (default page size is 15; limit=all is required for complete results). Follow with GET /ghost/api/admin/invites/ - pending invitees do not appear in /users/ until accepted, so both endpoints are required for a complete access picture.

Provision a new staff editor

  1. GET /ghost/api/admin/roles/ to retrieve the UUID for the 'Editor' role.
  2. POST /ghost/api/admin/invites/ with body {"invites":[{"email":"editor@example.com","role_id":"<editor_uuid>"}]}.
  3. The invitee receives an email; they must click the link and set a password to activate their account.
  4. After acceptance, GET /ghost/api/admin/users/?filter=email:'editor@example.com'&include=roles to confirm the user record exists with status active.

Watch out for: There is no way to programmatically complete the invite acceptance step - it requires human interaction via email link. Automate only up to sending the invite.

Suspend (deactivate) a staff user

  1. GET /ghost/api/admin/users/?filter=email:'user@example.com' to retrieve the user's id.
  2. PUT /ghost/api/admin/users/{id}/ with body {"users":[{"id":"{id}","status":"inactive"}]}.
  3. Confirm response contains "status":"inactive".

Watch out for: Setting status to inactive prevents login but does not delete the user or their content. The Owner account cannot be set to inactive via API.

Audit all staff users and their roles

  1. GET /ghost/api/admin/users/?limit=all&include=roles to retrieve all staff users with role data in a single request.
  2. GET /ghost/api/admin/invites/ to retrieve any pending invites not yet accepted.
  3. Combine both lists to produce a full picture of current and pending staff access.

Watch out for: Pending invitees do not appear in /users/ until they accept the invite. Always check /invites/ separately to audit who has been granted access but not yet activated.

Why building this yourself is a trap

The invite-only provisioning model is the primary automation trap: there is no POST /users/ endpoint, so any pipeline that assumes it can create a user record programmatically will fail silently or error. The human acceptance step is non-negotiable and must be documented in any runbook.

JWT generation is error-prone: the secret component of the Admin API Key is hex-encoded and must be decoded with Buffer.from(secret, 'hex') before use as the HMAC signing key. Passing the raw string produces a valid-looking but incorrect signature that Ghost will reject.

Tokens must also expire within 5 minutes; long-lived tokens are rejected at the API layer.

Ghost Pro does not publish rate-limit thresholds or expose rate-limit headers, so backoff logic cannot be tuned to documented limits - implement conservative exponential backoff by default.

On Ghost Pro, the me endpoint returns an integration pseudo-user, not a real staff account, which can produce misleading results if used for identity resolution in an identity graph context.

Automate Ghost workflows without one-off scripts

Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.

Every app coverage, including apps without APIs
60+ app integrations plus browser automation for apps without APIs
IT graph reconciliation across apps and your IdP
Less than a week to launch, maintained as APIs and admin consoles change
SOC 2 Type II. ~2 hours of your team's time

UpdatedMar 11, 2026

* Details sourced from official product documentation and admin references.

Keep exploring

Related apps

Abnormal Security logo

Abnormal Security

API Only
AutomationAPI only
Last updatedMar 2026

Abnormal Security is an enterprise email security platform focused on detecting and investigating threats such as phishing, account takeover (ATO), and vendor email compromise. It does not support SCIM provisioning, which means every app in your stack

ActiveCampaign logo

ActiveCampaign

API Only
AutomationAPI only
Last updatedFeb 2026

ActiveCampaign uses a group-based permission model: every user belongs to exactly one group, and all feature-area access (Contacts, Campaigns, Automations, Deals, Reports, Templates) is configured at the group level, not per individual. The default Adm

ADP logo

ADP

API Only
AutomationAPI only
Last updatedFeb 2026

ADP Workforce Now is a mid-market to enterprise HCM platform that serves as the HR source of record for employee data — payroll, benefits, time, and talent. User access is governed by a hybrid permission model: predefined security roles (Security Maste