TEASEDocs
ProductsClubHow-to

Run a native OnlyFans promo or bundle

Create or stop an on-platform free-trial/discount promo, or edit the 3/6/12-month bundle discounts, and know what "connected"/"stale" means before you trust the numbers.

Who this is for: a workspace owner.

Native promos and bundles are OnlyFans' own on-platform offers, not a TEASE construct — this area only mirrors what the engine sees on your promo page and queues writes back to it.

Never blind-retry a create

Every write here — create/stop/delete a promo, create/update/delete a bundle — is asynchronous: the endpoint answers {"status": "pending", "nonce": "..."} immediately and the real OnlyFans write happens later, through an engine job. If a synchronous caller's connection drops before that, a retry that assumes "it probably failed" can create a second live promo on OnlyFans. Poll /promo/ops/{nonce} instead of guessing.

Read the current promos and bundles

curl https://app.tease.link/api/admin/promo/campaigns \
  -H "Authorization: Bearer $TEASE_API_KEY"
{
  "items": [ { "promo_id": "pr_9021", "audience": "new_subscribers", "kind": "free_trial",
               "value": 7, "claim_limit": 100, "claims": 42, "expires_at": 1751880000,
               "active": true, "page_owner_id": "..." } ],
  "connected": true, "captured_at": 1751877000,
  "of_enabled": true, "ops": { "create_promo": true, "stop_promo": true, "..." : "..." }
}

connected is false in two different situations you should read differently: the engine has never captured this page's promo screen yet ("connecting"), or it captured one over 3 hours ago (stale — treat the numbers as possibly out of date). captured_at: 0 distinguishes the first case from the second. GET /api/admin/promo/bundles returns the same envelope shape for the three fixed 3/6/12-month bundle slots on the current page only — bundles are not summed across accounts the way promos can be with ?scope= (see below).

Pass ?scope=<page|creator|tenant>&id=... on the promo list to widen it beyond the current page — each returned promo carries its own page_owner_id so a multi-account read stays attributable.

Create a promo

curl -X POST https://app.tease.link/api/admin/promo/campaigns \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "audience": "new_subscribers", "kind": "free_trial", "value": 7,
        "limit": 100, "expiry_days": 14, "message": "" }'
FieldTypeDescription
audiencestringall_subscribers, new_subscribers, or expired_subscribers.
kindstringfree_trial or first_month (a discount).
valueintegerDays for free_trial (1–30). Percent for first_month (5–65, step 5).
limitintegerClaim cap; 0 = unlimited.
expiry_daysintegerDays until the promo ends; 0 = no end date.
messagestringOptional note.

A value outside these ranges is rejected with 422 before it ever reaches the engine — OnlyFans' own limits, checked locally so you find out immediately rather than a minute later.

curl -X POST https://app.tease.link/api/admin/promo/campaigns/pr_9021/stop \
  -H "Authorization: Bearer $TEASE_API_KEY"

Stopping means no new claims — it does not touch fans who already claimed.

Create or edit a bundle

Bundles are fixed 3/6/12-month slots — there's no list of arbitrary bundles to create, only these three to configure:

curl -X PUT https://app.tease.link/api/admin/promo/bundles/d3 \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "duration": 3, "discount": 15 }'

bundle_id in the path is whatever OnlyFans' own feed reported for that slot, or d<duration> (d3 / d6 / d12) when it didn't — read it off the GET /promo/bundles list rather than guessing it. duration must be 3, 6, or 12; discount is 050, step 5.

Poll a pending write

curl https://app.tease.link/api/admin/promo/ops/a1b2c3d4 \
  -H "Authorization: Bearer $TEASE_API_KEY"
{ "nonce": "a1b2c3d4", "status": "done", "attempts": 1, "error": "",
  "requested_at": 1751877000, "settled_at": 1751877012 }

status is one of queued, done, failed, or stale. stale means the engine hasn't answered — it may have failed after it already reached OnlyFans — so treat it as "check the next scan," not as "definitely didn't happen."

A pending write only stages if the gate is off

Until the write-ops gate is live for an account, these endpoints answer {"enabled": false, "staged": true} and write nothing at all — an honest no-op rather than a fake success.

What's next

Check what breaks before deleting a promo or bundle — deleting a promo is not the same as re-creating it later (the claim counter restarts at zero).

On this page