OnlyFans API

Available OnlyFans webhook events

A list of all available OnlyFans webhook events that you can subscribe to.

Looking for a specific event?

Please reach out to us, we can add it!

Looking for Fansly? See the available Fansly webhook events.

Accounts

accounts.connected

A new OnlyFans account was connected.

Please note that payload.client_reference_id can be null - it's only included if you provided us with client_reference_id while Creating Client Session

Example payload
{
  "event": "accounts.connected",
  "account_id": "acct_123",
  "payload": {
    "client_reference_id": "my_crm_model_12345", // Optional - Value you provided while creating Client Session - otherwise null
    "state": "authenticated",
    "progress": "started",
    "latestAuthAttempt": {
      "started_at": "2025-05-13T20:26:56.000000Z",
      "completed_at": null,
      "success": false,
      "error_message": null,
      "needs_otp": false,
      "needs_face_otp": false,
      "otp_phone_ending": null
    }
  }
}

accounts.reconnected

An OnlyFans account was reconnected.

Example payload
{
  "event": "accounts.reconnected",
  "account_id": "acct_123",
  "payload": {
    "state": "authenticated",
    "progress": "started",
    "latestAuthAttempt": {
      "started_at": "2025-05-13T20:26:56.000000Z",
      "completed_at": null,
      "success": false,
      "error_message": null,
      "needs_otp": false,
      "needs_face_otp": false,
      "otp_phone_ending": null
    }
  }
}

accounts.disconnected

An OnlyFans account was disconnected, either from the dashboard or with the Disconnect Account endpoint.

Every request for that acct_ ID from this point on returns a 404 with account_not_found, so stop polling it when this arrives.

Reconnecting gives you a new acct_ ID

The disconnected acct_ ID is never reused. If the creator reconnects later, they come back as a new account, so key your own records on onlyfans_id (the OnlyFans user ID, stable across reconnects) rather than on the acct_ ID.

Example payload
{
  "event": "accounts.disconnected",
  "account_id": "acct_123",
  "payload": {
    "account_id": "acct_123",
    "onlyfans_id": "123456789",
    "onlyfans_username": "janedoe",
    "disconnected_at": "2025-05-13T20:26:56+00:00"
  }
}

Not the same as a broken session

This event only fires when the account is deliberately disconnected. A connection that breaks on its own gives you accounts.session_expired or accounts.authentication_failed instead, and the account stays connected and usable in both of those cases.

accounts.session_expired

OnlyFans account connection has expired, but we automatically re-connected it using our system - you don't need to do anything.

Example payload
{
  "event": "accounts.session_expired",
  "account_id": "acct_123",
  "payload": {
    "state": "authenticated",
    "progress": "started",
    "latestAuthAttempt": {
      "started_at": "2025-05-13T20:26:56.000000Z",
      "completed_at": null,
      "success": false,
      "error_message": null,
      "needs_otp": false,
      "needs_face_otp": false,
      "otp_phone_ending": null
    }
  }
}

accounts.authentication_failed

OnlyFans account connection has expired, and we couldn't automatically re-connect it using our system. You need to reconnect with our /authenticate endpoint or manually in our dashboard.

Example payload
{
  "event": "accounts.authentication_failed",
  "account_id": "acct_123",
  "payload": {
    "state": "authenticated",
    "progress": "started",
    "latestAuthAttempt": {
      "started_at": "2025-05-13T20:26:56.000000Z",
      "completed_at": null,
      "success": false,
      "error_message": null,
      "needs_otp": false,
      "needs_face_otp": false,
      "otp_phone_ending": null
    }
  }
}

accounts.otp_code_required

A two-factor authentication code is required to connect one of your OnlyFans accounts.

Example payload
{
  "event": "accounts.otp_code_required",
  "account_id": "acct_123",
  "payload": {
    "state": "authenticated",
    "progress": "started",
    "latestAuthAttempt": {
      "started_at": "2025-05-13T20:26:56.000000Z",
      "completed_at": null,
      "success": false,
      "error_message": null,
      "needs_otp": false,
      "needs_face_otp": false,
      "otp_phone_ending": null
    }
  }
}

accounts.face_otp_required

Face verification is required to connect one of your OnlyFans accounts.

Example payload
{
  "event": "accounts.face_otp_required",
  "account_id": "acct_123",
  "payload": {
    "state": "authenticated",
    "progress": "started",
    "face_otp_verification_url": "https://id.onlyfans.com/s?123",
    "latestAuthAttempt": {
      "started_at": "2025-05-13T20:26:56.000000Z",
      "completed_at": null,
      "success": false,
      "error_message": null,
      "needs_otp": false,
      "needs_face_otp": false,
      "otp_phone_ending": null
    }
  }
}

Transactions

transactions.new

A new transaction has been received from a fan (eg. a subscription, a tip, a post, etc.).

Please note that this event may be delayed by up to 3 minutes.

Supported payload.type valuesDescription
messageA message has been sent to you
postPayment for a post
tipPayment for a tip
new_subscriptionPayment for a new subscription
recurring_subscriptionPayment for a recurring subscription
streamPayment for a stream
otherAnother type of transaction
Example payload
{
  "event": "transactions.new",
  "account_id": "acct_123",
  "payload": {
    "id": "abc123",
    "created_at": "2026-01-01 00:00:00",
    "type": "message",
    "tips_source": null,
    "amount": 10,
    "vat_amount": 0,
    "tax_amount": 0,
    "net_amount": 8,
    "fee_amount": 2,
    "currency": "USD",
    "description": "Payment for message from \"<a href=\"https://onlyfans.com/johndoe\">John Doe</a>\"",
    "status": "loading",
    "fan": {
      "id": "123",
      "username": "johndoe",
      "name": "John Doe",
      "display_name": "",
      "avatar": null
    },
    "fanData": {
      "available": true,
      "last_updated_at": "2026-01-01T00:00:00.000000Z",
      "spending": {
        "total": 6.6,
        "messages": 10,
        "subscribes": 2.6,
        "posts": 0,
        "tips": 4,
        "streams": 0
      },
      "previous_reply_at": null
    }
  }
}

