{"slug":"mastodon","name":"Mastodon","domain":"joinmastodon.org","category":"Social network","summary":"Mastodon is self-serve: any app can register on any Mastodon server through a public API call, and there is no review, test-user list or user cap. The work is technical. Mastodon is decentralized, so you must register a separate OAuth client on each server your users come from, usually on the fly when a user enters their server domain.","tier":"self-serve","tierReason":"POST /api/v1/apps is a public endpoint that returns client credentials immediately, and the docs mention no app review or approval.","quality":"researched","updatedAt":"2026-10-27","composio":{"status":"unknown","note":"Mastodon isn't in Composio's managed-auth catalog, managed or bring-your-own. Plan to register OAuth clients yourself on each Mastodon server."},"totals":{"effort":{"min":1,"max":4,"unit":"hours"},"wait":{"min":0,"max":0,"unit":"minutes"},"firstConnection":"Today, in about 15 to 30 minutes"},"stages":[{"id":"build","name":"Your account","whoCanConnect":"You, on any Mastodon server where you've registered your app","userSees":"The server's standard authorization page, showing your app name and requested scopes","effort":{"min":15,"max":60,"unit":"minutes"},"wait":{"min":0,"max":0,"unit":"minutes"},"unlock":"Add per-server client registration so users on any server can connect","evidence":{"kind":"docs","sources":["s1","s3","s4"],"confidence":"high"}},{"id":"launch","name":"Any Mastodon server","whoCanConnect":"Any user on any Mastodon server your app registers with","userSees":"The same authorization page on their own server. The docs describe no unverified-app warning.","effort":{"min":1,"max":3,"unit":"hours"},"wait":{"min":0,"max":0,"unit":"minutes"},"unlock":null,"evidence":{"kind":"docs","sources":["s3","s4"],"confidence":"high"}}],"requirements":[{"id":"mastodon-account","label":"A Mastodon account","detail":"An account on any Mastodon server, used to test the authorization flow.","stage":"build","actor":"you","evidence":{"kind":"inferred","sources":["s4"],"confidence":"high"}},{"id":"https-redirect-uri","label":"An absolute redirect URI","detail":"A callback URL in your OAuth client. It must be absolute or the server returns 422. Use urn:ietf:wg:oauth:2.0:oob only for manual testing.","stage":"build","actor":"agent","evidence":{"kind":"docs","sources":["s3"],"confidence":"high"}},{"id":"per-instance-storage","label":"Per-server credential storage","detail":"Somewhere to store an encrypted client_id and client_secret for each Mastodon server domain, so you register once per server and reuse the credentials.","stage":"launch","actor":"agent","evidence":{"kind":"docs","sources":["s3","s4","s7"],"confidence":"high"}}],"steps":[{"id":"pick-scopes","title":"Choose the narrowest scopes","actor":"agent","stage":"build","time":{"min":5,"max":15,"unit":"minutes"},"detail":"Pick granular scopes such as read:statuses or write:statuses, or the broad read, write and profile scopes. The scopes you register are the most an authorize request can ask for. Avoid follow (deprecated since 3.5.0) and crypto (removed in 4.3.0).","actions":["List the API calls your integration makes","Map them to scopes (e.g. read:accounts, write:statuses, write:media)","Add profile if you only need the user's identity (4.3.0+)"],"agentNote":"An agent can map the features to scopes from the scopes doc.","gotcha":"Asking for a scope at authorize time that you didn't register makes the authorization fail.","link":{"label":"OAuth scopes","url":"https://docs.joinmastodon.org/api/oauth-scopes/"},"evidence":{"kind":"docs","sources":["s2"],"confidence":"high"}},{"id":"register-app","title":"Register your app on a Mastodon server","actor":"agent","stage":"build","time":{"min":5,"max":10,"unit":"minutes"},"detail":"Send POST https://{server}/api/v1/apps with client_name, redirect_uris, scopes (space-separated) and an optional website. No auth is needed. The response contains client_id and client_secret.","actions":["POST /api/v1/apps with client_name, redirect_uris (your redirect URI), scopes and website","Hand the client_id and client_secret to your backend's secret store without displaying them"],"agentNote":"An agent can make the call. A person or your backend should handle and store the client_secret.","gotcha":"Before 4.3, servers could delete apps that were registered but never used. On older servers, request a client_credentials token right after registering to keep the app active.","link":{"label":"Apps API","url":"https://docs.joinmastodon.org/methods/apps/"},"evidence":{"kind":"docs","sources":["s3","s1"],"confidence":"high"}},{"id":"configure-client","title":"Configure your OAuth client endpoints","actor":"agent","stage":"build","time":{"min":10,"max":20,"unit":"minutes"},"detail":"Set the authorize URL to https://{server}/oauth/authorize and the token URL to https://{server}/oauth/token, with response_type=code. Use PKCE (S256 only) on servers running 4.3.0 or later.","actions":["Set authorize URL and token URL for the server","Set scope to a subset of the registered scopes","Enable PKCE S256 if GET /.well-known/oauth-authorization-server returns 200"],"agentNote":"An agent can set up the client config. The client_secret should be loaded from your secret store, not pasted by the agent.","gotcha":"Very long state values can break Doorkeeper redirects. Keep state short and store the rest on your server.","link":{"label":"OAuth methods","url":"https://docs.joinmastodon.org/methods/oauth/"},"evidence":{"kind":"docs","sources":["s4","s7"],"confidence":"medium"}},{"id":"test-connection","title":"Connect your own account and verify","actor":"you","stage":"build","time":{"min":5,"max":10,"unit":"minutes"},"detail":"Sign in on your server, approve the app, and exchange the code for a token. Call GET /api/v1/accounts/verify_credentials to confirm the token works.","actions":["Open the authorize URL and approve","Exchange the code at /oauth/token (a code works only once)","Call /api/v1/accounts/verify_credentials"],"agentNote":"The person signs in and approves. An agent can then run the verification call.","gotcha":"invalid_grant usually means the redirect_uri doesn't match exactly, or the code came from a client registered on another server.","link":{"label":"Logging in with an account","url":"https://docs.joinmastodon.org/client/authorized/"},"evidence":{"kind":"docs","sources":["s5","s7"],"confidence":"high"}},{"id":"dynamic-registration","title":"Register automatically for each user's server","actor":"agent","stage":"launch","time":{"min":1,"max":3,"unit":"hours"},"detail":"Ask users for their server domain, look up stored credentials for that domain, and call POST /api/v1/apps if none exist. Route authorize and token requests to that server.","actions":["Add a server-domain input to your connect flow","Cache client credentials per domain, encrypted","Use the discovery endpoint to detect PKCE and scope support, falling back if it returns 404 (pre-4.3)"],"agentNote":"An agent can implement and test this logic.","gotcha":"Mastodon doesn't support standard RFC 7591 Dynamic Client Registration, so generic DCR clients won't work. Use /api/v1/apps.","link":{"label":"OAuth methods","url":"https://docs.joinmastodon.org/methods/oauth/"},"evidence":{"kind":"docs","sources":["s4","s3"],"confidence":"high"}}],"scopes":{"intro":"Mastodon has no scope review. Every scope is self-serve, but admin scopes only work for accounts with moderator or admin roles on that server.","tiers":[{"scope":"profile / read:* / write:* / push","grants":"The user's identity, plus reading and writing their data","level":"basic","consequence":"Nothing. Available immediately."},{"scope":"admin:read / admin:write (and granular admin:*)","grants":"Moderation and admin APIs","level":"sensitive","consequence":"No review, but only usable by staff accounts on that server"}]},"blockers":[{"symptom":"422 Redirect URI must be an absolute URI","cause":"The redirect URI passed to /api/v1/apps is relative or malformed.","fix":"Register a full absolute URL, or urn:ietf:wg:oauth:2.0:oob for testing.","evidence":{"kind":"docs","sources":["s3","s7"],"confidence":"high"}},{"symptom":"invalid_grant at token exchange","cause":"The redirect_uri doesn't match the registered one, or the client_id belongs to a different server.","fix":"Use the exact registered redirect URI and the credentials stored for that server's domain.","evidence":{"kind":"community","sources":["s7"],"confidence":"medium"}},{"symptom":"Discovery endpoint returns 404","cause":"The server runs a Mastodon version before 4.3.0, which has no discovery endpoint and no PKCE.","fix":"Fall back to the plain authorization code flow without PKCE and register only scopes that server supports.","evidence":{"kind":"docs","sources":["s4","s2"],"confidence":"high"}},{"symptom":"429 Too Many Requests","cause":"The default limit is 300 calls per 5 minutes per account and per IP. Media uploads are limited to 30 per 30 minutes.","fix":"Respect the X-RateLimit headers and back off.","evidence":{"kind":"docs","sources":["s6"],"confidence":"high"}}],"unknowns":[{"question":"Can individual server admins block or remove third-party apps, or restrict registration through /api/v1/apps?","why":"The official docs don't cover admin controls over third-party apps.","resolve":"Check the server's rules or ask its admins. Test registration on the major servers your users are on."},{"question":"Is POST /api/v1/apps rate-limited by IP?","why":"The rate limits page doesn't list a separate limit for app registration.","resolve":"Watch the X-RateLimit headers when registering, and cache credentials so you register once per server."},{"question":"Do Mastodon-compatible servers (forks or other ActivityPub software) behave the same way?","why":"This research covered upstream Mastodon only.","resolve":"Test against each server type your users need."}],"handoff":{"agentCan":["Choose scopes","Call POST /api/v1/apps on a server","Configure the authorize and token URLs and PKCE in your OAuth client","Implement per-server registration and credential caching","Run verify_credentials after you connect"],"youMust":["Have a Mastodon account and sign in to approve the test connection","Store the client_secret for each server securely","Decide which servers or server types to support"]},"sources":[{"id":"s1","title":"Obtaining client app access","url":"https://docs.joinmastodon.org/client/token/","publisher":"Mastodon docs","kind":"official","checkedAt":"2026-10-27"},{"id":"s2","title":"OAuth Scopes","url":"https://docs.joinmastodon.org/api/oauth-scopes/","publisher":"Mastodon docs","kind":"official","checkedAt":"2026-10-27"},{"id":"s3","title":"apps API methods","url":"https://docs.joinmastodon.org/methods/apps/","publisher":"Mastodon docs","kind":"official","checkedAt":"2026-10-27"},{"id":"s4","title":"oauth API methods","url":"https://docs.joinmastodon.org/methods/oauth/","publisher":"Mastodon docs","kind":"official","checkedAt":"2026-10-27"},{"id":"s5","title":"Logging in with an account","url":"https://docs.joinmastodon.org/client/authorized/","publisher":"Mastodon docs","kind":"official","checkedAt":"2026-10-27"},{"id":"s6","title":"Rate limits","url":"https://docs.joinmastodon.org/api/rate-limits/","publisher":"Mastodon docs","kind":"official","checkedAt":"2026-10-27"},{"id":"s7","title":"oauth_dropins.mastodon source","url":"https://oauth-dropins.readthedocs.io/en/latest/_modules/oauth_dropins/mastodon.html","publisher":"oauth-dropins docs","kind":"search","checkedAt":null},{"id":"s8","title":"Managed auth toolkits","url":"https://docs.composio.dev/toolkits/managed-auth.md","publisher":"Composio docs","kind":"composio","checkedAt":"2026-10-27"}],"feedback":{}}