Skip to content

SOT and Finance: suggested sub counts, model launch arms, and quarterly bonus downloads

HRIS now fetches a suggested subscriber count for each stream session and offers it to CT managers with an Apply button — nothing is paid on it until someone applies it. Every model's record says which setup she started streaming on. And the CT quarterly bonus tables download as CSV.

A suggested sub count, with Apply (CT managers)

An hour after a stream ends, HRIS asks OnlyFans how many new subscriptions landed during that session and shows the answer on SO-OF Model Sub Count.

The suggestion pays nothing on its own. A session with one waiting still reads Not recorded and keeps its amber ⚠ — it is a to-do, one click shorter than before. This is deliberate: the operators' pay should never move because nobody looked.

  • Apply records the suggested figure as the session's count, with your name on it.
  • Fetch from OnlyFans asks straight away instead of waiting for the hour, and is the retry after a failed fetch.
  • Type your own number any time. A fetch never overwrites a count you recorded.

Guide: SO-OF Model Sub Count

When there is no suggestion

The fetch says it could not produce a number rather than offering a zero. A suggestion of none would invite a click that pays the operators nothing and looks like a deliberate count.

Four reasons, each named on screen: the hour has not passed, the fetch failed, the model has no OnlyFans account, or the subscription price changed during the stream — in which case both figures are shown and you judge between them.

Guide: When there's no suggestion

Blank and 0 are now different

Previously a saved session recorded a zero whether you meant one or not. Now blank means nobody has counted — the session keeps its amber flag and pays nothing — and 0 means you counted and there were none. Saving a note alone no longer records a count.

Every sub count entered before this release is treated as hand-entered, so nothing a manager recorded can be replaced by a fetch.

Guide: Entering a session's numbers

A launch arm on every model (P&C, SOT)

Each model's record now says which setup she started streaming on: MTS (multistreaming with Stream Ops from her first stream) or Independent (on her own, no operator). It is required when you add a model, and there is no default.

Switching it later is recorded — who moved it, from what to what, and when — in a Launch arm history on her record.

"Not recorded" is not a third arm. Every model on the roster before this release reads that way, and there is no backfill. Counting those as independent would skew the comparison the field exists to make.

The arm does not affect scheduling. Setting a model to Independent does not stop anyone being assigned to her sessions.

Guide: Launch arm

Take the quarterly bonus figures away (Finance)

Both tables in the CT Quarterly Bonus review — per model and per member — now have a small CSV button in their caption bar. One click, one file each.

Each file opens with a line naming the quarter, its dates, the pool rate, and whether the figures are live or a prepared run's frozen ones. That last part matters once two of these are sitting on a desktop: they look identical otherwise, and only one of them is what an approval will pay.

  • The download ignores the model filter. Filtering to one model narrows the per-member table and its Σ on screen; the file still carries every model and every member for the quarter. The filter is for reading, the file is a record — a file holding one model's slice while named after the quarter is how a partial figure ends up treated as the whole one.

Guide: Taking the figures away

Public API (for integrations)

launch_arm is published on the model roster and on the model inside each stream session, so a consumer holding only the schedule can group sessions by arm. Values are MTS and IND; empty means no arm was ever recorded, which is not the same as either arm — every model predating the field reads empty, and counting those as independent will skew any comparison.

It is the model's current arm, not the one in force when a past session ran.

The API reference is inside HRIS at Profile menu → HRIS API. It needs an HRIS account — a partner who holds a key may not have one, so send them what their integration needs along with the key.

Technical changes

For developers and whoever deploys. None of this needs a guide.

Data model

  • agency_models.Model.launch_arm — MTS / IND, blank for rows predating it. New agency_models.LaunchArmChange (model, from_arm, to_arm, changed_by), append-only.
  • management.StreamSessionMetrics gains manual_sub_count and fetched_sub_count (both nullable), fetch_state, fetched_at, fetch_error, fetch_detail and effective_source. sub_count keeps its meaning as the figure commission reads, but is now derived by resolve_effective from the manual column only and never written directly.
  • EffectiveSource has no fetched value: a fetched figure is never effective.

Behaviour

  • POST /api/management/ct/stream-metrics/sessions/<id>/apply-fetched/ and .../fetch/ are new. The second is queued, returns 202, and is refused (400) for a session that has not ended or was cancelled.
  • The board's PATCH writes manual_sub_count only when sub_count is present in the payload — the partial upsert previously defaulted it to 0.
  • A per-session Celery task is armed at end_at + 1h on create, update and restore, and re-reads the window at run time so a rescheduled session is not counted over a stale one. Retries 2m→1h; a failure leaves fetch_state=failed and never a zero.
  • agency_models.services.aruna_of gains fetch_fans (stats/fans, returning the _rate_limits.remaining_minute headroom _unwrap_payload drops) and fetch_subscribe_price (users/me).
  • ModelCreateSerializer now requires launch_arm; any client creating models must send it.
  • The quarterly bonus export is entirely client-side — no endpoint, no new permission surface, nothing added to the finance/exports/ async machinery.

Deploy

  • agency_models/0040 — adds launch_arm and LaunchArmChange. Schema only, reversible.
  • agency_models/0041 — AlterField on launch_arm for its choice labels. Schema only, no data.
  • management/0040 — adds the sub-count columns and runs a data migration setting manual_sub_count = sub_count and effective_source = manual on every existing row. Reversible, but the reverse clears manual_sub_count. Commission has already paid on these figures, so this is what stops a fetch overwriting them.
  • management/0041 — alters effective_source choices. Schema only.
  • No env vars, no new workers. The fetch uses the existing Celery queue and the Aruna-OF credentials already configured.

Tests

  • The sub-count counting logic is covered by fixtures built from the written spec, not from recorded API responses. They verify classification, backwards paging, the fan dedupe and the three window verdicts — they do not prove the total matches a hand count. That parity check needs a real session and has not been done.
  • The quarterly bonus CSV has no automated coverage: a render-and-download path with no server behaviour to assert.