Messages

messages.received

New message received from a fan.

Example payload
{
  "event": "messages.received",
  "account_id": "acct_123",
  "payload": {
    "responseType": "message",
    "text": "<p>Text</p>",
    "giphyId": null,
    "lockedText": false,
    "isFree": true,
    "price": 0,
    "isMediaReady": true,
    "mediaCount": 0,
    "media": [],
    "previews": [],
    "isTip": false,
    "isReportedByMe": false,
    "isCouplePeopleMedia": false,
    "queueId": 123,
    "isMarkdownDisabled": true,
    "fanData": {
      "available": true,
      "last_updated_at": "2025-10-23 00:52:34",
      "spending": {
        "total": 50,
        "messages": 30,
        "subscribes": 10,
        "posts": 0,
        "tips": 20,
        "streams": 0
      }
    },
    "fromUser": {
      "view": "s",
      "avatar": "https://example.com",
      "avatarThumbs": {
        "c50": "https://example.com",
        "c144": "https://example.com"
      },
      "header": null,
      "headerSize": null,
      "headerThumbs": null,
      "id": 123,
      "name": "Name",
      "username": "username",
      "canLookStory": true,
      "canCommentStory": true,
      "hasNotViewedStory": false,
      "isVerified": false,
      "canPayInternal": true,
      "hasScheduledStream": false,
      "hasStream": false,
      "hasStories": false,
      "tipsEnabled": false,
      "tipsTextEnabled": true,
      "tipsMin": 5,
      "tipsMinInternal": 1,
      "tipsMax": 200,
      "canEarn": false,
      "canAddSubscriber": false,
      "subscribePrice": 0,
      "displayName": "",
      "notice": "",
      "isRestricted": false,
      "canRestrict": true,
      "subscribedBy": true,
      "subscribedByExpire": false,
      "subscribedByExpireDate": "2034-12-22T00:38:29+00:00",
      "subscribedByAutoprolong": true,
      "subscribedIsExpiredNow": false,
      "currentSubscribePrice": 0,
      "subscribedOn": true,
      "subscribedOnExpiredNow": false,
      "subscribedOnDuration": "4 months",
      "listsStates": [
        {
          "id": "fans",
          "type": "fans",
          "name": "Fans",
          "hasUser": true,
          "canAddUser": false,
          "cannotAddUserReason": "ALREADY_EXISTS"
        },
        {
          "id": "following",
          "type": "following",
          "name": "Following",
          "hasUser": true,
          "canAddUser": false,
          "cannotAddUserReason": "ALREADY_EXISTS"
        },
        {
          "id": "recent",
          "type": "recent",
          "name": "Recent (last 24 hours)",
          "hasUser": false,
          "canAddUser": false,
          "cannotAddUserReason": "SYSTEM_LIST"
        },
        {
          "id": "muted",
          "type": "muted",
          "name": "Muted",
          "hasUser": false,
          "canAddUser": true,
          "cannotAddUserReason": null
        },
        {
          "id": "tagged",
          "type": "tagged",
          "name": "Tagged",
          "hasUser": false,
          "canAddUser": false,
          "cannotAddUserReason": "SYSTEM_LIST"
        },
        {
          "id": "friends",
          "type": "friends",
          "name": "Friends",
          "hasUser": false,
          "canAddUser": false,
          "cannotAddUserReason": "USER_NOT_PERFORMER"
        }
      ],
      "showMediaCount": true,
      "lastSeen": "2025-05-16T00:26:49+00:00",
      "canReport": true
    },
    "isFromQueue": false,
    "id": 123,
    "isOpened": false,
    "isNew": true,
    "createdAt": "2025-05-16T00:27:25+00:00",
    "changedAt": "2025-05-16T00:27:25+00:00",
    "cancelSeconds": 120,
    "isLiked": false,
    "canPurchase": false,
    "canPurchaseReason": "free",
    "canReport": true,
    "canBePinned": true,
    "isPinned": false,
    "replyToMessage": {
      "responseType": "message",
      "text": "<p>Text</p>",
      "giphyId": null,
      "lockedText": false,
      "isFree": true,
      "price": 0,
      "isMediaReady": true,
      "mediaCount": 0,
      "media": [],
      "previews": [],
      "isTip": false,
      "isReportedByMe": false,
      "isCouplePeopleMedia": false,
      "queueId": 123,
      "isMarkdownDisabled": true,
      "releaseForms": [],
      "fromUser": {
        "view": "s",
        "avatar": "https://example.com",
        "avatarThumbs": {
          "c50": "https://example.com",
          "c144": "https://example.com"
        },
        "header": "https://example.com",
        "headerSize": {
          "width": 4800,
          "height": 1440
        },
        "headerThumbs": {
          "w480": "https://example.com",
          "w760": "https://example.com"
        },
        "id": 123,
        "name": "Name",
        "username": "username",
        "canLookStory": true,
        "canCommentStory": true,
        "hasNotViewedStory": false,
        "isVerified": true,
        "canPayInternal": true,
        "hasScheduledStream": false,
        "hasStream": false,
        "hasStories": true,
        "tipsEnabled": true,
        "tipsTextEnabled": true,
        "tipsMin": 5,
        "tipsMinInternal": 1,
        "tipsMax": 200,
        "canEarn": true,
        "canAddSubscriber": false,
        "subscribePrice": 0,
        "hasStripe": false,
        "isStripeExist": false,
        "subscriptionBundles": [],
        "canSendChatToAll": true,
        "creditsMin": 10,
        "creditsMax": 200,
        "isPaywallPassed": true,
        "creditsMinAlternatives": 5,
        "canCreateLists": true,
        "showMediaCount": true,
        "lastSeen": "2025-05-16T00:24:06+00:00",
        "canReport": false,
        "subscribedOn": null
      },
      "isFromQueue": true,
      "canUnsendQueue": true,
      "unsendSecondsQueue": 1000000,
      "id": 123,
      "isOpened": false,
      "isNew": false,
      "createdAt": "2025-05-15T22:53:57+00:00",
      "changedAt": "2025-05-15T22:53:57+00:00",
      "cancelSeconds": 80792,
      "isLiked": false,
      "canPurchase": false,
      "canPurchaseReason": "free",
      "canReport": false,
      "canBePinned": true,
      "isPinned": false
    }
  }
}

