OAuth apps
API v1 (OAuth apps)

OAuth apps

Register an OAuth app, send creators, fans or agencies through the authorization-code flow with PKCE, and keep their tokens fresh with rotating refresh tokens.

View as MarkdownUpdated Nov 1, 2026

API v1 at https://api.dropfans.io is authorized with OAuth 2.0 access tokens instead of API keys: the account (a creator, a fan or an agency) signs in on Dropfans, approves the scopes your app asks for, and your app receives a token that acts for that account. The authorization server is https://auth.dropfans.io, with OpenID Connect on top.

Register your app#

  1. Apply for API access and wait for the app to be approved.
  2. Ask the Dropfans team for an OAuth client for that app, with the redirect URIs you will use (exact https URLs; http://localhost is fine for development) and the scopes it needs.
  3. You receive a client id and a client secret. The secret is shown once: store it server-side only. If it leaks, ask for a new one; the old one stops working immediately.

Each API category of your app (chat, posts, vault, …) has its own switch on our side, so a scope you were granted can still answer 404 while its category is off for your app.

Endpoints#

EndpointPurpose
GET https://auth.dropfans.io/oauth2/authAuthorization: the account signs in and approves your scopes
POST https://auth.dropfans.io/oauth2/tokenExchange a code, or refresh a token
POST https://auth.dropfans.io/oauth2/revokeRevoke an access or refresh token (and its whole grant)
GET https://auth.dropfans.io/userinfoOpenID Connect claims of the token’s account
GET https://auth.dropfans.io/.well-known/openid-configurationDiscovery document
GET https://auth.dropfans.io/.well-known/jwks.jsonKeys that sign the id_token (RS256)
GET https://auth.dropfans.io/oauth2/sessions/logoutEnd-session endpoint

Read the endpoints from discovery rather than hard-coding them where your OAuth library supports it.

1. Send the account to the authorization endpoint#

PKCE with S256 is required for every client: create a random code_verifier, send code_challenge = BASE64URL(SHA256(code_verifier)). Requests without it, or with plain, are refused.

GET https://auth.dropfans.io/oauth2/auth?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
  &scope=openid%20offline_access%20read%3Aself%20read%3Achat
  &state=RANDOM_STATE
  &code_challenge=BASE64URL_SHA256_OF_VERIFIER
  &code_challenge_method=S256

redirect_uri must match a registered URI exactly. scope is space-separated; scopes your client is not allowed, or that the account type cannot hold, are left out of the grant (check scope in the token response). The account comes back to your redirect_uri with code and your state, or with error and error_description.

The code is single-use and expires after 60 seconds. Presenting a code twice revokes every token it produced.

2. Exchange the code#

Authenticate the client with HTTP Basic (client_secret_basic: client_id:client_secret, base64). It is the only client authentication method.

curl -X POST https://auth.dropfans.io/oauth2/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=authorization_code \
  -d code="$CODE" \
  -d redirect_uri=https://app.example.com/callback \
  -d code_verifier="$CODE_VERIFIER"
{
  "access_token": "…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "openid offline_access read:self read:chat",
  "refresh_token": "…",
  "id_token": "eyJ…"
}

refresh_token is only issued when the grant includes offline_access (or offline); id_token only with openid. Its sub is the account’s UUID, the same uuid that GET /users/me returns.

3. Call the API#

curl https://api.dropfans.io/v1/users/me -H "Authorization: Bearer $ACCESS_TOKEN"

See API v1 overview for paging, versions, errors and limits.

4. Refresh#

curl -X POST https://auth.dropfans.io/oauth2/token -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=refresh_token -d refresh_token="$REFRESH_TOKEN"

Refresh tokens rotate: every refresh returns a new access token and a new refresh token, and the one you sent stops working. Store the new pair before using it. If the same refresh token is presented again within 60 seconds (a retried request), you get the same new pair back; after that, reuse is treated as theft and the whole grant is revoked. The account then has to authorize your app again.

Token lifetimes#

TokenLifetime
Authorization code60 seconds, single use
Access token60 minutes
Refresh token30 days from issue; each refresh starts a new one
id_token60 minutes

Revoke#

curl -X POST https://auth.dropfans.io/oauth2/revoke -u "$CLIENT_ID:$CLIENT_SECRET" -d token="$TOKEN"

Revoking either token ends the grant. Webhook deliveries for that account stop as soon as no live grant of your app is left. Accounts can also disconnect your app themselves.

Userinfo#

GET /userinfo with the access token returns sub (the account UUID), plus preferred_username, name, picture, email and email_verified when the grant includes read:self.

Scopes#

System scopes: openid, offline_access, offline (openid adds an id_token; offline_access or offline adds a refresh token). API scopes, and which account types can hold them:

ScopeGrantsFanCreatorAgency
read:selfRead your profile and account details✓✓✓
read:chatRead your chats and messages✓✓✓
write:chatSend messages and manage chats✓✓✓
read:experienceRead fan-facing experiences (not served yet)✓✓✓
write:experiencePublish or unpublish fan-facing experiences (not served yet)—✓✓
read:fanRead your fans and their activity—✓✓
read:postRead your posts, comments and likes—✓✓
write:postCreate, edit and delete posts—✓✓
read:mediaRead your vault media—✓✓
write:mediaUpload and manage vault media and folders—✓✓
read:creatorRead creator profiles and the creators you manage—✓✓
write:creatorChange creator profile and settings—✓✓
read:insightsRead earnings and audience insights—✓✓
read:tracking_linksRead tracking links and who joined through them—✓✓
write:tracking_linksCreate and delete tracking links—✓✓
read:agencyRead your agency and its team——✓
write:agencyManage agency invites and team members——✓

Each API v1 operation needs every scope listed for it in the resource list; a token without them gets 403 {"error":"Insufficient scopes"}.

Errors#

The authorization and token endpoints answer in the standard OAuth form {"error": "invalid_grant", "error_description": "…"} (invalid_request, invalid_client, invalid_grant, invalid_scope, unsupported_grant_type, access_denied, …). The token endpoint allows 120 requests a minute per client and 60 per IP address.

Questions? support@dropfans.io