The Pallyy MCP server is protected by an OAuth 2.1 authorization server that Pallyy runs itself, following the MCP authorization specification. This page is for people building or configuring an MCP client. If you just want to connect Claude, ChatGPT, or Cursor, the MCP server page has the steps, and the consent flow takes care of everything below.
Discovery
A request to https://app.pallyy.com/mcp without a bearer token answers 401 with a challenge pointing at the protected resource metadata:
WWW-Authenticate: Bearer resource_metadata="https://app.pallyy.com/.well-known/oauth-protected-resource/mcp"
That document (RFC 9728) names the authorization server, and the server's own metadata (RFC 8414) lists every endpoint:
| Document | URL |
|---|---|
| Protected resource metadata | https://app.pallyy.com/.well-known/oauth-protected-resource/mcp |
| Authorization server metadata | https://app.pallyy.com/.well-known/oauth-authorization-server |
The endpoints it advertises:
| Endpoint | URL |
|---|---|
| Authorization | https://app.pallyy.com/oauth/authorize |
| Token | https://app.pallyy.com/api/oauth/token |
| Revocation | https://app.pallyy.com/api/oauth/revoke |
Both discovery documents and the token and revocation endpoints allow cross-origin requests, so browser-based clients can complete the flow.
Client identification
Pallyy does not offer dynamic client registration. Clients identify themselves with a Client ID Metadata Document: the client_id is the https URL of a JSON document describing the client (its client_name, redirect_uris, and so on, in RFC 7591 format), and Pallyy fetches, validates, and caches that document the first time it sees the URL.
The metadata document, and every https redirect URI it lists, must be hosted on one of the supported platforms, including their subdomains:
claude.aichatgpt.comcursor.com
Native apps such as Claude Code may instead redirect to a loopback address (http://localhost, http://127.0.0.1, or http://[::1]) on any port, as RFC 8252 allows. A client_id on any other host is rejected with invalid_client, which is why other MCP clients connect with an API key instead.
Every client is a public client. The only supported token_endpoint_auth_method is none, and PKCE is required.
Authorization request
The client sends the user to the authorization endpoint with these parameters:
| Parameter | Required | Meaning |
|---|---|---|
client_id | Yes | The URL of the client's metadata document |
redirect_uri | Yes | One of the URIs registered in the document |
response_type | Yes | code |
code_challenge | Yes | PKCE challenge, S256 only |
code_challenge_method | Yes | S256 |
scope | No | Space-separated scopes, see below |
state | No | Returned unchanged on the redirect |
resource | No | Must be https://app.pallyy.com/mcp when present (RFC 8707) |
The user signs in to Pallyy if needed, reviews what the client asked for, optionally limits it to specific social sets, and approves or cancels. Approval redirects to redirect_uri with a single-use code that expires after 10 minutes, plus state and an iss parameter naming the issuer (RFC 9207). Cancelling redirects with error=access_denied.
Consent must come from the account owner in person: an administrator viewing an account on a user's behalf cannot approve a connection.
Scopes
The scopes are the same ones API keys use. Every token can read, so a request with no scope produces a read-only connection.
| Scope | Grants |
|---|---|
post-sets:write | Create and update draft post sets |
post-sets:publish | Required on top of post-sets:write to create or update post sets with status SCHEDULED |
media:write | Create media uploads |
notes:write | Create and update calendar notes |
offline_access | Not a permission: asks for a refresh token so the connection outlives the access token |
An unknown scope fails the request with invalid_scope. The consent page shows the user every scope requested, and the grant records exactly those.
Token exchange
The client exchanges the code at the token endpoint with a form-encoded or JSON POST:
| Parameter | Meaning |
|---|---|
grant_type | authorization_code |
code | The code from the redirect |
client_id | The same client_id as the authorization request |
redirect_uri | The same redirect_uri as the authorization request |
code_verifier | The PKCE verifier matching code_challenge |
resource | Optional, must match the authorization request when present |
A successful exchange returns the tokens:
{
"access_token": "pallyy_oat_...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "pallyy_ort_...",
"scope": "post-sets:write post-sets:publish offline_access"
}
A code is consumed on first use, so a failed exchange burns it and the client has to start over. The code is bound to the client, redirect URI, PKCE challenge, and resource it was issued for, and any mismatch fails with invalid_grant.
Token lifetimes
| Token | Prefix | Lifetime |
|---|---|---|
| Access token | pallyy_oat_ | 1 hour |
| Refresh token | pallyy_ort_ | 30 days from issue, capped at 365 days after the first token of the connection |
Access tokens are sent as Authorization: Bearer pallyy_oat_... to /mcp. They are also accepted by the REST API directly, with the grant's scopes and social set restriction, so a client that already holds a token can call https://app.pallyy.com/api/v1 with it.
A refresh token is only issued when the user approved offline_access. Refreshing uses grant_type=refresh_token with refresh_token and client_id, returns a new access token, and rotates the refresh token: the old one stops working and the new one gets a fresh 30-day lifetime, until the connection reaches its 365-day cap and the user has to approve it again.
Presenting a refresh token that has already been rotated is treated as a leak: every token issued from that authorization is revoked and the client has to send the user through consent again. Clients must store the rotated token as soon as the refresh response arrives.
Scopes and the social set restriction are read from the grant on every request rather than baked into the token. Re-approving a client updates its outstanding tokens immediately, and a grant that drops offline_access ends its refresh chain at the next rotation.
Revocation
Clients revoke a token by posting it to the revocation endpoint (RFC 7009) with token and client_id. Users revoke a whole connection from Settings > Connected Apps, which invalidates every token the client holds.
Errors
OAuth endpoints answer with the standard { "error", "error_description" } body. The codes you are most likely to meet:
| Code | Meaning |
|---|---|
invalid_client | The client_id is not a metadata document URL on a supported host, or the document could not be fetched or is invalid |
invalid_redirect_uri | The redirect_uri is not registered in the client's metadata document |
invalid_request | A required parameter is missing, or PKCE is missing or not S256 |
invalid_scope | The scope names something other than the scopes above |
invalid_target | The resource is not https://app.pallyy.com/mcp |
invalid_grant | The code or refresh token is expired, used, revoked, or does not match the request |
access_denied | The user cancelled on the consent page |
Once a token is issued, the MCP server and the REST API report problems with the API's own error format. A connection whose account no longer includes the MCP server gets a 403 with the code oauth-token:plan_disabled at the token endpoint, and apis_not_available on tool calls.
More in MCP server
Questions about the API? Email us at hey@pallyy.com and we'll get back to you.