messages.sent

A new message has been sent from one of your OnlyFans accounts to a fan.

Example payload
{
  "event": "messages.sent",
  "account_id": "acct_123",
  "payload": {
    "responseType": "message",
    "text": "<p>Text</p>",
    "giphyId": null,
    "lockedText": false,
    "isFree": true,
    "price": 0,
    "isMediaReady": true,
    "mediaCount": 1,
    "media": [
      {
        "id": 123,
        "type": "photo",
        "convertedToVideo": false,
        "canView": true,
        "hasError": false,
        "createdAt": "2025-04-05T06:19:31+00:00",
        "isReady": true,
        "files": {
          "full": {
            "url": "https://example.com",
            "width": 3840,
            "height": 5215,
            "size": 0,
            "sources": []
          },
          "thumb": {
            "url": "https://example.com",
            "width": 300,
            "height": 300,
            "size": 0
          },
          "preview": {
            "url": "https://example.com",
            "width": 960,
            "height": 1304,
            "size": 0
          },
          "squarePreview": {
            "url": "https://example.com",
            "width": 960,
            "height": 960,
            "size": 0
          }
        },
        "duration": 0,
        "releaseForms": [],
        "hasCustomPreview": false,
        "videoSources": {
          "240": null,
          "720": null
        }
      }
    ],
    "previews": [],
    "isTip": false,
    "isReportedByMe": false,
    "isCouplePeopleMedia": false,
    "queueId": 123,
    "isMarkdownDisabled": true,
    "releaseForms": [],
    "isFromQueue": false,
    "id": 123,
    "isOpened": false,
    "isNew": true,
    "createdAt": "2025-05-15T23:37:24+00:00",
    "changedAt": "2025-05-15T23:37:24+00:00",
    "cancelSeconds": 86400,
    "isLiked": false,
    "canPurchase": false,
    "canPurchaseReason": "free",
    "canReport": false,
    "canBePinned": true,
    "isPinned": false,
    "toUser": {
      "view": "s",
      "avatar": null,
      "avatarThumbs": null,
      "header": null,
      "headerSize": null,
      "headerThumbs": null,
      "id": 123,
      "name": "Name",
      "username": "username",
      "canLookStory": false,
      "canCommentStory": false,
      "hasNotViewedStory": false,
      "isVerified": false,
      "canPayInternal": true,
      "hasScheduledStream": false,
      "hasStream": false,
      "hasStories": false,
      "tipsEnabled": false,
      "tipsTextEnabled": true,
      "tipsMin": 5,
      "tipsMinInternal": 1,
      "tipsMax": 200,
      "canEarn": false,
      "canAddSubscriber": true,
      "subscribePrice": 0,
      "displayName": "",
      "notice": "",
      "isRestricted": false,
      "canRestrict": true,
      "subscribedBy": false,
      "subscribedByExpire": null,
      "subscribedByExpireDate": null,
      "subscribedByAutoprolong": null,
      "subscribedIsExpiredNow": null,
      "currentSubscribePrice": null,
      "subscribedOn": true,
      "subscribedOnExpiredNow": false,
      "subscribedOnDuration": "",
      "listsStates": [
        {
          "id": "fans",
          "type": "fans",
          "name": "Fans",
          "hasUser": true,
          "canAddUser": false,
          "cannotAddUserReason": "ALREADY_EXISTS"
        },
        {
          "id": "following",
          "type": "following",
          "name": "Following",
          "hasUser": false,
          "canAddUser": false,
          "cannotAddUserReason": "SYSTEM_LIST"
        },
        {
          "id": "recent",
          "type": "recent",
          "name": "Recent (last 24 hours)",
          "hasUser": true,
          "canAddUser": false,
          "cannotAddUserReason": "ALREADY_EXISTS"
        },
        {
          "id": "muted",
          "type": "muted",
          "name": "Muted",
          "hasUser": false,
          "canAddUser": true,
          "cannotAddUserReason": null
        },
        {
          "id": "tagged",
          "type": "tagged",
          "name": "Tagged",
          "hasUser": false,
          "canAddUser": false,
          "cannotAddUserReason": "SYSTEM_LIST"
        },
        {
          "id": "friends",
          "type": "friends",
          "name": "Friends",
          "hasUser": false,
          "canAddUser": false,
          "cannotAddUserReason": "USER_NOT_PERFORMER"
        }
      ],
      "showMediaCount": true,
      "lastSeen": "2025-05-15T23:37:03+00:00",
      "canReport": false
    }
  }
}

messages.ppv.unlocked

A PPV message that you sent has been purchased by a fan.

