BetterFans Link: the OnlyFans APIBetterFans Link

Writes and approvals

Writes wait for a person to approve them unless the key has auto-approve on. How actions work, from request to result.

Product page: OnlyFans messaging API

BetterFans Link never writes to OnlyFans or Fansly on its own. Your code or your agent asks for a write, a person on your team approves or rejects it, and only then does it run. MCP clients that sign in with OAuth always wait for that approval. Keys wait too, unless an owner or admin gave the key auto-approve. An auto-approve key's writes skip step 2: the request runs the action and answers 200 with the result.

The loop

  1. You create an action with Create action, or an MCP write tool such as send_message. BetterFans Link checks the params, writes a one line summary such as "Send a $12 message to @jess" and returns 202 with the action in pending status and its approvalUrl.
  2. A person opens the approvalUrl, or the Approvals page in the dashboard, reads the summary and the details, and approves or rejects it. They can add a note of up to 280 characters.
  3. An approved action runs on the account's platform and ends as executed, with result, or failed, with error.
  4. You find out through webhooks or by calling Get action.

A pending action that nobody decides within 24 hours becomes expired. Nothing is sent. No webhook event is sent for an expired action, so if you track actions yourself, treat expiresAt as the deadline or read the action again after it.

What you need

RequirementWithout it
A key with the write scope, or an MCP client given write access.403 missing_scope
Full access, or writes turned on for the account. An owner or admin can allow custom-access keys to write on the account's Settings page.403 writes_disabled
An Idempotency-Key header on the request.400 idempotency_key_required
Someone who can approve. Owners and admins can; developers and workspace viewers cannot.The action waits, then expires.

Action types

TypeWhat it doesPlatformsParams
send_messageSends a message to one fan. Add mediaIds to attach vault media. On OnlyFans, add priceCents to make it a paid message. On Fansly the message goes to the chat the account already has with the fan, is always free and attaches only free vault media.OnlyFans and FanslySend message
send_mass_messageSends one message to fan lists or a set of fans. The action shows an estimate of how many fans it will reach.OnlyFansSend mass message
unsend_messageTakes back a message. On OnlyFans, pass a massMessageId to take back every copy of a mass message.OnlyFans and FanslyUnsend message
add_fan_to_listAdds a fan to one of the fan lists.OnlyFansAdd fan to list
remove_fan_from_listRemoves a fan from one of the fan lists.OnlyFansRemove fan from list

Fansly accounts

A Fansly account takes two action types: send_message and unsend_message. A Fansly message:

  • goes to the chat the account already has with the fan,
  • is always free, so leave out priceCents (a price above 0 is 400 invalid_parameter),
  • attaches only free vault media.

If the account has no chat with the fan yet, or a media item is not free, nothing is sent and the approved action ends failed with the reason in error.

Any other type on a Fansly account is 400 not_supported_on_platform when you create it, and no action is created. Every action records its account's platform.

Action statuses

StatusLabelMeaning
pendingWaiting for approvalWaiting for a person to approve or reject it. It expires after 24 hours.
approvedApprovedA person approved it and it is about to run.
rejectedRejectedA person rejected it. Nothing was sent.
expiredExpiredNobody decided within 24 hours. Nothing was sent.
executingRunningRunning on the account's platform.
executedDoneDone. result holds what the write returned.
failedFailedIt ran and did not succeed. error says why.

Retrying safely

Create one new Idempotency-Key for each write you intend, such as a UUID, and store it with your own record of the request. If the request times out or you are not sure it arrived, send it again with the same key.

  • The same key with the same body returns the original action. Nothing is created twice.
  • The same key with a different body is 409 idempotency_conflict.
  • A key is unique across your whole workspace, in both modes, and never expires. Reusing one for another account, the other mode, another type or other params is also idempotency_conflict. Use a new UUID for every write you intend.

What the approver sees

The approval page shows the summary, the account, the fan, the full text and price, any media, and who asked: the key name, or the MCP client and the tool it used. For a mass message it also shows estimatedRecipients. Write your text as you want it sent. The approver can approve or reject it, not edit it.

Following the outcome

Prefer webhooks. action.pending tells approvers there is something to decide. action.executed, action.rejected and action.failed tell your code how it ended.

If you poll Get action instead, wait a few seconds between calls and stop when the status is executed, rejected, expired or failed. A person may take hours to decide, so do not hold a request open waiting for one.

Test mode

In test mode the loop is the same, but an approved action never calls OnlyFans or Fansly. It ends as executed, and its result is only {"simulated": true}. See test mode.

Drafts are not writes

The MCP tool draft_message gathers what a reply needs: the chat so far, what the fan has spent and bought, and paid messages they have not bought yet. The agent writes the reply from that. It never creates an action and nobody needs to approve it. An agent can draft a reply, show it to you, and then ask to send it with send_message. See approvals over MCP.

On this page