This tab also includes custom admin-uploaded videos (the Upload template tab drops them here for review).
🔍
No pending videos. Run the scraper to find new dances.
✕ Failed: click Retry to regenerate
Generating…
⏸ Stalled: approved, but generation is not running (retry or discard)
✅ Ready: preview generated, waiting to publish
✅
Nothing waiting to publish right now.
🎉
No templates published yet.
Rejected creator templates
Rejected scraped videos
Moderated renders (hidden or deleted by admin)
🗑️
No rejected templates. Anything you reject, hide or delete lands here and can be restored.
Loading…
Loading…
Manage Gallery
0
Dances users submitted to the public gallery. Subscribers opt in; free-tier
dances are enrolled automatically and marked Free tier. Nothing is
public until it is added here.
or→
📺
No videos in the gallery yet.
Creators
Joined between→
Creator
Category
Source ↕
Templates ↕
# Posts ↕
Revenue ↕
# Referred Users ↕
Credits ↕
Status
Loading creators…
Where creators came from
Total creators
–
From a campaign
–
Organic
–
Tracked, no campaign tag
Mobile app
–
Install source not attributable
Unknown
–
Signed up before tracking
By source
Loading…
By medium
Loading…
By campaign
Loading…
Recent signups
Creator
Joined
Source
Medium
Campaign
Attribution
Loading…
Users
Joined between→
View
User ↕
Login / Platform ↕
Source ↕
Email ↕
Phone / TikTok ↕
Location ↕
Joined ↕
Last active ↕
Generations ↕
Plan ↕
Credits ↕
Status ↕
Device
IP ↕
Loading users…
Generations
→
Why
Generation analytics
→
Outcome
Audience
Platform
Type
Audio
Paid vs free
Watermark
Country
Length
Provider / model
Trend
Unaccounted Assets
The reconciliation state has not been read yet
This card fills in as soon as the server answers. If it never does,
the button below still works.
What this screen is. We ask Cloudinary to list every file it is storing for us, then ask our
own records to explain each one: a generation, a template, an upload log, a creator's avatar.
Whatever is left over: the files nothing we store mentions: is listed here.
Being listed does not mean a file is broken, unused, or bad. It means only that we cannot say
what it is for. Most of these are ordinary photos and videos whose paperwork went missing: the
mobile app uploads selfies straight from the phone to Cloudinary, so the server never saw
that request and no record of it could ever have existed. Some are from before we kept records at
all. Some belong to a different product that shares this Cloudinary account, and those are marked
and cannot be deleted from here.
Each row carries our best explanation, labelled Known cause, Likely cause or
Guess: a guess is only a guess, and is never worth chasing as if it were a finding. If you
are looking for one specific file, use Look it up below: a file that is healthy is
absent from this list, and absent looks exactly like missing.
Uploaded between→
Reconciling against Cloudinary…
Creator Categories
ℹ️ Changing a creator's category updates their revenue share immediately (not retroactive to previous months).
Category
Monthly Posts
Min New Paid Users
Min Referrals
Referral %
Referral Period
Payout per Generation
Creators
Loading categories…
Payout Rules & Queue
Global Payout Rules
Loading payout requests…
Pending Payouts
Total due: $0.00
Loading payouts…
Traffic Source
LIVE
→
-
attributed visitors
Platform
Find a video by its codeevery SnapDance video carries an invisible 8-character code; paste it to see whose video it is
Daily visitors
SEO & LLM
-
⏳ 0 waiting on a manual web deploy
Pushed to web/dashboard-setup, but not live until someone runs
git pull && ./scripts/deploy-web.sh. Each row disappears on its own once its
marker is found in the live page.
Verified backlinks
-
-
Brand mentions
-
-
Referring domains
-
unique registrable domains
Placements by channel
-
-
Google organic clicks
-
-
AI citation rate
-
-
Verified placements per batch, and AI citation rate
Search Console isn’t connected yet - set GSC_SERVICE_ACCOUNT to populate this panel. It never blocks a batch.
Clicks
-
Impressions
-
Avg position
-
Conversions
-
Top queries
Query
Clicks
Top landing pages
Page
Clicks
Placements by channel
No verified placements yet
Batches
No batches yet
Select a batch
Placement
Landing
Status
Sessions
Paid
No placements for this batch yet
×
-
Placement - · -
Live URL
-
Outbound link
-
Verification
-
Anchor text
-
Attribution
-
Weekly quota
Backlinks per week
Brand mentions per week
Blog posts per week
Syndication per post
Only verified placements count against the quota. If a channel cannot fill its share, the batch closes short rather than substituting low-quality targets.
Benchmark prompts
Event Planners
Planner
Company
Phone
Events
Status
Last seen
Loading planners…
Name
Email
Phone
Package
Total
Last step
Last updated
Logged in
Paid?
Loading…
Event Catalog
Loading catalog…
Event Settings & Content
Business settings
Loading settings…
Landing page & email content
Loading content…
Events Data Migration
ℹ️ The import only reads the legacy events JSON files and only adds what is missing in the database - it never deletes or modifies the JSON files, and re-running it is safe. A dry run touches nothing.
Last completed run
Loading…
Needs review
Loading…
User Access Management
Loading admin users…
Invite Admin
Page Permissions
Revoke Admin Access
System Settings
Feature Flags
Google OAuth Login
Apple OAuth Login
TikTok OAuth Login
Creator Signups
New User Registrations
Solo-only Creator Imports
Mobile Cloudinary Preview
Scraper (hashtag discovery)
AI assistant (support tickets)
Welcome Credits
Granted once to the first account created on a given device. Takes effect for
accounts created from the moment you save: existing accounts are never
re-granted or clawed back.
A dance costs 300 credits (Trending), 400 (Going Viral)
or 500 (Blow Up), so 600 credits = two Trending dances.
Welcome grants per network
How many new accounts may receive welcome credits from one internet address in 24 hours.
The device rule only stops the same device twice; a person with a phone and a laptop,
or one who reinstalls the app, has a new device id each time. This is the only limit
that sees across them.
It is a rate limit, not a block: the account is still created, the user
is told nothing, and the same device on another network still gets its grant.
Home and office networks (127.*, 10.*, 192.168.*) are exempt, not pooled.
Keep it generous. A household, a campus or anything behind carrier-grade NAT shares one
address. 0 turns the limit off. Only applies while
Welcome abuse guards is on.
0 = never forget, so the number above becomes a lifetime cap for that address
rather than a daily one. Worth knowing before you set it: an address is not a person.
Home addresses get reassigned and mobile carriers rotate them constantly, so a lifetime cap
will eventually refuse a genuinely new customer who inherited an address someone else used up,
silently and with no way for them to ask. Watch for it under Welcome abuse.
Blocked networks
Addresses an admin has closed the free path for. A blocked address gets
no welcome credits, no free generation (including credits granted before the block) and
no promo redemptions. Paid generation still works. A subscription, a top-up balance or a card
payment renders as normal. This is a refusal to keep giving things away, not a ban:
one address is often a whole household, and a paying customer can be sitting behind it.
Block from the IP column on Users, or from a generation card. The reason
you type is kept for you and other admins and is never shown to the user.
Loading...
No-watermark prices (free credits)
A free-credit user can buy the watermark off per generation. Premium spends are clean at the normal price. 0 falls back to the default.
OFF (default): every new signup gets the welcome credit, even a second account on the same phone. Leave it off while testing or if real users are not receiving credits. ON: one grant per device and a per-IP daily budget apply, which silently refuses repeat signups.
Referral Signup Bonus
Double-sided bonus, on top of the welcome credits above. When a new signup
is attributed to a referrer (via their invite link), BOTH the new creator
and the referrer get this many extra credits, once per signup. Set to 0
to disable; the base welcome grant is unaffected either way.
Dance Promotion
A creator spends part of their earned cash balance (royalties plus affiliate,
never credits) to have one dance placed higher on the Dances grid. Requests are reviewed
at Promotions; nothing is charged until an admin approves. Slots caps how many promoted dances hold a placement at once. The grid layout
defines 4 physical slots, spread so no more than two of any twelve cards are paid placement
and the first card is never one, so raising this above 4 changes nothing. Set to 0 to close
promotion entirely without deleting any history.
Subscriber Referral Reward
When someone who joined through a creator's Invite users link
completes a subscription, that creator is paid this many free dances,
once per invited person, ever. A renewal, a re-sync or a repeated
webhook never pays again.
Set in dances, not credits, so the reward keeps tracking the real
Trending price (currently 300 credits per dance) if that price ever moves.
Set to 0 to switch the reward off; subscriptions are unaffected.
Store Review Reward
Credits granted when an admin CONFIRMS a creator's store-review claim (Rewards screen).
Priced independently of the TikTok-post reward. Set to 0 to disable new grants: a claim can
still be filed and denied, it simply cannot be confirmed until this is raised above 0 again.
Web
Attribution is delivered directly by the site's pixels (TikTok, Meta, Reddit),
and ttclid + UTM are captured at signup. There is no switchable
delivery path on web yet: this column becomes editable when one exists.
Android
Conversion delivery path for the Android app. Applies OTA within ~5 minutes -
no build needed. Play Install Referrer capture is always on.
iOS
Same switch for the iOS app. User-level install attribution on iOS needs an
MMP: until Singular ships in a binary, ad networks only get SKAN aggregates.
⚠️ Do not switch to Singular before its SDK is live in the shipped app.
In Singular mode the app mutes its direct TikTok/Meta sinks, so flipping early
silences mobile conversions entirely. Firebase/GA4 is never muted. "Off" is an
emergency kill-switch only.
Who reports app-store purchases to Singular
A first subscription or credit top-up bought inside the app is reported to
Singular by the phone while this
is off. If that phone is offline at the moment of purchase, gets killed, or is
uninstalled straight after, the money still exists in RevenueCat, in Apple's
books and in ours, and never appears in Singular. App-store purchases are our
largest revenue line. Turn this on and the RevenueCat webhook reports them
instead, and the app stops sending its copy. Renewals, web billing and refunds
are already the server's and are not affected. Firebase and GA4 are not
affected at all.
⚠️ Order matters, and only in one direction.
The app learns to stay quiet from this same flag, but only a build that carries
that code can read it: an older install ignores the flag and keeps reporting.
So turn this on only after a
release carrying the change has shipped and users have picked it up. Flipping it
on early double-counts every mobile subscription. Turning it back off is always
safe and takes effect within about 5 minutes.
Photo moderation (NSFW gate)
Strictness of the selfie check that runs before every render (web + app).
Applies immediately: no deploy. The generation provider still refuses
genuinely explicit content downstream, so "Off" is safe as a
business-continuity switch.
Why was a photo flagged?
TikTok import floor
Minimum likes a creator's TikTok video needs to be importable as a template
(web + app; the server enforces it too). 0 = no floor. The global bypass waives
the floor for everyone while on;
per-creator bypasses (Users → Investigate → Import floor) stay independent.
Per-creator bypasses active: -
TikTok post reward
Minimum TikTok follower count a creator needs for a post of a generated dance
to file a reward claim (the Rewards screen). Below the floor the post still
works, it just earns nothing. Claims always need an admin confirm before any
credits move; the reward equals the minimum generation cost (currently 300).
0 = every TikTok-connected creator qualifies.
Template test renders
A template used to be judged from a still frame and its source video, which says
nothing about what a dance made from it looks like. The Template Queue now has a
Test renders button that runs the real pipeline and shows the result
before you publish.
One reference photo is used for every test, so two templates stay comparable. Each
test is a full render and costs the same as a live generation, which is why the count
is capped. Removing a test frees its slot.
A Cloudinary URL. Copy one from any generation that rendered well.
or upload a photo
Total across the whole product, for Template Queue → Long generation.
3 buys exactly one 15s, one 20s and one 30s run, which is 7 real
provider renders. 0 stops new runs without hiding what earlier ones found.
Generation access
Who may generate a video, and where free generation is open. Both
refusals happen before any moderation call, generation token or
credit spend, so a refused user is never charged for one.
Off restores the old guest allowance (2 renders per IP per 24h).
Two-letter country codes, comma separated. Leave empty to open free
generation everywhere. Subscribers and anyone paying are never affected:
only the free path closes, and the refusal never names the country.
Creator payout threshold
Minimum cash balance before a creator payout is processed (web + app quote it
as "Payouts available from $X"). 0 = no minimum. Transfer fees are deducted
from the payout itself: this only gates when one can happen.
Generation capacity & queue
How many dances may generate at the same time. This used to be
a server environment variable that only a deploy could change.
Everything past the limit now waits in line and starts
automatically as earlier dances finish. Nobody is ever turned away for
arriving while we are busy, and nothing is charged twice for waiting.
This is our limit, not the AI model's. Replicate's own
ceiling is per account, so setting this higher than the accounts can take
does not add throughput: the extra renders come back and wait anyway. To
genuinely raise it, add a Replicate account first, then raise this.
The queue ceiling is how many may be waiting. Past it we say plainly
that we are at capacity and charge nothing. Setting it to 0 switches the
queue off and restores the old behaviour, where anything over the limit was
refused on the spot. The wait message is editable below, under
"Notification messages" (type "Dance waiting in the queue").
New dances drop push
How many newly published dances it takes before every user gets a "fresh
dances landed" push, deep-linked to the Dances grid. 0 turns the broadcast
off. Admin accounts are never included. At most one send per 20 hours: a
big import batch fires it once, the surplus stays counted, and the next
publish after the cooldown releases it. The wording is editable below,
under "Notification messages" (type "New dances drop").
Notification messages
Every message the server sends users: push banners and the in-app inbox.
Edit and Save; Reset (or save an empty field) returns to the built-in default.
Placeholders like {templateName} are filled in at send time where noted.
Each message is editable per language: click a language chip to open it.
A language with no translation falls back to English, and its chip says so.
Loading…
App error messages
Every error/alert message the mobile app shows users. Edit and Save; Reset
(or save an empty field) returns to the built-in default. Applies OTA within
~5 minutes: no app rebuild. This changes only the displayed text, never how
the app behaves.
Each message is editable per language: click a language chip to open it.
A language with no translation falls back to English, and its chip says so.
Loading…
Social Connections
Connect SnapDance's own accounts once and the Share button on Generations can post
directly to them. Credentials are encrypted at rest and never shown again after
saving; only "which fields are set" is displayed. A platform activates after its
Test passes. Every platform also has account-side prerequisites (paid API tier,
Page, Business account, app review) listed on its card - pasting a key alone is
not always enough.
Loading connections...
Find subscriptions we missed
We learn about a subscription only when RevenueCat's webhook fires or when the
app checks in. Someone who subscribed on the web and never opened the app, or
who paid before the webhook existed, stays invisible. This asks RevenueCat about
each of our creators instead of waiting to be told.
Purchases with no account
Bought from onboarding before the buyer had an account, so the payment reached us
belonging to nobody. Apple and Google never say who paid, so these close only when
the buyer signs in on the device they bought on. Their subscription follows them
automatically when they do.
API Key Health
Loading…
Announce a Program
Broadcast a push notification to every reachable device: signed-in users and guests who
rendered without an account. Sends in batches of ~100 with a short pause between batches.
Run a dry run first to see how many devices this will reach.
Ask for a Review
Push + inbox message asking existing users for a 5-star rating, with the credit reward
explained in-app once they tap through. Sent at most ONCE per account, ever, no matter how
many times this is run. Copy is editable at System Settings, App & Web Configs,
Notification messages (type "Ask for a review (campaign)"); the suggested review text shown
to the user is the "Store review: suggested text" entry there.
Push Delivery
One row per device token per send: every path (dance-ready, failure notices, self tests,
admin sends, broadcasts). "Delivered to FCM" means Google accepted the message; the OS can
still hold it back on the device. A failed row names the FCM error, and dead tokens
(uninstalled devices, expired web subscriptions, legacy iOS rows) are pruned automatically.
Generations Today
-
all providers
Failures
-
- fail rate
Avg Duration
-
p95: -
🔍
All Issues
-
Loading…
Select an issue to inspect
Loading audit log…
Financial Overview
Loading…
Gross Revenue
-
total collected
Total Costs
-
all categories
Net Revenue
-
gross − costs
Margin
-
net ÷ gross
Loading…
Affiliate Program
Loading…
New Affiliate
Promo Codes
A code can be redeemed once per account. Editing a code's credit value only affects
redemptions made after the change: past redemptions keep the amount they were granted.
Codes are paused rather than deleted so their usage history is never lost.
Loading…
Messages
One thread per user. Sends and replies also push to the user's devices and land in the in-app Inbox.
Broadcast campaigns (Announce / push-all) appear in every user's Inbox automatically and are not listed here.
?
-
Select a thread, or use "Message" on a user in Users → Investigate.
What you wrote
Translation (this is what is sent)
Support
Every ticket was opened from /help or the app. The assistant answers first;
only the user decides whether that landed, and a ticket they escalate sits at the top of this list.
Replying here also lands in the user's in-app Inbox when their ticket is tied to an account.
?
-
Pick a ticket on the left.
What you wrote
AI draft (this is what is sent)
Deletion Requests
Every request here was filed from /account-deletion, /content-deletion or the app.
Nothing happens automatically. A request cannot be executed while it is still inside its cancellation
window, and the requester can withdraw it up to that moment.
Free accounts keep their published gallery dances by default after the account goes;
for a paying account the choice is yours. Every switch below is independent, and each starts on the
rule so the ordinary case is one click.
File a request from the mailbox
For requests that came in by email before this queue existed. Nothing was stored for those,
so they can only be entered by hand. Use the date on the email: the cancellation window runs
from when they actually asked, not from today.
Loading…
Name Changes
Every row is a creator asking to rename a dance that is already live. The dance keeps the name
it has until you approve the change. A refusal needs a reason: the creator is shown it, so
"no" on its own is what sends them to support instead of back with a better name.
Loading…
Promotions
A creator asks to spend part of their earned cash balance (royalties plus affiliate,
never credits) to have one dance placed higher on the Dances grid.
Nothing is charged until you approve. Approving debits the amount once and starts the run
window; declining an active promotion refunds the full amount that was charged, and can only do so once.
Declining a pending request costs the creator nothing, because nothing was taken.
Give a reason when you decline: the creator reads it word for word on their dashboard.
Placement is capped at 4 dances at a time and they never occupy the first card
of the grid, so the newest-first ordering still means something.
Amounts, slots and run length live at System Settings, Credits.
Loading…
Rewards
Confirming a claim grants credits to the creator's earned bucket and is audit-logged. The two
reward types are priced independently, editable at System Settings, Credits tab:
TikTok post = 300 credits, store review =
300 credits (0 disables new store-review grants without
breaking the claim/deny workflow). A confirmed claim can never grant twice.
For store reviews, check the store listing first: reviews cannot be verified by API. One
store-review claim per account, ever, regardless of which store or device it was filed from.
Every TikTok post files a claim automatically; the follower floor is a SIGNAL (below-floor claims are badged) and the decision is the admin's. Floor
set in System Settings, App & Web Configs. The inbox flow posts through the creator's own
TikTok token, so the post always lands on the account they authed with.
Loading…
Pending Confirmations
TikTok posts and review claims waiting for a human check. Open the verification link
before confirming a claim in the queue.
Loading…
Referral Chains
Referrer, then everyone who joined through their link, with template and
first-qualifying-generation progress per invitee.
Loading…
Award History
Loading…
What users shared on social media
Every dance a user posted to their own TikTok account, and what happened next.
We post as a DRAFT to the user's TikTok inbox (the app holds the upload permission,
not the publish one), so a post only becomes public when the user finishes posting it
themselves. Engagement is counted 7 days after the post and only for videos TikTok
gave us a public id for. Where that is blank the reason is shown; a blank is never
the same thing as zero.
Loading...
Data & Safety
Store health, volume space, off-volume backups, whole-deployment export/import and
creator recovery. Anything that can change or delete data runs as a
DRY RUN first and then needs a typed confirmation that
spells out what changes: no single click on this screen destroys anything.
Store health
Loading…
A store marked lost is a one-way trap: writes are refused to protect what is
still on disk, so no .bak is ever written and every backup taken after
the damage records the file as unreadable. It cannot recover on its own: repair it
here.
Loading…
Repair store
Railway volume usage
Loading…
Loading…
Reclaim space
Deletes pipeline scratch, staged media whose durable copy is in Cloudinary, scraped
downloads no store references any more, decided scrapes (downloads already approved
or rejected, with durable copies on Cloudinary), and expired import artifacts.
Lowering the import artifact age shortens the window in which an import can
still be rolled back.
Off-volume backups
Loading…
Loading…
Export everything
A gzipped tar of every store, restorable here or on another deployment.
This restores state, not assets
Durable media lives in Cloudinary and is referenced by URL from inside the
stores. Selfies, scraped video and audio on the volume are scratch and are not in
the archive: it does not survive losing the Cloudinary account.
Loading preview…
Import an archive
Import replaces whole files: it never merges
Each store in scope is replaced in its entirety. A real apply also
signs everyone out (sessions are not in a default export). The file it
replaces is kept as a .preimport- snapshot for 7 days, which is
the whole rollback window.
Step 1
Validate: writes nothing
Step 2
Dry run the apply
Step 3
Apply for real
Uploading…
Import history
Loading…
Loading…
Creator integrity
Loading…
Cross-references every store that names a creator id against creators.json. An id
other stores still reference but the creator store no longer has is proof of loss,
not a fresh install.
Loading…
Rebuild lost creators
Reads RevenueCat, Stripe and Firebase: this one is not automatic.
Rebuilds missing creators under their original ids so the stores referencing
them line up again. It never overwrites a creator that exists. A row rebuilt with no
email cannot reconnect anyone: sign-in matches on email: so Undo exists to
drop reconstructed rows that carry nothing.
Click “Build plan”: it calls external APIs and can take a minute.
The only real cap on a leaked code: signups are free, so one-per-account alone doesn't bound it.
Upload to Gallery
For a result video that belongs to no creator. It is published to the
public gallery immediately: the upload is the review. A template is
optional.
A video file (max 200 MB): or paste a Cloudinary URL below instead.
Leave as “Unknown / none” if the source dance isn’t known.
Bulk Upload to Gallery
Pick a parent folder whose subfolders each hold one selfie
(selfie_*) and one or more result videos
(*.mp4). Every video is uploaded as a separate entry that
lands pending in Manage Gallery for review: nothing
goes public until you add it here.
Each video max 200 MB. Non-video / non-image files are skipped.
Redemptions
Loading…
Add Revenue Entry
Add AI Cost Entry
Billing & Activity
From
To
Kling $/gen
Music $/gen
Showing: All time
Loading billing data…
Group by:
ℹ️ Creator payout = per-run $ × template runs in app. Live run counts require app backend integration.
Start Scrape
Run the scraper with today's hashtags + keywords
Scraper enabled
Videos · Min likes (default 10K)
Add Manual URL
Paste a TikTok, Instagram or YouTube dance URL to add it directly
Manage Hashtags
Add hashtags for future scraping runs
Creator Payout Settings
Set how much creators earn per template run
Payout Details
Total Earned
Paid Out
Pending
PAYOUT HISTORY
Creator Profile
Templates
Revenue
Referrals
Spendable Credits
-
Posts
Status
Category
Videos
Templates
Change Category
Select a category for this creator. This affects their earnings rate and visibility.
🎁 Gift Credits
Add gives extra on top. Set makes the balance exactly this, so 0 zeroes it.
Dance details
📢 Push to All Users
Sends a notification to every user who has the mobile app with notifications enabled.
?
-
Login source-
Platforms-
Plan-
Email-
Subscription
Credits-
Phone-
TikTok-
Location-
Source-
Import floor-
Comp sub-
Joined-
Payout-
Devices-
Inbox
Device ids-
📲 Send push notification
Each render shows status · platform · audio (failed included)
New Planner
They must have signed in to snapdance.app at least once.