Example payload
{
  "event": "messages.ppv.unlocked",
  "account_id": "acct_123",
  "payload": {
    "id": "123",
    "type": "paided_message",
    "createdAt": "2025-05-05T22:18:00+00:00",
    "text": "has purchased your <a href='https://onlyfans.com/my/chats/chat/123?firstId=123'>message</a> for $5.00!",
    "replacePairs": {
      "{NAME}": "Name",
      "{MESSAGE_LINK}": "<a href='https://onlyfans.com/my/chats/chat/123?firstId=123'>message</a>",
      "{AMOUNT}": "$5.00"
    },
    "subType": "subscriber_pay_for_chat_message",
    "user_id": "123",
    "isRead": false,
    "canGoToProfile": true,
    "fanData": {
      "available": true,
      "last_updated_at": "2026-01-02 17:12:00",
      "spending": {
        "total": 6.6,
        "messages": 4,
        "subscribes": 2.6,
        "posts": 0,
        "tips": 4,
        "streams": 0
      }
    },
    "user": {
      "avatar": null,
      "avatarThumbs": null,
      "header": null,
      "headerSize": null,
      "headerThumbs": null,
      "id": 123,
      "name": "Name",
      "username": "username",
      "canLookStory": false,
      "canCommentStory": false,
      "hasNotViewedStory": false,
      "isVerified": false,
      "canPayInternal": false,
      "hasScheduledStream": false,
      "hasStream": false,
      "hasStories": false,
      "tipsEnabled": false,
      "tipsTextEnabled": true,
      "tipsMin": 5,
      "tipsMinInternal": 1,
      "tipsMax": 200,
      "canEarn": false,
      "canAddSubscriber": false,
      "subscribePrice": 0,
      "subscribedBy": false,
      "subscribedOn": false,
      "canReceiveChatMessage": false,
      "hideChat": false,
      "lastSeen": "2025-05-05T22:17:29+00:00",
      "isPerformer": false,
      "isRealPerformer": false,
      "view": "mn",
      "canReport": true,
      "subscribedByData": null,
      "subscribedOnData": null,
      "isBlocked": false
    }
  }
}

messages.deleted

A chat message was deleted.

Example payload
{
    "event": "messages.deleted",
    "account_id": "acct_123",
    "payload": {
        "id": "123"
    }
}

messages.liked

A chat message was liked. This event is delivered when OnlyFans reports the reaction over the connected account's websocket.

Example payload
{
  "event": "messages.liked",
  "account_id": "acct_123",
  "payload": {
    "id": "1234567890123"
  }
}

payload.id is the OnlyFans message ID, represented as a string. The reaction event does not include the fan ID, chat ID, message contents, or the time the action happened. Use account_id and a message-to-chat mapping saved from your message history or messages.sent / messages.received events if you need that context.

Normalize message IDs to strings both when storing that mapping and when looking up a reaction, for example with String(payload.id). Sent and received message payloads can contain numeric IDs, while reaction payloads contain strings; normalizing both avoids mismatched typed map keys.

messages.unliked

A like was removed from a chat message. Subscribe to both messages.liked and messages.unliked if you need to track changes to a message's reaction state.

Example payload
{
  "event": "messages.unliked",
  "account_id": "acct_123",
  "payload": {
    "id": "1234567890123"
  }
}

The payload has the same fields and limitations as messages.liked.

Repeated reactions and retries

A message can be liked, unliked, and liked again. Each change is a separate event, so do not deduplicate using only the message ID or the combination of message ID and event name. Use the X-OFAPI-Idempotency-Key header, when present, to recognise a retry of the same event. Webhook retries can arrive after newer events, so delivery order should not be treated as authoritative message state.

Reaction events do not indicate that a message was read or that a PPV was purchased. Use messages.ppv.unlocked for PPV purchases. Message pin/unpin webhook events are not currently available.

To read or change pins, use List Pinned Chat Messages, Pin Message, and Unpin Message. The existing filter=pinned option on List Chat Messages remains supported.

Chat Queue

chat_queue.updated

A queued message batch has been updated (e.g. progress change).

Example payload
{
    "event": "chat_queue.updated",
    "account_id": "acct_123",
    "payload": {
        "id": 123,
        "date": "2025-01-01T00:00:00+00:00",
        "canUnsend": true,
        "hasError": false,
        "isCanceled": false,
        "isCouplePeopleMedia": false,
        "isDone": false,
        "isReady": true,
        "pending": 6,
        "total": 7,
        "unsendSeconds": 1000000
    }
}

chat_queue.finished

A queued message batch has finished sending.

Example payload
{
    "event": "chat_queue.finished",
    "account_id": "acct_123",
    "payload": {
        "id": 123,
        "date": "2025-01-01T00:00:00+00:00",
        "canUnsend": false,
        "hasError": false,
        "isCanceled": true,
        "isCouplePeopleMedia": false,
        "isDone": true,
        "isReady": true,
        "pending": 0,
        "total": 7,
        "unsendSeconds": 0
    }
}

Tips

tips.received

A new tip has been received from a fan.

