TEASEDocs
ProductsClubHow-to

Fix a fan's wrong source

Review and correct fans whose stored source came from the DM scan — assign the right one, or undo a prior override.

The Fan Source Review queue (/source-layout/fan-sources, "Needs review" / "Assigned manually" tabs in the panel) covers one specific population: fans whose current fan_source.kind is llm — i.e. their source came from the DM-scan, not from a click, a mapped link, or a campaign.

Hard sources can't be touched here

A fan attributed by cid, a mapped link, or a campaign is rejected with hard_source if you try to assign or undo through this queue. To change how a whole campaign resolves, use Confirm the source for a scanned campaign instead; there's no per-fan override for hard-attributed money.

Read the queue

curl "https://app.tease.link/api/admin/source-layout/fan-sources?mode=pending&limit=50" \
  -H "Authorization: Bearer $TEASE_API_KEY"

mode=pending (default) lists fans not yet manually resolved; mode=manual lists ones you've already manually assigned, for undoing. Each row carries a proposal computed against your registered sources for that fan's guessed platform:

proposal.statusMeaning
uniqueExactly one registered account matches the guessed platform — the obvious pick.
ambiguousMore than one registered account matches — you choose.
unregisteredNo registered account matches this platform at all.

Each row also carries a state.version — an opaque tag over the fan's current source, kind, provenance, and any prior override — required on every write as an optimistic-concurrency check.

Assign a source

curl -X POST "https://app.tease.link/api/admin/source-layout/fan-sources/assign" \
  -H "Authorization: Bearer $TEASE_API_KEY" -H "Content-Type: application/json" \
  -d '{"items": [{
        "ref": {"page_owner_id": "owner_123", "fan_id": "fan_456"},
        "source_key": "ig-main.link",
        "expected_version": "a1b2c3d4e5f6a1b2c3d4"
      }]}'

Send expected_version from the row you just read. Each item in the response comes back with a status:

statusMeaning
assignedWritten.
unchangedAlready exactly this source/provenance — a no-op, not an error.
conflictsource_changed — the fan's version moved since you read it; re-fetch.
rejectedhard_source, source_not_registered, or (for mode=pending selections) ambiguous_sources on an auto-only path.

Undo a manual assignment

curl -X POST "https://app.tease.link/api/admin/source-layout/fan-sources/undo" \
  -H "Authorization: Bearer $TEASE_API_KEY" -H "Content-Type: application/json" \
  -d '{"items": [{
        "ref": {"page_owner_id": "owner_123", "fan_id": "fan_456"},
        "override_id": 981,
        "expected_version": "9f8e7d6c5b4a3f2e1d0c"
      }]}'

override_id comes from the row's state.override_id. Undoing a fan whose override was already reversed returns unchanged/already_undone rather than an error — safe to retry.

Auto-assign the unambiguous ones in bulk

For the subset of the pending queue where exactly one candidate exists, you don't have to confirm each one by hand:

curl -X POST "https://app.tease.link/api/admin/source-layout/fan-sources/backfill-unique" \
  -H "Authorization: Bearer $TEASE_API_KEY" -H "Content-Type: application/json" \
  -d '{"apply": false}'

apply: false (the default) is a dry run — it returns the fans it would touch and their target source without writing anything. Set apply: true to actually assign them.

Money surfaces refresh automatically

A successful assign or undo invalidates the dashboard-money, /api/admin/stats, and source-leaderboard caches, plus the Links tab's read cache — you don't need to manually refresh anything else in the panel after fixing a fan.

What's next

On this page