PILLAR · AUTO DM

OnlyFans Auto DM — Mass Messaging via API at Scale

Send welcome DMs, PPV blasts, and win-back campaigns across one creator account or hundreds — from a single API. Dry-run mode is on by default, live sends are gated behind an explicit per-account write flag, and every request is capped at 5,000 recipients. Operating since 2023.

DEFINITION

What is OnlyFans auto-DM?

OnlyFans auto-DM is the practice of sending direct messages to fans programmatically — typically welcome messages, pay-per-view offers, and re-engagement campaigns — without a chatter manually typing each one. The platform itself supports mass-message and welcome-message features in its native dashboard, but those built-in tools are limited: no spend-based audience resolution, no webhooks, no dry-run preview, and no cross-account workflow for agencies.

An auto-DM API solves that. Instead of clicking through the OnlyFans web UI, you make HTTP calls — POST a message with an audience object, read back the resolved recipient count, flip dry_run to false, and get a synchronous per-recipient result array. The Only API exposes more than 200 endpoints across OnlyFans and Fansly covering the messaging primitives that actually exist: single send, mass send with audience resolution, PPV with locked vault media, event-triggered send_dm automations, and the fan and subscriber data layer underneath. Mass messaging itself is currently OnlyFans-only — a mass-send against a Fansly account returns 501 PLATFORM_NOT_SUPPORTED.

The phrases people search for around this — “OnlyFans auto DM”, “OnlyFans mass DM”, “OnlyFans auto message”, “OnlyFans bulk message”, “OnlyFans message automation” — all describe the same underlying capability: programmatic, segmented outbound messaging at scale. Whether you call it auto-DM, mass-DM, or bulk message, what you actually want is an audience layer that targets the right fans, a preview step that tells you who a send will reach before it reaches them, and a webhook layer that closes the loop on conversion. That's what this page covers.

USE CASES

Why creators and agencies use auto-DM

Auto-DM isn't one feature — it's six different revenue workflows that all happen to share the same two send primitives: mass send and single send. Most creators start with welcome automations and graduate to the full stack within their first month.

01.

Welcome new subscribers

Create an automation with trigger_event: "new_subscriber" and action_type: "send_dm" and every new subscriber gets a DM the moment the event fires. The first 60 seconds is the highest-intent window of a fan's lifecycle — they're actively in the app, they just paid, and they're waiting for a reason to spend more. The automation fires immediately on the event; any delayed follow-up is scheduled by your own code.

02.

PPV blasts to a spend-based audience

Send pay-per-view offers to a slice of your subscriber list — not the whole roster. The mass-send audience object supports type (active / all / expired), min_spent, a subscribed_at since/until window, and an explicit fan_ids list you compute yourself. Set a price and attach vault media to lock the message. Tag-based and last-activity filtering are not supported for audience resolution.

03.

Win back expired subscribers

Set audience.type to "expired" to reach fans whose subscription lapsed, optionally narrowed with a since/until window on subscribed_at. Recover churn revenue that would otherwise quietly disappear. Run it as a dry run first to see exactly how many fans the window resolves to before a single message goes out.

04.

Spend-based segmentation

Use audience.min_spent to carve a high-spend band out of your subscriber cache and route premium copy and price points to it. For bands with an upper bound (for example $100–$500), pull the roster from the cached-subscribers endpoint, compute the band in your own code, and pass the result as audience.fan_ids. Same engine, several audiences, several price points.

05.

Dry-run before every blast

dry_run defaults to true on the mass-send endpoint. A request without it resolves the audience, returns the recipient count plus a 10-fan sample, and sends nothing. That makes "who will this actually reach?" a safe, free, repeatable question — and it catches an empty subscriber cache before it costs you a campaign. Set dry_run to false only when the preview looks right.

06.

Multi-creator agency workflow

Run the same messaging playbook across every creator account connected to a single CRM panel. Per-account session isolation, per-account proxy routing, central reporting and webhooks. Each connected account consumes one slot, so a 40-creator agency runs 40 slots on one panel and one API key.

PLAYBOOKS

Auto-DM strategies that convert

Three patterns that outperform generic blasts — and exactly which part of each one the API does for you versus which part you implement caller-side.

PLAYBOOK 01

The 3-message welcome sequence