Example payload
{
    "event": "tips.received",
    "account_id": "acct_123",
    "payload": {
        "amountGross": 5,
        "amountNet": 4,
        "fanData": {
            "available": true,
            "last_updated_at": "2026-01-02 17:12:00",
            "spending": {
                "total": 6.6,
                "messages": 0,
                "subscribes": 2.6,
                "posts": 0,
                "tips": 4,
                "streams": 0
            }
        },
        "id": "123123123123",
        "type": "tip",
        "createdAt": "2026-01-02T17:12:00+00:00",
        "text": "paid you a tip of $5.00",
        "replacePairs": {
            "{SUBSCRIBER_LINK}": "<a href='https:\/\/onlyfans.com\/xxxxxxxx'>John Doe<\/a>",
            "{AMOUNT}": "$5.00",
            "{POST_BLOCK}": "",
            "{POST_DATA}": ""
        },
        "subType": "new_tips",
        "user_id": "9999999999",
        "isRead": false,
        "canGoToProfile": true,
        "user": {
            "avatar": "https:\/\/public.onlyfans.com\/files\/u\/uf\/ufs\/ufsXXXXXXXXXXXXX\/111111111\/avatar.jpg",
            "avatarThumbs": {
                "c50": "https:\/\/thumbs.onlyfans.com\/public\/files\/thumbs\/c50\/u\/uf\/ufs\/ufsXXXXXXXXXXXXX\/111111111\/avatar.jpg",
                "c144": "https:\/\/thumbs.onlyfans.com\/public\/files\/thumbs\/c144\/u\/uf\/ufs\/ufsXXXXXXXXXXXXX\/111111111\/avatar.jpg"
            },
            "header": "https:\/\/public.onlyfans.com\/files\/g\/g2\/g2e\/g2eXXXXXXXXXXXXXXX\/111111111\/header_image.jpg",
            "headerSize": {
                "width": 3362,
                "height": 1780
            },
            "headerThumbs": {
                "w480": "https:\/\/thumbs.onlyfans.com\/public\/files\/thumbs\/w480\/g\/g2\/g2e\/g2eXXXXXXXXXXXXXXX\/111111111\/header_image.jpg",
                "w760": "https:\/\/thumbs.onlyfans.com\/public\/files\/thumbs\/w760\/g\/g2\/g2e\/g2eXXXXXXXXXXXXXXX\/111111111\/header_image.jpg"
            },
            "id": 111111111,
            "name": "John Doe",
            "username": "johndoe",
            "canLookStory": false,
            "canCommentStory": false,
            "hasNotViewedStory": false,
            "isVerified": false,
            "canPayInternal": false,
            "hasScheduledStream": false,
            "hasStream": false,
            "hasStories": false,
            "tipsEnabled": false,
            "tipsTextEnabled": true,
            "tipsMin": 5,
            "tipsMinInternal": 1,
            "tipsMax": 200,
            "canEarn": false,
            "canAddSubscriber": false,
            "subscribePrice": 0,
            "subscribedBy": false,
            "subscribedOn": false,
            "canReceiveChatMessage": false,
            "hideChat": false,
            "lastSeen": "2026-01-02T17:09:46+00:00",
            "isPerformer": false,
            "isRealPerformer": false,
            "view": "mn",
            "canReport": true,
            "subscribedByData": null,
            "subscribedOnData": null,
            "isBlocked": false
        }
    }
}

Subscriptions

subscriptions.new

A new fan has subscribed.

Example payload
{
  "event": "subscriptions.new",
  "account_id": "acct_123",
  "payload": {
    "id": "123",
    "type": "subscribed",
    "createdAt": "2025-05-05T21:27:00+00:00",
    "text": "subscribed to your profile!",
    "replacePairs": {
      "{SUBSCRIBER_LINK}": "<a href='https://onlyfans.com/username'>Name</a>",
      "{PRICE}": "free"
    },
    "subType": "new_subscriber",
    "user_id": "123",
    "isRead": false,
    "canGoToProfile": true,
    "fanData": {
      "available": true,
      "last_updated_at": "2026-01-02 17:12:00",
      "spending": {
        "total": 6.6,
        "messages": 0,
        "subscribes": 2.6,
        "posts": 0,
        "tips": 4,
        "streams": 0
      }
    },
    "user": {
      "avatar": "https://example.com",
      "avatarThumbs": {
        "c50": "https://example.com",
        "c144": "https://example.com"
      },
      "header": "https://example.com",
      "headerSize": {
        "width": 767,
        "height": 1152
      },
      "headerThumbs": {
        "w480": "https://example.com",
        "w760": "https://example.com"
      },
      "id": 123,
      "name": "Name",
      "username": "username",
      "canLookStory": false,
      "canCommentStory": false,
      "hasNotViewedStory": false,
      "isVerified": true,
      "canPayInternal": false,
      "hasScheduledStream": false,
      "hasStream": false,
      "hasStories": false,
      "tipsEnabled": true,
      "tipsTextEnabled": true,
      "tipsMin": 5,
      "tipsMinInternal": 1,
      "tipsMax": 200,
      "canEarn": true,
      "canAddSubscriber": false,
      "subscribePrice": 0,
      "subscribedBy": false,
      "subscribedOn": false,
      "canReceiveChatMessage": false,
      "hideChat": false,
      "lastSeen": "2025-05-05T21:26:04+00:00",
      "isPerformer": true,
      "isRealPerformer": true,
      "view": "mn",
      "canReport": true,
      "subscribedByData": null,
      "subscribedOnData": null,
      "isBlocked": false
    }
  }
}

subscriptions.renewed

A fan has renewed their subscription.

Example data
{
  "event": "subscriptions.renewed",
  "account_id": "acct_123",
  "payload": {
    "id": "123",
    "type": "subscribed",
    "createdAt": "2025-05-05T21:27:00+00:00",
    "text": "Good news! <a href='https://onlyfans.com/u34547118'>u34547118</a> has Just restarted their monthly subscription to your OnlyFans profile!",
    "replacePairs": {
      "{SUBSCRIBER_LINK}": "<a href='https://onlyfans.com/u34547118'>u34547118</a>",
      "{PRICE}": "$4.00"
    },
    "subType": "returning_subscriber",
    "user_id": "34547118",
    "isRead": false,
    "canGoToProfile": true,
    "fanData": {
      "available": true,
      "last_updated_at": "2026-01-02 17:12:00",
      "spending": {
        "total": 6.6,
        "messages": 0,
        "subscribes": 2.6,
        "posts": 0,
        "tips": 4,
        "streams": 0
      }
    },
    "user": {
      "avatar": "https://example.com",
      "avatarThumbs": {
        "c50": "https://example.com",
        "c144": "https://example.com"
      },
      "header": "https://example.com",
      "headerSize": {
        "width": 767,
        "height": 1152
      },
      "headerThumbs": {
        "w480": "https://example.com",
        "w760": "https://example.com"
      },
      "id": 34547118,
      "name": "Name",
      "username": "username",
      "canLookStory": false,
      "canCommentStory": false,
      "hasNotViewedStory": false,
      "isVerified": true,
      "canPayInternal": false,
      "hasScheduledStream": false,
      "hasStream": false,
      "hasStories": false,
      "tipsEnabled": true,
      "tipsTextEnabled": true,
      "tipsMin": 5,
      "tipsMinInternal": 1,
      "tipsMax": 200,
      "canEarn": true,
      "canAddSubscriber": false,
      "subscribePrice": 0,
      "subscribedBy": false,
      "subscribedOn": false,
      "canReceiveChatMessage": false,
      "hideChat": false,
      "lastSeen": "2025-05-05T21:26:04+00:00",
      "isPerformer": true,
      "isRealPerformer": true,
      "view": "mn",
      "canReport": true,
      "subscribedByData": null,
      "subscribedOnData": null,
      "isBlocked": false
    }
  }
}

