Skip to main content
You move your whole organization from V2 to V3 in one guided flow in the OFAuth dashboard. This guide covers the changes your app needs before and after cutover.
The move is a hard cut. At cutover, V2 stops serving your migrated connections, and V3 has no compatibility layer for V2 API keys, /v2/* routes, or the raw access proxy. Prepare your V3 integration before you schedule cutover.
Creators do not reconnect. Every Live connection moves with its id, its OnlyFans session, and its permissions.

What changes

What moves

What is removed

Concept map

Permissions to scopes

Each migrated connection keeps its own V2 permissions, mapped to V3 scopes. Nothing is widened, and there is no review wait. The extra queue:* and users:read scopes cover operations those V2 permissions already allowed. V3 lists scheduled posts and queued mass messages under queue:*, and blocked and restricted users under users:read.

Route map

V3 Access operations live under /v3/access. Authenticate with Authorization: Bearer <access_token> instead of apikey, and send the same account id in the x-linked-account-id header. These V2 routes call the same OnlyFans operation as the listed V3 operation.

Profile and notifications

Chats and mass messages

Subscribers and subscriptions

Users and lists

Posts and stories

Vault

Uploads use a different flow in V3: POST /v3/access/uploads/start, PUT /v3/access/uploads/:mediaUploadId/parts/:partNumber, then PUT /v3/access/uploads/:mediaUploadId/finalize or POST /v3/access/uploads/finalize (vault:write). These operations are not a one-to-one match for the V2 upload routes (POST /v2/access/uploads/init, PUT /v2/access/uploads/:mediaUploadId/parts/:partNumber, PUT /v2/access/uploads/:mediaUploadId, and POST /v2/access/uploads/complete).

Earnings and analytics

Promotions

Vault+

V2 Vault+ routes move from /v2/vault-plus/* to /v3/vault-plus/* with the same route table. Authenticate with Authorization: Bearer <access_token> and send the account id in x-linked-account-id. Vault+ must be on for the environment, or V3 returns 403 vault_plus_disabled. It must also be on for the account, or add and sync return 403 linked_account_vault_plus_disabled. Turn it on or off per account with linked account settings.

Not yet available on V3

These V2 routes have no V3 operation yet:
  • GET /v2/access/stories/map
  • GET /v2/access/stories/items
  • POST /v2/access/uploads/check
  • GET /v2/access/promotions/tracking-links/:trackingLinkId
  • PUT /v2/access/promotions/tracking-links/:trackingLinkId
  • GET /v2/access/promotions/trial-links/:trialLinkId
  • DELETE /v2/access/promotions/trial-links/:trialLinkId
  • PUT /v2/access/promotions/:promotionId
  • PUT /v2/access/promotions/bundles/:bundleId
If you rely on one of these, or on a raw proxy path, contact support@ofauth.com with the path before you schedule cutover.

Prepare before cutover

1

Save your Live callback and client secret

The migration flow pre-fills Live callbacks from your V2 redirect URLs. Review them, then save the Live client secret where your app runs. It is shown once.
2

Build the V3 flow for new users

New users connect through V3 OAuth. Follow Authorize and Tokens and refresh. The authorization URL replaces POST /v2/link/init, and your callback receives code and state instead of a connection id. Store each user’s refresh token, encrypted at rest, with its authorization_id, and persist the rotated refresh token on every refresh.
3

Rehearse in Test

Get a Test token and make one successful Access call with your Test client.
4

Make a successful Live token request

Get at least one token with your Live client ID and current client secret.
5

Update your webhook receivers

V3 signs deliveries with the Standard Webhooks scheme, which is the same scheme V2 used. Only the header names change:Receivers that verify with a Standard Webhooks library keep working with the same whsec_ secret. Receivers that read svix-* headers by name must change before cutover. Event names and payloads also change. See Webhook events.
6

Handle the V2 migration response

Add the account switch described in Switch each account. It runs after cutover, but it must be deployed before.
The migration flow checks each step before you schedule cutover. You can schedule cutover with steps still open only if you accept that your API calls will fail until your app uses V3.

Verify V3 webhooks

Until cutover, V2 still sends svix-* headers. This Node.js receiver accepts both header sets, so you can deploy it before cutover:
The migration itself sends no webhook events. Switch each account when V2 tells you it has migrated.

After cutover on V2

Every V2 response for a migrated connection is 401 with this body:
V2 Access responses also include requestId. Match on error being Connection has migrated. Live V2 API keys of a migrated organization keep answering this 401 for 90 days after cutover. After that they stop working and V2 rejects them as invalid keys. Remove them from your configuration once every account is on V3.

Switch each account

When V2 answers Connection has migrated for an account, exchange that account id once for V3 tokens. Store the tokens, then call V3 with the same id.

Exchange the account id

Call the token endpoint with your Live client credentials, using HTTP Basic authentication (client_secret_basic):
The endpoint also accepts client_id, which must match the Basic credentials, and resource, which you do not need. If you send resource, it must be https://api.ofauth.com/v3. Any other parameter returns invalid_request.
The response is the same token response every grant returns:
The scopes are the ones mapped from that account’s V2 permissions. linked_accounts.data[0].id is the account id you exchanged. Access tokens last 10 minutes. Refresh them with grant_type=refresh_token at the same endpoint, and store the new refresh token every time. Refresh tokens rotate on every use, and replaying a used refresh token revokes the whole authorization.

Exchange rules

  • Each account can be exchanged once.
  • The exchange is available for 90 days after cutover.
  • Only accounts migrated from your organization can be exchanged, and only with your Live client.
  • Each Live client can make 120 exchange requests per minute. Above that, the endpoint returns 429 with a Retry-After header.
  • Make sure only one worker exchanges a given account. Two parallel exchanges for the same id leave one with invalid_grant.
  • If you lose the tokens after the exchange, send the creator through authorization again.

Errors

Call V3 with the same id

cURL
V3 Access errors use the shape { "error": "...", "recovery": { "action": "..." }, "requestId": "..." }. The account errors: See V3 errors for every code.

Error code mapping

Manage linked accounts

These routes take the bearer access token. They replace GET /v2/account/connections and the V2 connection routes for your app.

Revoke tokens

Send token (and optionally token_type_hint) with your client credentials, using HTTP Basic authentication. Revoking a refresh token ends that session only. Revoking the last active session ends the whole authorization and sends authorization.revoked with reason: "oauth_revocation". Unknown tokens still return 200. To remove one account but keep the rest, call DELETE /v3/linked-accounts/{id} instead.

Group accounts under one person

When a creator or agency reconnects through your integration, you can group the other migrated accounts that belong to the same person. Add linked_account_ids to the Live authorization request:
After the person reconnects, the listed accounts are grouped under the person who reconnects. Each account keeps its id, its scopes, and your tokens for it. A malformed list, more than 25 ids, or the parameter on a non-Live client returns invalid_request. Grouping sends no webhook events.

Webhook events

V2 connection.* events become V3 lifecycle events. The migration maps each V2 endpoint filter like this: Lifecycle events are on or off per webhook destination (lifecycleEventsEnabled). A migrated endpoint gets them if its V2 filter had any connection.* event or no filter at all. Realtime turns on if the filter had any Realtime event or no filter at all. Only enabled V2 endpoints with a plain HTTPS URL (no query string, fragment, or credentials) move. An endpoint whose filter had only rules.updated is not moved. Payloads carry ids only. data has authorization_id, reason, and, for linked_account.* events, linked_account_id. authorization.* events list the account ids in linked_account_ids, or in added_linked_account_ids and removed_linked_account_ids (plus scopes_changed) for authorization.updated. Above 100 ids the list is replaced by linked_account_ids_truncated: true, linked_account_count, and, while the authorization still works, linked_accounts_url: "/v3/linked-accounts". authorization.revoked never carries linked_accounts_url. Lifecycle deliveries are not billed. See V3 webhooks. Every delivered event, lifecycle or Realtime, has this envelope:
linkedAccountId is null for authorization.* events. Deliveries use the Standard Webhooks headers webhook-id, webhook-timestamp, and webhook-signature, signed with the same whsec_ secret.

Realtime over WebSocket

Open wss://v3-realtime.ofauth.com/v3/realtime?linkedAccountId=<account id> with Authorization: Bearer <access_token>. Each message has eventId, type, schemaVersion, order, receivedAt, environment, integrationId, linkedAccountId, test, and data. The socket carries Realtime events only; lifecycle events arrive by webhook.

Authorization server discovery

The authorization server metadata is at:
The issuer is https://auth.ofauth.com/link. The document lists the authorize, token, and revoke endpoints and the JWKS URL.

Pricing

Migrating organizations get 30% off the first month of the V3 plan they choose. The Developer Build plan keeps its own launch discount instead; the two discounts do not combine. The offer has no end date. V2 billing stops at cutover.

FAQ

No. Migrated accounts keep their OnlyFans session. Creators only reconnect if OnlyFans ends the session, as on V2.
Yes. Every migrated account keeps its id. Send it in x-linked-account-id on V3.
Typed V3 Access operations. If an OnlyFans path you rely on has no operation, contact support@ofauth.com with the path.
Yes. One authorization can cover several linked accounts. The token response and GET /v3/linked-accounts list them. Choose one per request with x-linked-account-id.
Access tokens last 10 minutes. Refresh tokens last 90 days and are replaced on every refresh. If a session is unused for 90 days, the user authorizes again.
Send the user through authorization again. They see the accounts and scopes they already approved, and you get a new session in the same authorization. For a migrated account whose exchanged tokens you lost, the same applies.
Yes for data: deterministic fixtures and no OnlyFans calls. The difference is the flow: you authorize with your Sandbox client instead of a sk_sandbox_ key.
No. The whole organization moves together: every Live client app and every Live connection. V2 Sandbox apps, keys, and connections are not migrated. Set up V3 Sandbox and Test fresh.