The admin panel

A tour of the fifteen NFT admin screens — the moderation dashboard and its five queues, the content and trading tables, creator verification, the dispute board, and the onboarding checklist that is not in the navigation.

6 min readUpdated 3 August 2026admin, moderation, disputes, queues

The NFT admin area lives at /admin/nft and has fifteen screens grouped into four navigation sections. Thirteen are in the menu; two — Disputes and Onboarding — are reachable only by URL or by a link from another screen.

Section Screens
Dashboard (/admin/nft)
Content Categories, Collections, NFTs
Trading Marketplace, Listings, Offers, Auctions, Sales
Community Creators, Activity
System Analytics, Settings
Not in the menu Disputes (/admin/nft/dispute), Onboarding (/admin/nft/onboarding)

Every screen carries its own permission. Granting access.nft alone opens almost nothing — see Permissions.

The moderation dashboard

/admin/nft answers exactly one question: what needs a decision right now?

It deliberately does not show collection counts, sales volume or average price. Those are scoreboards; none of them changes what you do in the next ten minutes. Instead it publishes five queues, each backed by a real row state somebody wrote deliberately, each with a count, the age of the oldest item waiting, and — where money is frozen — the amount.

nft_collection.status = 'PENDING'. Nobody can mint into these. The door is the status toggle on the Collections screen.

The four non-terminal nft_dispute states, with the HIGH/CRITICAL and unassigned subsets called out separately. The dispute SLA is 24 hours — the tightest budget on the platform, tighter than a withdrawal, because escrow is held while a dispute runs and both sides are frozen out of their own money.

nft_listing.settlementBlockedAt is stamped when an auction ends with a winning bid and there is no on-chain auction contract holding the money. A winner exists, bids were placed, and nothing will move until a human acts. Deploying an auction contract for the listing clears the flag.

nft_offer.flaggedAt on an ACCEPTED offer, written by the stale-offer sweep when a buyer's escrow could not be released. Their funds are locked and they hold no NFT. This is the queue to work first.

Three token states that cannot support what the rest of the product asks of them:

  • a listed token with no metadata URI or no image renders as a blank card with nothing to show a buyer;
  • a token marked minted with no blockchainTokenId is rejected outright by transfer, approval and listing — it looks minted and can never move;
  • a DRAFT older than 72 hours is a batch preparation that was never carried out.

The headline is a sum of defects, not of distinct tokens: one row can carry more than one, and the interface says so.

Every counter is a SQL aggregate, not a page summed in the browser. Only the two preview lists are capped, and the response declares that cap.

A timeRange of 24h, 7d or 30d controls the dispute inflow-versus- resolution series.

Content screens

Categories

/admin/nft/category — the only NFT table where an admin can create, edit and delete rows. Collection creation requires a valid category id, so with zero categories nobody can create anything. Create at least two.

Creates a category
Updates a category
Deletes a category

Collections

/admin/nft/collection — read-mostly. Create and delete are switched off in the interface because collections belong to their creators. What an admin does here is approve (the status toggle) and correct metadata.

The toggle takes a boolean: true → ACTIVE, false → SUSPENDED. There is no route back to PENDING.

NFTs

/admin/nft/token — every token in the marketplace. Read-only apart from a status toggle, which maps true → MINTED and falseBURNED.

There is no SUSPENDED state on nft_token. Switching a token off writes status: 'BURNED' — the same terminal state a real burn produces. Use it for genuinely unwanted content, not as a temporary hide.

Trading screens

Marketplace

/admin/nft/marketplace — contract deployment, configuration, emergency controls and fee withdrawal. This is the only screen that spends money, and it has its own page: Marketplace contracts.

Listings, Offers, Auctions, Sales

Four tables over nft_listing, nft_offer and nft_sale. All four are read-only in the interface — create, edit and delete are all switched off — with a single status action each:

Screen Table Status action
Listings nft_listing Toggle a listing on or off
Offers nft_offer Toggle an offer on or off
Auctions nft_listing filtered to type = AUCTION Toggle an auction on or off
Sales nft_sale None — sales are a record, not a workflow

The Auctions screen is the Listings model with a fixed filter, so a row appearing on both is the same row.

There is a second, richer listing-moderation endpoint that the tables do not call — it accepts a status of ACTIVE, CANCELLED or EXPIRED plus a moderation note and a flag reason:

Moderates a listing with a note and a flag

Community screens

Creators

/admin/nft/creator — creator profiles, created automatically the first time a user makes a collection or mints. Two controls:

Updates a creator profile and its verification status
Makes a creator profile public or disabled

Verification tiers are BRONZE, SILVER, GOLD and PLATINUM. Setting a tier sets isVerified to true automatically — you cannot have a tiered but unverified creator.

The onboarding checklist looks for at least one verified creator, so verifying somebody is part of a clean launch.

Activity

/admin/nft/activity — the full nft_activity feed: mints, transfers, sales, listings, delistings, bids, offers, burns, collection creation, collection deployment and auction endings. Rows can be deleted individually with delete.nft.

This is the audit trail for everything the marketplace did. Deleting from it loses history permanently; prefer filtering.

Disputes

/admin/nft/dispute — not in the navigation, and the only NFT screen that can move money on its own.

Disputes are created two ways: by the stale-offer sweep when escrow cannot be released, and by whatever front-end path your install exposes. Seven types are recognised — FAKE_NFT, COPYRIGHT_INFRINGEMENT, SCAM, NOT_RECEIVED, WRONG_ITEM, UNAUTHORIZED_SALE, OTHER — across six states: PENDING, INVESTIGATING, AWAITING_RESPONSE, RESOLVED, REJECTED, ESCALATED. Priority is a four-rung ordinal: LOW, MEDIUM, HIGH, CRITICAL.

Changes a dispute's status
Changes a dispute's priority
Assigns a dispute to an admin
Posts a message to the thread
Resolves a dispute

Resolving

Seven resolution types: REFUND, PARTIAL_REFUND, CANCEL_SALE, REMOVE_LISTING, BAN_USER, WARNING, NO_ACTION. Resolving stamps resolvedAt and resolvedById and posts a system message into the thread for the audit trail.

Both credit the buyer's SPOT wallet inside the same database transaction as the dispute update, under the idempotency key dispute_refund_<disputeId>. This is not a paper resolution — the money leaves your platform.

The buyer is resolved from the linked sale, falling back to the linked listing, falling back to the dispute's reporter. REFUND refunds the full original sale price unless you supply an amount; PARTIAL_REFUND requires one. If the buyer, the amount or the currency cannot be determined, the whole resolution is rejected rather than half-applied.

PARTIAL_REFUND is not a value the nft_dispute enum accepts, so it is stored as REFUND with the real type recorded in the row's metadata. Reporting off resolutionType alone will undercount partial refunds.

System screens

Analytics

/admin/nft/analytics — volume, revenue, category splits and trends. The totals are real aggregates. Two decorations are not: the fourteen-point sparkline trends are generated from the current total times a random factor, and the category chart falls back to a hardcoded Art / Gaming / Music / Sports quartet when the real group-by returns nothing. Read the totals; ignore the shapes.

Settings

/admin/nft/settings — five tabs (Trading, Fees, Verification, Content, Integrations) over seventeen keys. Every key is documented in Settings.

Onboarding

/admin/nft/onboarding — a four-phase launch checklist. It is not in the navigation; the Marketplace screen links to it. The phase and task list is a fixed list in the page; the completion state is read from real data by /api/nft/onboarding/status.

See Install for what each phase checks.

API-only capabilities

Five admin capabilities have working endpoints and no screen in this build. They are usable through the API with the permission named:

Lists NFT reviews
Approves, rejects or hides a review
Schedules automated blockchain-state backups
Lists backup schedules
Deletes a backup schedule

Backups run per chain (ETH, BSC, POLYGON), on an HOURLY, DAILY, WEEKLY or MONTHLY schedule, as FULL or INCREMENTAL, optionally including dispute data. The processNFTBackups cron polls every 15 minutes, which is finer than the tightest schedule, so a due backup fires close to its nextRun.

nft_review.status = 'PENDING' is a real, countable state with no screen behind it, which is why the moderation dashboard does not publish a count for it — a queue with no door is a poster.