subscriptions.expired

A fan's subscription has expired.

Unlike subscriptions.new and subscriptions.renewed, this event is derived. OnlyFans emits no expiry signal, so we track the subscription end date it reports for each of your subscribers, notice when one passes, and confirm the lapse against OnlyFans before delivering it. Expect delivery within about 15 minutes of the subscription lapsing.

Four things worth planning for:

  • Subscriptions that had already lapsed before you enabled this event are not backfilled — you only receive lapses that happen from the moment you subscribe onwards. If another webhook on your team already watched that creator, you may receive lapses from shortly before you subscribed. The same bound applies after any interruption on our side: we resume from the last 48 hours rather than replaying further back.
  • Use expiredAt rather than the delivery time to decide when a subscription actually ended. It is the value OnlyFans reports, and it is fixed at the moment we detect the lapse, so it stays correct even if the fan resubscribes before the webhook reaches you.
  • A fan who renews instead of lapsing produces no event. We check with OnlyFans immediately before sending, so a renewal that lands first is never reported as a lapse. If they later lapse for real, the event fires then.
  • Coverage follows the subscribers we hold a current end date for. Every subscription that can later lapse was first created or renewed, and both carry that date, so in practice this is your whole active base. A subscription we have never observed — one that started before the account was connected and has not renewed since — can be missed.
Example payload
{
  "event": "subscriptions.expired",
  "account_id": "acct_123",
  "payload": {
    "id": "34547118:2025-05-05T21:27:00+00:00",
    "type": "subscription_expired",
    "subType": "expired_subscriber",
    "createdAt": "2025-05-05T21:34:12+00:00",
    "expiredAt": "2025-05-05T21:27:00+00:00",
    "user_id": "34547118",
    "user": {
      "avatar": "https://example.com",
      "avatarThumbs": {
        "c50": "https://example.com",
        "c144": "https://example.com"
      },
      "header": "https://example.com",
      "headerSize": {
        "width": 767,
        "height": 1152
      },
      "headerThumbs": {
        "w480": "https://example.com",
        "w760": "https://example.com"
      },
      "id": 34547118,
      "name": "Name",
      "username": "username",
      "canLookStory": false,
      "canCommentStory": false,
      "hasNotViewedStory": false,
      "isVerified": true,
      "canPayInternal": false,
      "hasScheduledStream": false,
      "hasStream": false,
      "hasStories": false,
      "tipsEnabled": true,
      "tipsTextEnabled": true,
      "tipsMin": 5,
      "tipsMinInternal": 1,
      "tipsMax": 200,
      "canEarn": true,
      "canAddSubscriber": false,
      "subscribePrice": 12.99,
      "subscribedBy": false,
      "subscribedOn": false,
      "canReceiveChatMessage": false,
      "hideChat": false,
      "lastSeen": "2025-05-04T09:12:31+00:00",
      "isPerformer": true,
      "isRealPerformer": true,
      "view": "f",
      "canReport": true,
      "subscribedByData": null,
      "subscribedOnData": {
        "price": 12.99,
        "newPrice": 12.99,
        "regularPrice": 12.99,
        "subscribePrice": 12.99,
        "discountPercent": 0,
        "discountPeriod": 1,
        "subscribeAt": "2025-04-05T21:27:00+00:00",
        "expiredAt": "2025-05-05T21:27:00+00:00",
        "renewedAt": null,
        "discountFinishedAt": null,
        "discountStartedAt": null,
        "status": null,
        "isMuted": false,
        "unsubscribeReason": "Redacted",
        "duration": "1 month",
        "tipsSumm": 15,
        "subscribesSumm": 12.99,
        "messagesSumm": 40.5,
        "postsSumm": 0,
        "streamsSumm": 0,
        "totalSumm": 68.49,
        "subscribes": [],
        "hasActivePaidSubscriptions": false
      },
      "isBlocked": false
    },
    "fanData": {
      "available": true,
      "last_updated_at": "2025-05-05T21:34:11.000000Z",
      "spending": {
        "total": 68.49,
        "messages": 40.5,
        "subscribes": 12.99,
        "posts": 0,
        "tips": 15,
        "streams": 0
      },
      "previous_reply_at": "2025-04-29T14:02:55.000000Z"
    }
  }
}

Posts

posts.liked

A fan has liked one of your posts.

Example payload
{
  "event": "posts.liked",
  "account_id": "acct_123",
  "payload": {
    "id": "123",
    "type": "favorited",
    "createdAt": "2025-05-05T22:22:00+00:00",
    "text": "liked your <a href='https://onlyfans.com/123/username'>post</a>",
    "replacePairs": {
      "{POST_LINK}": "<a href='https://onlyfans.com/123/username'>post</a>"
    },
    "subType": "new_favorite",
    "user_id": "123",
    "isRead": false,
    "canGoToProfile": true,
    "user": {
      "avatar": null,
      "avatarThumbs": null,
      "header": null,
      "headerSize": null,
      "headerThumbs": null,
      "id": 123,
      "name": "Name",
      "username": "username",
      "canLookStory": false,
      "canCommentStory": false,
      "hasNotViewedStory": false,
      "isVerified": false,
      "canPayInternal": false,
      "hasScheduledStream": false,
      "hasStream": false,
      "hasStories": false,
      "tipsEnabled": false,
      "tipsTextEnabled": true,
      "tipsMin": 5,
      "tipsMinInternal": 1,
      "tipsMax": 200,
      "canEarn": false,
      "canAddSubscriber": false,
      "subscribePrice": 0,
      "subscribedBy": false,
      "subscribedOn": false,
      "canReceiveChatMessage": false,
      "hideChat": false,
      "lastSeen": null,
      "isPerformer": false,
      "isRealPerformer": false,
      "view": "mn",
      "canReport": true,
      "subscribedByData": null,
      "subscribedOnData": null,
      "isBlocked": false
    }
  }
}

