Authentication
Secure every AdaptlyPost API request with an API key, learn where to generate keys and how to send them in your headers.
API Keys
AdaptlyPost uses API keys to authenticate REST API requests. You can generate and manage API keys from your dashboard under Settings > API Tokens.
Keep Your API Key Secure
Never expose your API key in client-side code, public repositories, or frontend applications. Always use environment variables or a secure vault to store your keys.Using Your API Key
Include your API key in the Authorization header of every request:
Authorization: Bearer your_api_key_hereExample Request
curl https://post.adaptlypost.com/post/api/v1/social-accounts \
-H "Authorization: Bearer adaptly_8f3c1d9a7b2e4605c1a8d7f36b904e2d"Roles and permissions
Every member of a workspace has one of four roles. The role decides what the member can do in the dashboard, and what any API key, MCP session or integration acting for that member can do. The check runs on the server for every request, so a client cannot work around it.
| Role | What it can do |
|---|---|
Adminadmin | Everything in the workspace: connects and disconnects social accounts, picks pages and boards, manages members, webhooks and every key, and does everything an Editor does. |
Editoreditor | Creates, edits, schedules, publishes, retries and deletes posts, including posts by other members. Manages webhooks and the post signature, uploads media, uses AI and reads analytics. Cannot connect or disconnect accounts. |
Contributorcontributor | Creates and edits its own drafts, uploads media, uses AI, reads posts and analytics, and creates its own keys. Cannot schedule, publish, retry or bulk schedule, cannot delete anything but its own drafts, and cannot touch other members' posts, accounts or webhooks. |
Viewerviewer | Reads posts, connected accounts, analytics, members and webhooks. Cannot create or change anything. |
Permissions by role
Each endpoint page names the permission it needs. A key holds exactly the permissions of its role, minus anything its creator can no longer do. GET /api/v1/me returns the list.
| Permission | Allows | Admin | Editor | Contributor | Viewer |
|---|---|---|---|---|---|
workspace.read | See the workspace at all | ✓ | ✓ | ✓ | ✓ |
workspace.manage | Change workspace settings | ✓ | – | – | – |
posts.read | Read posts and their per-platform results | ✓ | ✓ | ✓ | ✓ |
posts.draft | Create drafts, and edit or delete your own drafts | ✓ | ✓ | ✓ | – |
posts.others | Edit, publish, retry or delete posts created by other members | ✓ | ✓ | – | – |
posts.schedule | Schedule a post for a future time, and edit a scheduled post | ✓ | ✓ | – | – |
posts.publish | Publish now, and retry failed platforms | ✓ | ✓ | – | – |
posts.delete | Delete posts that are not drafts | ✓ | ✓ | – | – |
media.upload | Request upload URLs for images and videos | ✓ | ✓ | ✓ | – |
accounts.read | List connected social accounts | ✓ | ✓ | ✓ | ✓ |
accounts.manage | Connect, disconnect and re-check accounts, choose pages and boards, create connect links | ✓ | – | – | – |
analytics.read | Read analytics | ✓ | ✓ | ✓ | ✓ |
analytics.sync | Ask for an analytics refresh | ✓ | ✓ | – | – |
ai.generate | Generate captions and images | ✓ | ✓ | ✓ | – |
members.read | See who is in the workspace | ✓ | ✓ | ✓ | ✓ |
members.manage | Invite, change and remove members | ✓ | – | – | – |
tokens.own | Create and revoke your own API keys | ✓ | ✓ | ✓ | – |
tokens.manage | See and revoke every key in the workspace | ✓ | – | – | – |
webhooks.read | List webhooks | ✓ | ✓ | – | ✓ |
webhooks.manage | Create, update, delete and test webhooks | ✓ | ✓ | – | – |
signature.manage | Change the post signature | ✓ | ✓ | – | – |
Keys carry a role
When you create a key you choose its role. The key then acts with that role in the workspace it was created in, under these rules:
- A key can never be created with a role above the role of the member creating it. An Editor can mint Editor, Contributor or Viewer keys, not Admin keys.
- On every request the key's effective permissions are the permissions of its role, minus anything its creator can no longer do. The check reads the creator's current role, not the role at creation.
- If the creator is demoted, every key they created shrinks with them at once. An Editor key created by a member who is now a Contributor can only draft.
- If the creator leaves the workspace or is removed, their keys stop working and answer 401 with code token_issuer_lost_access. A workspace admin creates a replacement.
- An MCP session or OAuth grant has no role of its own: it acts with the signed-in member's role in the workspace the call lands in. That is the member's default workspace unless X-Workspace-Id names another.
Check what a key can do before you use it
Any valid key can call this endpoint. It returns the role, the exact permission list and a can object with draft, schedule and publish flags, so a client or an agent can hide what it may not do instead of finding out through a 403: GET /api/v1/meChoosing a workspace
A member can belong to several workspaces, in one organization or several. An OAuth connection, such as the Claude or ChatGPT connector, can act in any of them. An API key acts only in the workspace it was created in. Send the optional X-Workspace-Id header on any request to choose the workspace. Without it the call lands in the default workspace.
X-Workspace-Id: ag_02k7mThis endpoint lists the workspace ids you can send, with your role and a can object for each: GET /api/v1/workspaces
Authentication Errors
A missing, invalid or expired key answers 401. A key that exists but may not do what you asked answers 403. Every error body carries a machine-readable code so a client can tell them apart:
{
"statusCode": 401,
"message": "Invalid or missing API key",
"error": "Unauthorized"
}The key is valid but the member who created it no longer belongs to the workspace. The key is dead for good; ask a workspace admin for a new one.
{
"statusCode": 401,
"error": "Unauthorized",
"code": "token_issuer_lost_access",
"message": "This API key no longer works: the member who created it lost access to the workspace. Ask a workspace admin for a new key."
}An OAuth sign-in, such as the Claude or ChatGPT connector, used an email that has no AdaptlyPost account. Disconnect the app and connect again with the email you use on AdaptlyPost.
{
"statusCode": 401,
"error": "Unauthorized",
"code": "oauth_account_not_found",
"message": "You signed in as jane@example.com, which has no AdaptlyPost account. Disconnect this app and connect again with the email you use on AdaptlyPost."
}The key's role does not hold the permission this operation needs. The body names the missing permission, the role the key acts as, and what to do instead. For a post operation the required permission depends on the request: publishing now needs posts.publish, a future scheduledAt needs posts.schedule, and touching another member's post also needs posts.others.
{
"statusCode": 403,
"error": "Forbidden",
"code": "permission_denied",
"requiredPermission": "posts.publish",
"role": "contributor",
"keyRole": "contributor",
"issuerRole": "editor",
"tokenType": "api_token",
"message": "The Contributor role cannot publish posts. Send the post with saveAsDraft: true and ask a workspace member to publish it."
}Stop on a 403
A 403 with code permission_denied is final for that key. Retrying, waiting, or trying another key created by the same member will not change the answer. If your integration should not have hit it, ask a workspace admin for a key with a role that holds the permission, or send the post with saveAsDraft: true and let a member with posts.publish publish it.The organization's plan is not active, or does not include API access. Every call with the key or OAuth sign-in gets this answer until the plan is renewed or upgraded. code tells this 403 apart from the others.
{
"statusCode": 403,
"error": "Forbidden",
"code": "subscription_required",
"message": "This API key no longer works: your organization's plan does not include API access. Renew or upgrade the plan to use it again."
}X-Workspace-Id names a workspace the caller cannot reach. An API key gets the same answer for any workspace other than its own. Send an id from GET /api/v1/workspaces, or leave the header out.
{
"statusCode": 403,
"error": "Forbidden",
"code": "workspace_access_denied",
"workspaceId": "ag_03p4r",
"message": "You have no access to workspace ag_03p4r. Call GET /workspaces to list the workspaces you can use, and send one of those ids in the X-Workspace-Id header."
}For regulated teams
A Contributor seat and a Contributor key are enforced on the server, not in the interface. Every request, whether it comes from the dashboard, the REST API, the MCP server or an integration such as Zapier or n8n, passes through the same permission check in the post service before anything is written: the check reads the caller's role in the workspace at that moment, works out the permission the request needs from its facts (draft, scheduled or publish now; own post or a teammate's), and refuses with a 403 when it is missing. A key is created under a role, never above the member creating it, and its permissions are recalculated on every call as the overlap of the key's role and the creator's current role, so demoting the creator shrinks the key at once and removing them kills it. An auditor can point at three things: GET /api/v1/me, which returns the role and the exact permission list a key has right now; the 403 body, which names the missing permission, the role that lacked it and the kind of token that tried; and the post audit trail, where every create, schedule, publish, retry and delete attempt is recorded with who did it, through which key, under which role, from which status to which, and whether it was allowed or refused. The trail is written by the post service; it is not exposed in the dashboard or the API yet.