Token Status API
The Token Status API provides real-time information about Colored Key NFT existence and usage within the RitoSwap ecosystem. It powers UI states like “Available”, “Already used for access”, or “Token doesn’t exist” without forcing the frontend to hit the chain directly.
This endpoint is optimized for frequent polling from the dApp UI.
Use this page for a high-level understanding of how token status works. For full request/response schema and example payloads, see the OpenAPI Reference section below.
What this endpoint tracks
Each token has two distinct states:
-
On-chain existence
Whether the token has been minted and exists in the Key NFT smart contract. -
Off-chain usage
Whether the token has already been used to access gated content, along with who used it and when.
The API combines these into a single JSON response so the dApp can render token state with one call.
At a high level, the backend does:
- Check the database for an existing token record (including usage info).
- If missing, hit the chain once to verify existence.
- If it exists on-chain but not in the DB, create a record (unused).
- Return the combined state back to the client.
This keeps the UI snappy while avoiding unnecessary chain reads.
Endpoint summary
| Property | Value |
|---|---|
| URL | /api/token-status/[tokenId] |
| Method | GET |
| URL Parameters | tokenId – The NFT token ID to check (non-negative integer) |
| Authentication | None (public endpoint) |
| Typical Usage | Real-time UI checks when viewing tokens or attempting gated access |
Network-Aware Database Operations
This API leverages RitoSwap’s sophisticated network routing infrastructure to ensure all operations target the correct blockchain network and corresponding database table. The system automatically handles network detection and routing, allowing the API to work seamlessly across Ethereum mainnet, Sepolia testnet, and local RitoNet development networks.
Automatic Network Routing
The API uses two key utilities from the prismaNetworkUtils module to handle multichain operations:
Database Operations via getTokenModel()
This function returns a unified interface that automatically routes database queries to the correct network-specific table:
import { getTokenModel } from '@/app/lib/prisma/prismaNetworkUtils'
const tokenModel = getTokenModel()
// Automatically queries token_ethereum, token_sepolia, or token_ritonet
const token = await tokenModel.findUnique({
where: { tokenId }
})Blockchain Configuration via getChainConfig()
This function provides the correct RPC endpoints and chain metadata for the active network:
import { getChainConfig } from '@/app/lib/prisma/prismaNetworkUtils'
import { createPublicClient, http } from 'viem'
const chainConfig = getChainConfig()
const publicClient = createPublicClient({
chain: chainConfig.chain,
transport: http(chainConfig.transport)
})Network Detection
The active network is defined by the single NEXT_PUBLIC_ACTIVE_CHAIN environment variable (ethereum, sepolia, or ritonet). For local development, additional metadata (NEXT_PUBLIC_LOCAL_CHAIN_ID, RPC/WSS URLs, etc.) must also be provided so the helpers can construct a viable viem client.
Cross-Network Isolation
Each blockchain network maintains its own isolated database table with identical structure:
- Ethereum Mainnet →
token_ethereumtable - Sepolia Testnet →
token_sepoliatable - RitoNet Local →
token_ritonettable
This isolation prevents cross-network data conflicts while enabling network-specific optimizations and simplified debugging. The same token ID can exist on different networks representing entirely different assets, and this architecture ensures they never interfere with each other.
The network routing happens transparently to the API logic. Developers write network-agnostic code while the infrastructure handles proper routing behind the scenes. For detailed information about the network utilities, see the Network-Aware Database Utils documentation.
Response shape (high-level)
All valid requests (with a valid numeric tokenId) return HTTP 200, even if the token does not exist. The presence and state of the token are communicated via the JSON fields:
interface TokenStatusResponse {
count: number; // 1 if the token exists, 0 if it does not
exists: boolean; // true if the token exists on-chain
used: boolean; // true if it has been used for gating
usedBy: string | null; // address that used it (if used)
usedAt: string | null; // ISO timestamp of usage (if used)
}Typical results:
-
Token exists and unused
exists: true,used: false,usedBy: null,usedAt: null -
Token exists and used
exists: true,used: true,usedByandusedAtpopulated -
Token does not exist
exists: false,used: false,count: 0
Use exists and used as your primary flags in the UI.
Minimal request example
A simple fetch from the dApp:
// Check status of token #42
async function getTokenStatus(tokenId: number) {
const res = await fetch(`/api/token-status/${tokenId}`);
if (!res.ok) {
// See “Error behavior” below for details
throw new Error(`Failed to check token status: ${res.status}`);
}
const data = (await res.json()) as {
count: number;
exists: boolean;
used: boolean;
usedBy: string | null;
usedAt: string | null;
};
return data;
}Example usage in a React component:
function TokenStatusBadge({ tokenId }: { tokenId: number }) {
const [status, setStatus] = React.useState<null | {
exists: boolean;
used: boolean;
}>(null);
React.useEffect(() => {
getTokenStatus(tokenId)
.then((data) => setStatus({ exists: data.exists, used: data.used }))
.catch(() => setStatus(null));
}, [tokenId]);
if (!status) return <span>Checking…</span>;
if (!status.exists) return <span>Does not exist</span>;
if (status.used) return <span>Used</span>;
return <span>Available</span>;
}Error behavior & rate limiting
This endpoint distinguishes between:
-
Validation errors (400)
Returned whentokenIdis missing, negative, or not a valid number. -
Rate limiting (429)
Returned when a client exceeds the allowed request rate.
The response includes headers like:-
X-RateLimit-Limit -
X-RateLimit-Remaining -
Retry-After
Use
Retry-Afterto back off polling intervals. -
-
Internal errors (500)
Returned when something unexpected happens while checking status (e.g. DB connectivity issues, RPC failures, etc.).
For exact JSON shapes and full header definitions, rely on the OpenAPI reference below.
Polling guidelines
The endpoint is designed to handle real-time UI polling, but you should still be polite:
- Poll more frequently while a token is in a “pending” or “unknown” state.
- Slow down or stop polling once:
- the token is marked as
used, or - the user navigates away / the tab is hidden.
- the token is marked as
A simple pattern is:
- 3–5 second interval while waiting for on-chain actions to settle.
- Back off if you receive
429and respect theRetry-Afterheader. - Stop polling once
used === trueif that’s a terminal state for your flow.
OpenAPI Reference
For full request/response schema, enums, example payloads, and all status codes, use the generated OpenAPI docs filtered to this tag: