Dashboard
Loading live status…
Needs your attention
from live readiness + queue signalsSystem health
Checking…Catalog backfill by category
set coverageRecent admin activity
How this works
Everything here is fetched when you sign in and when you press Refresh — the calls run in parallel, so the page is as slow as its slowest endpoint rather than the sum of them. Needs your attention joins three sources: readiness checks that are not configured, the pricing review queue, and failed scans. If one of those cannot be reached the card says so rather than counting it as zero — an empty attention list means every source answered and there is nothing to do. The figures in the strip are totals reported by their own endpoints, not counts of anything loaded on screen. There is deliberately no registered-user total: the users endpoint returns a page, not a count of the product.
Users
Loading…
All users
Press / then @address to search by email| User | Plan | Status | Items | Scans / mo | Devices | Last active |
|---|---|---|---|---|---|---|
| Loading users… | ||||||
How this works
Search by email from the topbar — press / to focus it, then type @ and the address. That query runs server-side against the users endpoint; there is no client-side filtering on this page. The Plan column is the subscription plan only and says nothing about console access — an account with a brand-coloured role pill beside its plan has admin access, and opening the user shows their real role. Free and Pro above count only the rows loaded so far. Support and destructive actions live on the user detail page, behind a typed confirmation.
Pricing review queue
Loading…
| Select | Item | Reason | Value | Confidence | Provider | Assignee |
|---|---|---|---|---|---|---|
| Loading… | ||||||
How this works
The chips above are the filter: each one re-queries the queue server-side, and the count beside the title is the real total for whichever reason is selected — not a count of the rows on screen. Open an item to see why it is here and to act on it. Retry runs a live reprice against the configured provider and asks you to type RETRY first. A manual override needs a value and a note, replaces the item's value immediately, and is written to the audit log with your email against it. Marking reviewed clears the item from the queue without changing its price.
Catalog
Loading…
PriceCharting set backfill
—KicksDB catalog
SneakersStockX-sourced sneaker data, refreshed on its own daily cron.
Last refreshed —
Portfolio ↔ catalog matching
Trigger onlyLinks owned items to catalog products at ≥90% confidence — matched items reprice free from the catalog. No live match-rate summary exists yet; running it here reports what it did.
Scan-derived promotion
Trigger onlyCorroborated live-scan prices get promoted into the catalog once repeat scans confirm them. No live count summary exists yet; running it here reports what it did.
Catalog products
metadata is editable — prices come from providers and can't be hand-edited| Product | Category | Set | Market value (USD) | Source | Updated | |
|---|---|---|---|---|---|---|
| Loading… | ||||||
Catalog image sources
Per-category kill switch for publisher-sourced card images shown in the app
Catalog image categories
toggling off hides that category's publisher-sourced images across PackLox instantly — no deploy neededScheduled jobs
Live status from the run ledger + data-freshness probes
| Job | Schedule | Status | Last run | Output freshness | Reported | |
|---|---|---|---|---|---|---|
| Loading… | ||||||
Recent errors
Unhandled exceptions from the API and cron scripts, grouped by route/job + error class. Click a row for occurrences and stack traces.
| Where | Error | Latest message | Last seen | Count |
|---|---|---|---|---|
| Loading… | ||||
Subscriptions
Server-owned entitlements — the client can never grant itself Pro
Recent entitlement changes
from audit log| User | Change | Source | When |
|---|---|---|---|
| Loading… | |||
Only changes since 2026-08-16 appear here — this table is built from the audit log, which only started recording entitlement changes then; it isn't a full history of every subscription ever granted.
Receipt validation
—Plan mix
— accountsPriceCharting terms: at AUD ~1,540/mo revenue (USD $1,000) a formal Commercial Agreement is required — flag proactively.
Team & access
Everyone with elevated access to PackLox admin
Invite a new team member
Sends a real invite email via Supabase — no password to set or share. They sign in with the same auth as the app; this just grants console access.
| Person | Role | Permissions | Last admin action | Added | |
|---|---|---|---|---|---|
| Loading… | |||||
Removing someone's access is the same role-change flow as any user, from their detail drawer — open "Manage" on a row above.
Provider costs & compliance
What every pricing/AI provider costs, and where you stand on their terms
What this page is, and is not
Documentation, not telemetry. Every figure below is a written-down estimate from the provider agreements — there is no spend or rate-limit tracking behind it. The build it is waiting on: per-provider call counters (reusing the existing Postgres-backed throttle tables), a monthly cost rollup, and an alert when MRR crosses 80% of the commercial-agreement threshold.
Attribution & terms compliance
from the PriceCharting permission emailsProviders
spend is estimated — real tracking not yet built| Provider | Used for | Plan | Est. monthly cost | Rate limit | Status |
|---|---|---|---|---|---|
| PriceCharting | Catalog, sports/comics/coins backfill, direct pricing | Legendary | ~AUD 76 | 1/sec API · 1/10min CSV | Live |
| KicksDB | Sneaker pricing + catalog | Pay-per-use | ~AUD 12 | provider-set | Live |
| Gemini | Scan recognition (primary) | Pay-per-token | ~AUD 8 | provider-set | Live |
| OpenAI | Scan recognition (fallback) | Pay-per-token | <AUD 1 | provider-set | Live |
| eBay | Sold-comp pricing, metadata | Browse API (free) | $0 | provider-set | Insights pending partner access |
| TCGplayer | Card pricing (not yet used) | — | $0 | — | Not configured |
Support inbox
Direct user reports — separate from the automated scan/pricing queues
| Report | User | Category | When | ||
|---|---|---|---|---|---|
| Loading… | |||||
Replying pushes a real notification to the user's device and emails them via Resend, and they can reply back in the app — a real threaded conversation, not a one-way email. Images and CSV files can be attached in either direction (10 MB / 5 files per message).
Data requests
Privacy compliance — export and deletion, tracked to completion
| Request | User | Status | Requested | |
|---|---|---|---|---|
| Loading… | ||||
Export bundles every table scoped to the user into a JSON download (portfolio, wishlist, alerts, devices, profile, subscription, valuation history) — actual portfolio image files aren't included yet, since this backend has no visibility into the mobile app's own image storage convention; item rows still carry whatever image URLs the app already stored. Deletion hard-deletes those same tables plus the Supabase auth account itself — admin_audit_events is deliberately kept, since it's the platform's own compliance record, not the user's data. Every live action defaults to dry run; a live export asks you to confirm, a live deletion asks you to type DELETE.
Portfolio items
Loading…
All items
| Item | Owner | Category | Value | Confidence | Status | Source | |
|---|---|---|---|---|---|---|---|
| Loading… | |||||||
Editing an item's category, condition or admin note stamps it and lands in the audit log. Price and valuation status are provider- or scan-derived and not editable here — use the Pricing review queue's override for that.
Item
Loading…
Product
Loading…
Failed scans
Loading…
| Scan | Reason | Provider | Confidence | User | When | ||
|---|---|---|---|---|---|---|---|
| Loading… | |||||||
Resolve asks for a category (provider error, image quality, unpriced, duplicate/invalid) and an optional note. Honesty note: "Retry" currently flags the scan as retry-requested — the backend does not yet re-run the analysis. This page says so instead of pretending.
Push & alerts
Price-alert pipeline: evaluate → push → notify-once → re-arm
Recent deliveries
from the delivery log| Notification | User | Status | When |
|---|---|---|---|
| Loading… | |||
Failing devices
—| Device | Failures | |
|---|---|---|
| Loading… | ||
Derived from recent failed deliveries in the log, not a live device registry. Disabling stops sends to that device only — the user's other devices keep working. The app re-registers a fresh token on next launch.
Broadcast
Max 1 live broadcast/dayFans out over push_device_registrations for the chosen segment via FCM, logs every delivery, and defaults to a dry run — a live send asks you to type SEND and is capped at one per day.
Send to a specific user
search by email or user idEvery send action defaults to dry run. A live send asks you to type SEND. Alerts notify once: after a successful push the rule waits until its condition clears, then re-arms.
Audit log
Loading…
| Event | Actor | Target | Status | When | |
|---|---|---|---|---|---|
| Loading… | |||||
Filter by action. Any user or item page can jump here pre-filtered to its own trail.
Reports
Loading…
Audit events over time
events per day, selected rangeExport data
CSV, current range appliedSettings
Environment, providers and deployment — read-only visibility, no secrets shown
Reference
Providers
configured = key present on the server, never displayedCurrency conversion
Static ratesPinned in config, not live market rates — a known hardening item. Catalog/provider prices convert through these before display.
Environment
SITSwitch environment
You're on SIT. Production isn't configured yet — the switch unlocks automatically once a production API URL exists.