Users

users.typing

A fan is typing a message.

Example payload
{
  "event": "users.typing",
  "account_id": "acct_123",
  "payload": {
    "id": 1910197
  }
}

users.online

A fan was detected as online.

Recent messages.liked and messages.unliked events can also confirm that a fan is online when OFAPI can match the reaction to one stored, direct message sent by the creator. Reactions to queued messages, missing or ambiguous message records, and stale activity are ignored for presence; the reaction webhook is still delivered.

Subscribe to users.online separately to receive these presence transitions. Further reactions refresh activity but do not emit another users.online event while the fan is already online.

Example payload
{
  "event": "users.online",
  "account_id": "acct_123",
  "payload": {
    "fan": {
      "id": "123",
      "username": "johndoe",
      "name": "John Doe",
      "display_name": "John Doe",
      "spending": {
        "messages": 10,
        "subscribes": 2.6,
        "posts": 0,
        "tips": 4,
        "streams": 0,
        "total": 16.6
      }
    },
    "observed_at": "2026-01-02T17:12:00.000000Z",
    "status_changed_at": "2026-01-02T17:12:00.000000Z",
    "last_seen_online_at": "2026-01-02T17:12:00.000000Z"
  }
}

users.offline

A fan that was previously detected as online was detected as offline.

Example payload
{
  "event": "users.offline",
  "account_id": "acct_123",
  "payload": {
    "fan": {
      "id": "123",
      "username": "johndoe",
      "name": "John Doe",
      "display_name": "John Doe",
      "spending": {
        "messages": 10,
        "subscribes": 2.6,
        "posts": 0,
        "tips": 4,
        "streams": 0,
        "total": 16.6
      }
    },
    "observed_at": "2026-01-02T17:42:00.000000Z",
    "status_changed_at": "2026-01-02T17:42:00.000000Z",
    "last_seen_online_at": "2026-01-02T17:12:00.000000Z"
  }
}

These events are detected by periodic online-status polling, so delivery is not instant. users.online is emitted when a tracked fan transitions from unknown/offline to online. users.offline is emitted when a fan that was previously detected online is no longer present in a successful online-status check.

Data Exports

Data export webhook events are team-level events and do not include an account_id at the top level. Instead, the account_ids array is included in the payload.

data_exports.calculating_credits

Credit calculation has started for a data export.

Example payload
{
  "event": "data_exports.calculating_credits",
  "payload": {
    "id": "data_export_abc123",
    "type": "transactions",
    "status": "calculating_credits",
    "file_type": "csv",
    "credit_cost": null,
    "start_date": "2025-01-01T00:00:00+00:00",
    "end_date": "2025-01-31T23:59:59+00:00",
    "account_ids": ["acct_123", "acct_456"],
    "created_at": "2025-05-13T20:26:56+00:00"
  }
}

data_exports.calculating_credits_completed

Credit calculation has completed successfully for a data export.

Example payload
{
  "event": "data_exports.calculating_credits_completed",
  "payload": {
    "id": "data_export_abc123",
    "type": "transactions",
    "status": "calculating_credits_completed",
    "file_type": "csv",
    "credit_cost": 150,
    "start_date": "2025-01-01T00:00:00+00:00",
    "end_date": "2025-01-31T23:59:59+00:00",
    "account_ids": ["acct_123", "acct_456"],
    "created_at": "2025-05-13T20:26:56+00:00"
  }
}

data_exports.calculating_credits_failed

Credit calculation has failed for a data export.

Example payload
{
  "event": "data_exports.calculating_credits_failed",
  "payload": {
    "id": "data_export_abc123",
    "type": "transactions",
    "status": "calculating_credits_failed",
    "file_type": "csv",
    "credit_cost": null,
    "start_date": "2025-01-01T00:00:00+00:00",
    "end_date": "2025-01-31T23:59:59+00:00",
    "account_ids": ["acct_123", "acct_456"],
    "created_at": "2025-05-13T20:26:56+00:00"
  }
}

data_exports.in_progress

A data export is now in progress.

Example payload
{
  "event": "data_exports.in_progress",
  "payload": {
    "id": "data_export_abc123",
    "type": "transactions",
    "status": "in_progress",
    "file_type": "csv",
    "credit_cost": 150,
    "start_date": "2025-01-01T00:00:00+00:00",
    "end_date": "2025-01-31T23:59:59+00:00",
    "account_ids": ["acct_123", "acct_456"],
    "created_at": "2025-05-13T20:26:56+00:00"
  }
}

data_exports.completed

A data export has completed successfully. The payload includes a temporary download URL that expires after 1 hour.

Example payload
{
  "event": "data_exports.completed",
  "payload": {
    "id": "data_export_abc123",
    "type": "transactions",
    "status": "completed",
    "file_type": "csv",
    "credit_cost": 150,
    "start_date": "2025-01-01T00:00:00+00:00",
    "end_date": "2025-01-31T23:59:59+00:00",
    "account_ids": ["acct_123", "acct_456"],
    "created_at": "2025-05-13T20:26:56+00:00",
    "download_url": "https://example-bucket.s3.amazonaws.com/data-exports/team-slug/data_export_abc123xyz789.csv?X-Amz-Signature=...",
    "download_url_expires_at": "2025-05-13T21:26:56+00:00"
  }
}

data_exports.failed

