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. Newagency_models.LaunchArmChange(model, from_arm, to_arm, changed_by), append-only.management.StreamSessionMetricsgainsmanual_sub_countandfetched_sub_count(both nullable),fetch_state,fetched_at,fetch_error,fetch_detailandeffective_source.sub_countkeeps its meaning as the figure commission reads, but is now derived byresolve_effectivefrom the manual column only and never written directly.EffectiveSourcehas nofetchedvalue: 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, returns202, and is refused (400) for a session that has not ended or was cancelled.- The board's
PATCHwritesmanual_sub_countonly whensub_countis present in the payload — the partial upsert previously defaulted it to 0. - A per-session Celery task is armed at
end_at + 1hon 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 leavesfetch_state=failedand never a zero. agency_models.services.aruna_ofgainsfetch_fans(stats/fans, returning the_rate_limits.remaining_minuteheadroom_unwrap_payloaddrops) andfetch_subscribe_price(users/me).ModelCreateSerializernow requireslaunch_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— addslaunch_armandLaunchArmChange. Schema only, reversible.agency_models/0041—AlterFieldonlaunch_armfor its choice labels. Schema only, no data.management/0040— adds the sub-count columns and runs a data migration settingmanual_sub_count = sub_countandeffective_source = manualon every existing row. Reversible, but the reverse clearsmanual_sub_count. Commission has already paid on these figures, so this is what stops a fetch overwriting them.management/0041— alterseffective_sourcechoices. 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.