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.
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.
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.
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.
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.
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.
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_dmautomation. 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}/messagesso you can vary the copy. Tag the response withPOST /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
priceandmediaFiles.
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 ontotal_spentin your own code, and pass the IDs asaudience.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_messagewebhook 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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'.
# 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 unlessmediaFilesis 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, orexpired.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 onsubscribed_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}. Nocampaign_idis 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": falseresolves 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_actionson the account's polling config and return403 WRITES_DISABLEDuntil 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_CAPtruncates both an explicitfan_idslist 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 thesince/untilwindow, 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_idsand 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_spentto 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_messageandnew_purchasewebhooks 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.
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.