The first message is fully automated: POST /api/crm/{crm_id}/automations with trigger_event: "new_subscriber" and action_type: "send_dm". The automation fires immediately when the event lands. There is no delay or recurrence field, so the T+6h and T+24h steps run from your own scheduler, calling the single-send endpoint when they are due.

  • T+0 minutes (automation): Greeting sent by the send_dm automation. Text is fixed — there are no merge tokens, so keep it warm and generic. No price, no link. Goal: establish a real reply.
  • T+6 hours (your scheduler): Soft qualification — ask one question. Send it per fan with POST /accounts/{of_user_id}/chats/{with_user_id}/messages so you can vary the copy. Tag the response with POST /api/crm/{crm_id}/fans/{fan_id}/tags.
  • T+24 hours (your scheduler): First PPV offer at a low entry price (typically $8–$15) — same single-send endpoint with a price and mediaFiles.

PLAYBOOK 02

Segmented PPV drop with whale carve-out

One drop, three audiences, three prices — three separate mass-send calls. The top band is a one-liner because audience.min_spent is a native filter. The bounded middle band is not: min_spent has no upper bound, so you compute that band yourself and pass it as audience.fan_ids.

  • Whales (lifetime spend > $500): {"audience": {"type": "active", "min_spent": 500}, "price": 40} — send this one first.
  • Mid-tier ($100–$500): Pull the roster from GET /accounts/{of_user_id}/subscribers/cached, filter the band on total_spent in your own code, and pass the IDs as audience.fan_ids. Same media, $25.
  • Entry tier (< $100): Same approach with the complement of the two bands above. Same media, $15, with an expiry time written into the copy to drive urgency — the API has no offer-expiry field.

PLAYBOOK 03

The expired-subscriber win-back

Set audience.type to "expired" and narrow it with a since / until window on subscribed_at. Note there is no last-activity or last-seen filter in audience resolution — the window is on subscription date, not on when the fan last opened a message.

  • Audience: {"type": "expired", "since": "2026-05-01T00:00:00Z", "until": "2026-06-01T00:00:00Z"}. Dry-run it first to confirm the window resolves to a sensible count.
  • Copy: One sentence of context, one question. No PPV in the first message. The same text goes to everyone in the audience — there is no per-fan rendering on the mass endpoint.
  • Follow-up: Watch the new_message webhook for replies and route those to a chatter or your own AI flow. For everyone who didn't reply, send one PPV 48 hours later from your own scheduler via the single-send endpoint. Then stop.

IMPLEMENTATION

How it works in The Only API

Six steps. The whole flow runs against the same OnlyFans API the rest of the platform uses — same auth, same webhooks, same session isolation. Two of these steps are non-optional and are the most common cause of a first call that appears to succeed but sends nothing: the write flag and dry_run.

  1. 01.

    Connect an OnlyFans account

    Register a CRM panel, then log a creator account in via POST /api/crm/{crm_id}/accounts/login. Sessions are persisted server-side with curl-cffi Chrome impersonation. The Only API supports both OnlyFans and Fansly accounts, but mass messaging is OnlyFans-only — Fansly accounts return 501 PLATFORM_NOT_SUPPORTED on the mass endpoint.

  2. 02.

    Populate the subscriber cache

    Mass-send audiences are resolved from the subscriber cache, not the /fans list. Run POST /api/crm/{crm_id}/accounts/{of_user_id}/subscribers/refresh first. An account with polling off has zero tracked fans, which is why a naive mass-send resolves to 0 recipients even for a creator with thousands of subscribers. Panel-level fan CRM data lives at GET /api/crm/{crm_id}/fans.

  3. 03.

    Enable writes for the account

    PATCH /api/crm/{crm_id}/accounts/{of_user_id}/polling with {"allow_of_write_actions": true}. Every non-dry-run send is gated behind this flag and returns 403 WRITES_DISABLED until it is set. Dry runs work without it.

  4. 04.

    Dry-run the mass send

    POST /api/crm/{crm_id}/accounts/{of_user_id}/messages/mass with text, optional price / lockedText / mediaFiles, and an audience object. dry_run defaults to true, so this first call resolves the audience and returns {success, dry_run: true, recipients, sample, sent: 0} without sending anything.

  5. 05.

    Send for real, then read the result

    Re-POST the same body with "dry_run": false. The response is {success, dry_run: false, recipients, sent, failed, results} — a per-recipient result array returned synchronously. There is no campaign_id and nothing to poll. For a single fan, use POST /api/crm/{crm_id}/accounts/{of_user_id}/chats/{with_user_id}/messages instead.

  6. 06.

    Subscribe to webhook events

    Register a webhook for events such as new_subscriber, new_purchase, new_tip, and new_message to trigger follow-ups, tag fans, or update your downstream CRM. Event names are snake_case and must be members of the allowed set; anything else returns 400 'Unknown event_type'.

