The Only API docs

Subscribers & fans

Live vs cached reads, new-subscriber detection, tags and notes.

Two related resources. Subscribers are per-account subscription records. Fans are the panel-wide CRM view of people, with tags, notes and spend.

Subscribers

# Cached — zero platform requests, reliable total
curl "$BASE/api/crm/$CRM/accounts/$OFUID/subscribers/cached?limit=100&offset=0" \
  -H "X-API-Key: $KEY"

# Live — hits the platform
curl "$BASE/api/crm/$CRM/accounts/$OFUID/subscribers" -H "X-API-Key: $KEY"

Default to cached. See Cached reads.

Newly seen subscribers

curl "$BASE/api/crm/$CRM/accounts/$OFUID/subscribers/new" -H "X-API-Key: $KEY"

Every incoming subscription — new subs and renewals, because a renewal moves the fan's subscribed_at forward — newest first, from cache at zero platform cost.

There is no sync cursor. Without since/until you get the newest 50 of the entire cache. Accepts since, until (inclusive ISO bounds on subscribed_at), limit (1–500, default 50), offset, and type (all|active|expired).

For a genuine "what is new since I last looked" cursor, use GET /events?types=new_subscriber&since=… instead.

Statistics

curl "$BASE/api/crm/$CRM/accounts/$OFUID/subscribers/stats?granularity=day" \
  -H "X-API-Key: $KEY"

granularity is hour, day, week or month. Aggregated server-side, so this is one cheap request rather than bucketing thousands of rows yourself.

Keeping it fresh

curl -X POST "$BASE/api/crm/$CRM/accounts/$OFUID/subscribers/refresh" -H "X-API-Key: $KEY"
# 202 → poll .../subscribers/refresh/status

A delta sync, so it is cheap enough to schedule. See Async jobs.

Expiry events carry counts; the cache carries names

The expired_subscriber event has no fan identity — only previous_total, new_total and a negative delta, sampled every 10th poll.

But you do not need to diff snapshots to recover the who: GET /subscribers/cached?type=expired returns them directly with each row's expired_at, because is_active is recomputed from expired_at on every read rather than frozen at sync time.

Fans

Panel-wide, across every connected account:

curl "$BASE/api/crm/$CRM/fans?limit=100&offset=0" -H "X-API-Key: $KEY"

Each fan carries identity, tags, notes and total_spend. That figure is MAX(platform lifetime total, sum of captured tip/purchase events)not the chargeback-corrected signed sum. Check spend_known: when it is 0, spend is unknown rather than zero. See Earnings & transactions.

Tags

# Add — of_user_id is REQUIRED in the body
curl -X POST "$BASE/api/crm/$CRM/fans/$FANID/tags" \
  -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d '{"tag": "vip", "of_user_id": "'$OFUID'"}'

# Remove — of_user_id in the body or the query string
curl -X DELETE "$BASE/api/crm/$CRM/fans/$FANID/tags/vip?of_user_id=$OFUID" \
  -H "X-API-Key: $KEY"

Tags are how you segment, and they can be applied automatically — a tag_fan automation on new_purchase with an amount condition builds a VIP list with no code.

Tags are not a mass-DM audience filter. To message a tagged segment, read the ids from GET /fans and pass them as audience.fan_ids — see Messaging.

Notes

curl -X PUT "$BASE/api/crm/$CRM/fans/$FANID/note" \
  -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d '{"note": "Prefers video content.", "of_user_id": "'$OFUID'"}'

One free-text note per fan, replaced on write; max 2000 characters, and an empty string clears it.

A fan is identified by account + fan, so `of_user_id` is required

The fans table is unique on (crm_id, of_user_id, fan_of_user_id), so the panel-wide path alone cannot identify a row. All three of these routes return 400 without of_user_id. Tags cap at 40 characters.

Refreshing one fan's profile

curl -X POST "$BASE/api/crm/$CRM/accounts/$OFUID/fans/$FANID/refresh-profile" \
  -H "X-API-Key: $KEY"

Pulls that fan's current profile from the platform. Targeted, so it costs one platform request rather than a full re-walk — good for a "refresh" button in a UI.

Per-fan transactions

curl "$BASE/api/crm/$CRM/accounts/$OFUID/fans/$FANID/transactions/cached" \
  -H "X-API-Key: $KEY"

A common workflow

Refresh, then read

Kick off subscribers/refresh and transactions/refresh, then read from the cached routes. Do not block on the refresh — the cache always answers.

Tag automatically

An automation on new_purchase with payload.amount >= 100 and a tag_fan action keeps a vip segment current without any code.

Segment and act

Read GET /fans, filter by tag and spend, then send a targeted mass DM — with dry_run: true first to check the reach.

On this page