Developers
Get access

Authentication

How your AI host signs in to Stensul with OAuth, what a token can do, which scopes to grant, and what a 401 or 403 means.

The Stensul MCP server uses the OAuth 2.0 authorization-code flow. A person signs in to Stensul in their browser, approves the connection, and from then on the AI host acts as that person, with that person’s permissions. There is no API-key or client-credentials path for MCP: there is always a signed-in user behind every call.

The flow

You ──ask──▶ AI host ──(1) no token yet──▶ Stensul MCP server
                │                               │
                │◀──(2) sign-in URL─────────────┘
                │
   browser ─────▼───(3) log in + approve on your Stensul instance
                │
                └──(4) code ─▶ host exchanges it for tokens ─▶ (5) every tool call
                                                                 carries the token
  1. The host connects to your per-instance endpoint, https://mcp.stensul.com/<your-instance>/mcp, and finds it needs a sign-in.
  2. It opens Stensul’s authorization URL, https://mcp.stensul.com/<your-instance>/oauth/authorize.
  3. You log in to your own Stensul instance and approve.
  4. Stensul redirects back with a code, and the host exchanges it at https://mcp.stensul.com/<your-instance>/oauth/token.
  5. The host sends the token with every request. When it expires, the host refreshes it or asks you to sign in again.

Hosts that support remote OAuth connectors (Claude, ChatGPT, Writer, Copilot Studio, Claude Code) do all of this for you. The two sign-in tools in the reference exist for hosts that can’t.

Three ways a client identifies itself

Client type Used by What the admin creates Secret?
Public client with PKCE Claude, ChatGPT, Claude Code, Gemini Enterprise, Cursor, VS Code, Windsurf, Amazon Quick An Authorization Code connection with PKCE (Public) on No, the Client ID is enough
Confidential client Writer, Copilot Studio An Authorization Code connection with PKCE off Yes, shown once at creation
Dynamic Client Registration Atlassian Rovo, and Claude Code when it runs without a Client ID Nothing; the client registers itself No

Admins create connections in Admin > Settings > API connections. The connection’s redirect URI must include your instance name: https://mcp.stensul.com/<your-instance>/oauth/callback. A redirect URI without it fails with a mismatch.

What a token can do

Every request passes two independent checks, and both must pass:

Scope Permission
Belongs to The connection The signed-in person
Answers Is this integration allowed to do this at all? Is this person allowed to do this?
Set by An admin, on the API connection The person’s role in Stensul
Changed in Admin > Settings > API connections Admin > Users & Roles
Error when missing 403, Invalid scope(s) provided. 403, Action is unauthorized.

The scope is checked first. So the same connection behaves differently for different people: a connection with emails:write lets a campaign manager create emails and gives a read-only user nothing. The MCP can never do more than the person could do in the Stensul app.

Scopes

Scope Grants Tools
libraries:read List libraries and read their configuration stensul-get-libraries
emails:read Read emails, layouts and rendered previews; run quality checks stensul-list-emails, stensul-read-email, stensul-preview-email, stensul-run-quality-check
emails:write Create emails, edit content and settings, delete modules, copy emails stensul-build-email, stensul-create-email, stensul-copy-email, stensul-update-email-content, stensul-update-email-settings, stensul-add-email-module, stensul-delete-email-module, stensul-apply-email-variation
images:write Upload images and request upload links stensul-upload-images, stensul-image-upload-session
dam:read Browse the image gallery stensul-list-gallery-images (and gallery search in stensul-upload-images)
dynamic-content:read List a library’s Segment Sets stensul-list-segment-sets
dynamic-content:write Apply Segment Sets and write per-segment content stensul-apply-segment-set, stensul-update-segment-content

api-read and api-write are for server-to-server integrations only. They do nothing on an MCP connection, and a connection created with only those two is rejected with Invalid scope(s) provided.

For drafting emails, libraries:read, emails:read, emails:write and images:write cover the usual flow. Add dam:read for the gallery and the two dynamic-content scopes for audience versions.

Grant fewer scopes on purpose

  • Read-only integrations (reporting, audits) need emails:read and libraries:read and nothing else.
  • A scope is a hard stop, not an instruction the model can reinterpret. An assistant that should draft copy but never touch assets is a connection without images:write.
  • One connection per tool or team lets you revoke one without breaking the others, and the audit trail shows which integration acted.

Changing scopes later

  • Adding a scope revokes every token for that connection. People sign in again on their next request and get the new scope.
  • Removing a scope takes effect immediately for those capabilities.

Dynamic Client Registration

Clients that self-register get every scope Stensul defines, and a person approves or declines the whole set at sign-in. DCR clients don’t appear in the API connections list and can’t be narrowed. What still limits them is the signed-in person’s permissions. If you need a connection limited to specific scopes, create a static connection and configure the client with its Client ID.

What an admin has to switch on

  1. The external API on your instance, and the access_admin_api_connections permission for whoever creates connections. Your Stensul customer success manager enables these.
  2. An API connection for each host (or rely on DCR where the host supports it).
  3. Host-side settings: for Claude, allow <your-instance>.stensul.com under Capabilities > Additional allowed domains so image uploads work.
  4. User permissions: the people using it need the same permissions they’d need in the app, for example edit_campaign to create or edit emails, access_dashboard to list emails, and duplicate_email_between_libraries to copy across libraries.

When a call fails

Status What it means What to do
401 No valid token: it expired or was revoked (for example after a scope was added) Sign in again. Hosts usually prompt you. The server tells the model to re-check sign-in on any 401 or 403
403, Insufficient OAuth scope or Invalid scope(s) provided. The connection lacks the scope An admin adds it, then everyone signs in again
403, Action is unauthorized. The person’s role lacks the permission Grant it in Admin > Users & Roles. Signing in again won’t help
403, Sent emails cannot be edited. The email was already sent Copy it and edit the copy
403, Your IP address is not in the allowed list The external API IP allowlist blocks the caller An admin updates Settings > External API > Allowed IPs
410 A client is calling the removed shared /mcp endpoint with headers Use your per-instance URL with OAuth

Quick test: if it fails for everyone on a connection, it’s a scope. If it fails for one person but works for another, it’s a permission. If it works in the Stensul app but not in your AI host, it’s a scope: the app doesn’t use API scopes.

Rate limits

The server limits requests per signed-in identity and, for the OAuth endpoints, per IP address. Stensul hasn’t published the numbers yet. If you hit a limit, slow down and retry.

Esc