POST /api/crm/{crm_id}/accounts/{of_user_id}/messages/massshell
# 1. Enable writes for the account (one time, required for live sends)
curl -X PATCH https://theonlyapi.com/api/crm/CRM_ID/accounts/509955039/polling \
  -H "X-API-Key: sk_live_REPLACE_ME" \
  -H "Content-Type: application/json" \
  -d '{"allow_of_write_actions": true}'

# 2. Dry run first — dry_run defaults to true, so this sends NOTHING.
#    Returns: {"success":true,"dry_run":true,"recipients":842,"sample":[...],"sent":0}
curl -X POST https://theonlyapi.com/api/crm/CRM_ID/accounts/509955039/messages/mass \
  -H "X-API-Key: sk_live_REPLACE_ME" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "<p>dropped something new in your inbox — unlock below</p>",
    "price": 24.99,
    "lockedText": false,
    "mediaFiles": ["1234567", "1234568"],
    "previews": [],
    "audience": {
      "type": "active",
      "min_spent": 100
    }
  }'

# 3. Same body with "dry_run": false to actually deliver.
#    Returns: {"success":true,"dry_run":false,"recipients":842,"sent":840,
#              "failed":2,"results":[{"fan_of_user_id":"...","ok":true}, ...]}
curl -X POST https://theonlyapi.com/api/crm/CRM_ID/accounts/509955039/messages/mass \
  -H "X-API-Key: sk_live_REPLACE_ME" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "<p>dropped something new in your inbox — unlock below</p>",
    "price": 24.99,
    "mediaFiles": ["1234567", "1234568"],
    "audience": { "type": "active", "min_spent": 100 },
    "dry_run": false
  }'

# Single fan (welcome DM, win-back follow-up, per-fan copy):
curl -X POST https://theonlyapi.com/api/crm/CRM_ID/accounts/509955039/chats/98765/messages \
  -H "X-API-Key: sk_live_REPLACE_ME" \
  -H "Content-Type: application/json" \
  -d '{"text": "<p>hey Sarah — saw you tipped on the Tuesday post</p>", "price": 0}'

Request and response reference

  • text — message body, HTML string, max 5,000 characters. Sent verbatim to every recipient. Required unless mediaFiles is set.
  • price — number. Non-zero turns the message into a PPV.
  • lockedText — boolean. Locks the text itself behind the price.
  • mediaFiles / previews — arrays of vault media IDs.
  • audience.type active (default), all, or expired.
  • audience.fan_ids — explicit recipient list; overrides all other audience fields.
  • audience.min_spent — numeric lower bound on lifetime spend. No upper bound exists.
  • audience.since / audience.until — ISO window on subscribed_at. Use this to page audiences larger than the 5,000-recipient cap.
  • dry_run — boolean, defaults to true. True returns {success, dry_run: true, recipients, sample, sent: 0} and sends nothing. False returns {success, dry_run: false, recipients, sent, failed, results}. No campaign_id is returned by either — the send is synchronous and there is nothing to poll.

Known failure responses: 403 WRITES_DISABLED if allow_of_write_actions is not enabled on the account, 501 PLATFORM_NOT_SUPPORTED for Fansly accounts, and a successful response with "recipients": 0 if the subscriber cache is empty — run POST /accounts/{of_user_id}/subscribers/refresh or set audience.type to "all".

Webhook events you'll care about

Auto-DM is half-finished without a webhook layer. The send is the easy part; reacting to what happens next is where revenue compounds. Event names are snake_case, and registering an unknown one returns 400 Unknown event_type. The complete allowed set is:

  • new_subscriber — a fan subscribed. The trigger for welcome automations.
  • renewed_subscriber — a subscription renewed.
  • expired_subscriber — a subscription lapsed. Feed your win-back list.
  • new_tip — a tip landed.
  • new_message — inbound message from a fan. The signal to route a chatter or AI flow into the conversation.
  • new_purchase — fan paid for a PPV. Auto-tag them as a converter or queue an upsell.
  • balance_increased — account balance moved.
  • payout_completed — a payout settled.
  • polling_paused — polling stopped for an account, so your fan data is going stale.
  • * — subscribe to every event.

There is no message delivery, read-receipt, or reply-tracking event — that telemetry does not exist in the product. Reply detection is done via new_message, and PPV conversion via new_purchase.

Every event payload is HMAC-SHA256 signed and retried on a [5s, 30s, 5m, 30m, 2h] backoff if your endpoint returns a non-2xx. Endpoints that hard-fail repeatedly auto- deactivate so a misconfigured webhook never blocks the rest of your queue.

SAFETY

Sending safely