A data export has failed. The failed_reason field is only included when the failure was due to insufficient credits.

Example payload
{
  "event": "data_exports.failed",
  "payload": {
    "id": "data_export_abc123",
    "type": "transactions",
    "status": "failed",
    "file_type": "csv",
    "credit_cost": 150,
    "start_date": "2025-01-01T00:00:00+00:00",
    "end_date": "2025-01-31T23:59:59+00:00",
    "account_ids": ["acct_123", "acct_456"],
    "created_at": "2025-05-13T20:26:56+00:00",
    "failed_reason": "Insufficient credits: 150 required"
  }
}

data_exports.cancelled

A data export was cancelled.

Example payload
{
  "event": "data_exports.cancelled",
  "payload": {
    "id": "data_export_abc123",
    "type": "transactions",
    "status": "cancelled",
    "file_type": "csv",
    "credit_cost": 150,
    "start_date": "2025-01-01T00:00:00+00:00",
    "end_date": "2025-01-31T23:59:59+00:00",
    "account_ids": ["acct_123", "acct_456"],
    "created_at": "2025-05-13T20:26:56+00:00"
  }
}

Fan Summary

fan_summary.completed

An AI Fan Profile Summary has finished generating. This event fires for both new summaries and regenerations, and is dispatched after the summary completes successfully.

Example payload
{
  "event": "fan_summary.completed",
  "data": {
    "fan": {
      "onlyfans_id": 123456789,
      "username": "example_fan"
    },
    "summary": {
      "status": "completed",
      "summary_data": {
        "name": "Jane Doe",
        "preferred_name": "Jane",
        "family_pets": "Has a golden retriever named Max",
        "travel_plans": "Planning a trip to Bali",
        "kinks": "",
        "hobbies": "Yoga, photography",
        "content_preferences": "Behind the scenes content",
        "themes": "",
        "requests": "",
        "interests": "Fitness, travel",
        "other_notes": ""
      },
      "analyzed_message_count": 504,
      "last_analyzed_at": "2026-03-19T12:00:00+00:00"
    }
  }
}

Media Uploads

These events only fire for asynchronous uploads (async=true). A synchronous upload returns its result in the response body, so there is nothing left to notify you about.

media_uploads.completed

An async media upload finished successfully. Fires for both Upload Media to OnlyFans CDN and Upload Media to Vault.

The payload carries the same fields as Get Upload Status, so you can subscribe to this event instead of polling the polling_url you got back from the 202 response.

Example payload (vault upload)
{
  "event": "media_uploads.completed",
  "account_id": "acct_123",
  "payload": {
    "id": "ofapi_media_abc123def456abc123def456abc123de",
    "prefixed_id": "ofapi_media_abc123def456abc123def456abc123de",
    "account_id": "acct_123",
    "status": "completed",
    "file_name": "photo.jpg",
    "created_at": "2026-04-03T16:17:58+00:00",
    "media_id": 1234567890,
    "media": {
      "id": 1234567890,
      "type": "photo",
      "convertedToVideo": false,
      "canView": true,
      "hasError": false,
      "createdAt": "2026-04-03T16:18:05+00:00",
      "isReady": false,
      "files": {
        "full": { "url": null, "width": 0, "height": 0, "size": 0, "sources": [] },
        "thumb": null,
        "preview": null,
        "squarePreview": null
      },
      "duration": 0,
      "releaseForms": [],
      "hasCustomPreview": false
    },
    "credits_used": 3,
    "completed_at": "2026-04-03T16:18:05+00:00"
  }
}

Vault uploads carry the OnlyFans media ID in media_id. CDN uploads do not have one yet — they are referenced by prefixed_id (or media.processId) until you attach them to a post or message — so media_id is null there:

Example payload (CDN upload)
{
  "event": "media_uploads.completed",
  "account_id": "acct_123",
  "payload": {
    "id": "ofapi_media_abc123def456abc123def456abc123de",
    "prefixed_id": "ofapi_media_abc123def456abc123def456abc123de",
    "account_id": "acct_123",
    "status": "completed",
    "file_name": "photo.jpg",
    "created_at": "2026-04-03T16:17:58+00:00",
    "media_id": null,
    "media": {
      "prefixed_id": "ofapi_media_abc123def456abc123def456abc123de",
      "file_name": "photo.jpg",
      "processId": "abc123def456abc123de",
      "host": "convert3.onlyfans.com",
      "sourceUrl": "https://of2transcoder.s3.amazonaws.com/upload/00000000-0000-0000-0000-000000000000/000000000000/photo.jpg",
      "extra": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...",
      "additional": { "user": "123456789" },
      "thumbs": [
        {
          "id": 1,
          "url": "https://cdn2.onlyfans.com/files/0/00/00000000000000000000000000000000/300x544_00000000000000000000000000000000.jpg"
        }
      ]
    },
    "credits_used": 3,
    "completed_at": "2026-04-03T16:18:05+00:00"
  }
}

The prefixed_id can only be used once. After you include it in a post or message it is no longer valid, and you must upload the file again to get a new one.

media_uploads.failed

An async media upload failed. The error field carries the reason, and no credits are charged for the upload itself.

Example payload
{
  "event": "media_uploads.failed",
  "account_id": "acct_123",
  "payload": {
    "id": "ofapi_media_abc123def456abc123def456abc123de",
    "prefixed_id": "ofapi_media_abc123def456abc123def456abc123de",
    "account_id": "acct_123",
    "status": "failed",
    "file_name": "photo.jpg",
    "created_at": "2026-04-03T16:17:58+00:00",
    "error": "Failed to download file from the provided URL.",
    "failed_at": "2026-04-03T16:18:05+00:00"
  }
}

Common error values include a failed download from your file_url, a file larger than your subscription's upload limit, not enough credits to cover the upload, and Async upload staging expired before processing completed. Please retry. for an upload that sat unprocessed for more than 24 hours.

On this page