No automation tool can promise “ban-proof” — anyone who says otherwise is selling. Here is exactly what the platform does for you, and — just as importantly — what it does not, so you can build the rest yourself:

  • 01.

    Dry-run is the default

    A mass-send request without an explicit "dry_run": false resolves the audience, returns the count and a 10-fan sample, and sends nothing. You cannot blast your list by accident on the first call.

  • 02.

    Writes are off until you turn them on

    Live sends are gated behind allow_of_write_actions on the account's polling config and return 403 WRITES_DISABLED until you explicitly PATCH it on. A misbehaving script against a fresh account cannot send anything.

  • 03.

    Hard 5,000-recipient cap per request

    MASS_MESSAGE_CAP truncates both an explicit fan_ids list and a cached-subscriber query at 5,000. This bounds any single request — and it means a 10,000-fan roster needs two paged calls using the since / until window, not one.

  • 04.

    Per-account session isolation + dedicated proxies

    Each connected creator account gets its own session file, Chrome fingerprint via curl-cffi, and an optional dedicated residential proxy. Sessions never cross-pollute, and IPs are not re-used across accounts.

  • 05.

    What we do not do: pace your sends

    Be clear on this before you build: the mass-send endpoint loops the resolved audience and issues one upstream call per recipient with no delay, jitter, or batch pause. If you want messages spread out over time — and for large audiences you probably should — chunk the roster into batches with audience.fan_ids and space the requests from your own scheduler. We would rather tell you that than let you assume it is handled.

  • 06.

    Operating since 2023

    The session and proxy stack has been live across thousands of connected accounts since 2023 — roughly three years of operating history behind the isolation model described above.

ANATOMY

What a high-converting auto-DM actually looks like

The mechanics of sending — endpoints, audiences, dry runs — are only half the equation. The other half is the message itself. Four structural patterns separate the messages that convert from the ones that get ignored.

  • Specific opener, never generic

    “Hey” converts at a fraction of “Hey Sarah, saw you tipped on the Tuesday post.” The API has no merge tokens, so specificity has to come from you: render the copy per fan from the subscriber or fan data and send it with the single-send endpoint. Reserve the mass endpoint for copy that reads well to a whole segment.

  • One offer, one price, one CTA

    Auto-DMs that try to do two things — sell media and ask for a tip and promote a stream — convert worse than messages that do one. Pick one outcome per send. Run the second one as a follow-up triggered by lack of response.

  • Price the offer to the segment, not the average

    A $30 PPV will lose money on entry-tier fans and leave money on the table with whales. Use audience.min_spent to split the roster and send the same media at several prices across several calls.

  • Always plan a follow-up before you send

    Single-shot blasts leave revenue uncollected. The fans who didn't open today will open tomorrow. Wire the follow-up yourself: react to new_message and new_purchase webhooks to branch, and run the timed leg from your own scheduler — the API has no delayed or recurring send.

FIT

Auto-DM use cases by tier

Same engine, different scale. Pick the row that matches your roster.

Solo creator

Free (evaluation) → Slots

  • Free: 1 connected account, 1,000 API calls/month — read endpoints and testing only
  • Mass messaging and webhooks are not included on Free
  • 1 slot at $20/month unlocks mass sends, PPV, automations and webhooks

Small agency

Slots

  • 5–14 creators on a single panel = 5–14 slots at $20/month each
  • Spend-based audiences, PPV blasts, expired-subscriber win-backs
  • $20/slot/month — no per-message fees, unlimited API calls per slot

Large agency

Slots (15+) / Enterprise

  • At 15 slots every slot drops to $15/month — including the ones you already had
  • Isolated sessions and dedicated proxies per creator account
  • Enterprise pricing is custom: priority support and custom SLAs

PRICING

Mass messaging is included on every paid slot

Free: 1 account and 1,000 API calls/month for evaluation — mass messaging and webhooks are not included. Slots: $20 per account slot per month, dropping to $15 per slot once you reach 15 slots (every slot, not just the new ones). Enterprise: custom. No per-message fees, no credits, no overage. See the full breakdown, including comparison against Infloww, on the pricing page.

See Pricing

Related reading: the OnlyFans API homepage covers the full endpoint surface for both OnlyFans and Fansly, OnlyFans automation walks through scheduled posts, fan tagging, and webhook automations beyond DMs, and The Only API vs Infloww compares us head-to-head with the largest existing dashboard product.

FAQ

Auto-DM frequently asked questions

Start sending smarter auto-DMs.

14-day free trial on paid plans. All 200+ endpoints across OnlyFans and Fansly. Cancel anytime.