Monitor NFT API Reference
API endpoints used by the Monitor app's NFT page (apps/monitor/src/app/nft/). All routes are Next.js API routes under /api/nft/ that either proxy to emprops-api or call on-chain contracts directly via viem.
1. Factory Deployment
GET /api/nft/deploy
Tab: Factory
Fetch saved factory deployment details for a chain.
| Param | Type | Default | Description |
|---|---|---|---|
chainId | number | 31337 | Blockchain network ID |
Response:
{
"success": true,
"deployment": {
"chainId": 31337,
"deployer": "0x...",
"timestamp": "2026-02-11T...",
"contracts": {
"DiamondCutFacet": "0x...",
"DiamondLoupeFacet": "0x...",
"OwnershipFacet": "0x...",
"ERC721Facet": "0x...",
"MintFacet": "0x...",
"ContractURIFacet": "0x...",
"CollectionInit": "0x...",
"DiamondFactory": "0x..."
}
}
}Checks bytecode of saved DiamondFactory address — returns { stale: true, error: "..." } if contract no longer exists (e.g. Hardhat restart).
POST /api/nft/deploy
Tab: Factory
Deploy the entire Diamond factory + all facets (7 contracts).
Request Body:
{ "chainId": 31337 }Response: Same shape as GET — returns all deployed contract addresses.
On-chain: Deploys 6 facets + CollectionInit + DiamondFactory with all addresses as constructor args.
GET /api/nft/factory?chainId=X
Tab: Factory
Read factory contract state from chain.
| Param | Type | Default | Description |
|---|---|---|---|
chainId | number | 31337 | Blockchain network |
factoryAddress | string | from deployment | Override address |
Response:
{
"success": true,
"factory": {
"address": "0x...",
"collectionCount": "5",
"owner": "0x...",
"facets": {
"diamondCutFacet": "0x...",
"diamondLoupeFacet": "0x...",
"ownershipFacet": "0x...",
"erc721Facet": "0x...",
"mintFacet": "0x...",
"collectionInit": "0x..."
}
}
}Returns { stale: true } if bytecode is empty.
2. AI Collection Generation
POST /api/nft/collections/generate
Tab: Collections
Generate a collection config from a concept using Gemini AI. Optionally creates the collection in one call.
Proxies to: POST emprops-api/nft/collections/generate
Request Body:
{
"concept": "Cyberpunk cats in a neon city",
"save": true,
"blockchain": "ARBITRUM",
"project_id": "uuid (optional)",
"social_org": "farcaster (optional)",
"social_identifier": "12345 (optional)"
}| Field | Type | Required | Description |
|---|---|---|---|
concept | string | Yes | Text description for Gemini to generate a collection from (min 3 chars) |
save | boolean | No | When true, also creates the collection in the database (default: false) |
blockchain | string | No | "ETHEREUM", "BASE", "TEZOS", "ARBITRUM" (default: "ARBITRUM") |
project_id | UUID | No | Project to create the collection in (only used when save: true) |
social_org | string | No | Custodial social platform |
social_identifier | string | No | Custodial social user ID |
Response (save: false):
{
"success": true,
"generated": {
"title": "Cyberpunk Cats",
"description": "Neon-drenched feline portraits...",
"prompt": "A $cat_style cyberpunk cat in a neon city...",
"variables": [...],
"data": { "version": "v2", "steps": [...], "variables": [...] }
}
}Response (save: true):
{
"success": true,
"generated": { ... },
"collection_id": "uuid"
}3. Collection CRUD (Database)
POST /api/nft/collections/create
Tab: Collections
Create a new NFT collection in the EmProps DB (no on-chain action). When using the generate endpoint with save: true, this step is handled automatically.
Proxies to: POST emprops-api/nft/collections
Request Body:
{
"title": "My Collection",
"description": "A description",
"editions": 1000,
"price": 0.5,
"blockchain": "ARBITRUM",
"data": {
"version": "v2",
"steps": [
{
"id": 1,
"nodeName": "emerge-base-pro",
"nodePayload": {
"intent": "A generation prompt with $variables",
"user_image": null,
"optional_ref_image": null,
"seed": null
}
}
],
"generations": { "hashes": [], "generations": 1, "use_custom_hashes": false },
"variables": [
{
"name": "color",
"type": "pick",
"value_type": "strings",
"is_feature": true,
"feature_name": "Color",
"is_selectable": true,
"is_user_customizable": false,
"required": false,
"value": [
{ "value": "red", "display_name": "Red", "weight": 1 },
{ "value": "blue", "display_name": "Blue", "weight": 1 }
]
}
]
}
}Response:
{
"success": true,
"collection": { "id": "uuid", "title": "My Collection", ... },
"contractParams": {
"name": "MyCollection",
"symbol": "MC",
"baseURI": "https://metadata.emprops.app/collections/uuid/tokens/",
"contractURI": "https://metadata.emprops.app/collections/uuid/metadata.json",
"maxSupply": 1000
}
}Auto-generates contractParams used to pre-fill the on-chain deploy form.
GET /api/nft/collections/lookup?collectionId=<uuid>
Tab: Collections
Fetch collection metadata + deployment status from EmProps DB.
Proxies to: GET emprops-api/nft/collections/:collectionId
Response:
{
"success": true,
"collection": {
"id": "uuid",
"title": "My Collection",
"contract_address": "0x...",
"chain_id": 31337,
"data": { "variables": [...], "steps": [...] },
...
},
"contractParams": { "name": "...", "symbol": "...", ... }
}PUT /api/nft/collections/update
Tab: Collections
Update an existing collection in the DB.
Proxies to: PUT emprops-api/nft/collections/:collectionId
Request Body:
{
"collectionId": "uuid",
"title": "Updated Title",
"description": "Updated description",
"editions": 2000,
"data": { "version": "v2", "steps": [...], "variables": [...] }
}POST /api/nft/collections/reset
Tab: Collections
Clear on-chain deployment fields when contract is stale (e.g. Hardhat restart).
Proxies to: POST emprops-api/nft/collections/:collectionId/reset
Request Body:
{ "collectionId": "uuid" }Clears contract_address, chain_id, deploy_tx_hash from DB so the collection can be re-deployed.
4. Collection On-Chain Operations
GET /api/nft/collections?chainId=X&addresses=0x...,0x...
Tab: Collections
Read collection metadata directly from deployed Diamond contracts on-chain.
| Param | Type | Description |
|---|---|---|
chainId | number | Blockchain network (default 31337) |
addresses | string | Comma-separated contract addresses |
Response:
{
"success": true,
"collections": [
{
"address": "0x...",
"name": "My Collection",
"symbol": "MC",
"owner": "0x...",
"minter": "0x...",
"totalSupply": "42",
"maxSupply": "1000",
"mintActive": true,
"contractURI": "https://...",
"collectionId": "uuid"
}
],
"invalidAddresses": ["0x..."]
}On-chain reads: name(), symbol(), totalSupply(), maxSupply(), mintActive, minter, owner(), contractURI(). Extracts collection UUID from contractURI.
POST /api/nft/collections
Tab: Collections (Deploy Contract button)
Deploy a new Diamond collection contract via factory.
Request Body:
{
"chainId": 31337,
"name": "My Collection",
"symbol": "MC",
"baseURI": "https://metadata.emprops.app/collections/uuid/tokens/",
"contractURI": "https://metadata.emprops.app/collections/uuid/metadata.json",
"maxSupply": "1000",
"empropsCollectionId": "uuid"
}Response:
{
"success": true,
"collection": {
"address": "0x...",
"name": "My Collection",
"symbol": "MC",
"owner": "0x...",
"totalSupply": "0",
"maxSupply": "1000",
"mintActive": true
},
"txHash": "0x...",
"salt": "0x..."
}On-chain flow:
collectionCount()→ determine saltpredictAddress()→ predict deployment addresscreateCollection(struct)→ deploy Diamond proxy- If
empropsCollectionIdprovided → savescontract_addressback to DB viaPOST emprops-api/nft/collections/{id}/deploy-status
4. Minting
POST /api/nft/mint
Tab: Mint
Mint a token and trigger async generation.
Proxies to: POST emprops-api/nft/collections/:collectionId/mint-and-generate
Request Body:
{
"collectionId": "uuid",
"recipient": "0x...",
"variables": {
"color": "red",
"style": "abstract"
}
}Response:
{
"success": true,
"mint": {
"tokenId": 42,
"txHash": "0x...",
"recipient": "0x...",
"deckId": "uuid",
"metadataUri": "https://..."
},
"generation": {
"jobId": "uuid",
"outputId": "uuid",
"deckId": "uuid",
"status": "generating"
}
}emprops-api flow:
- Mints token on-chain immediately → reserves tokenId
- Creates deck with mint state via
mint_stateon generations endpoint - Triggers async image generation (same pipeline as
POST /collections/:id/generations) - Client polls
GET /nft/decks/:deckId/mint-statusfor completion
GET /api/nft/mint?collectionId=<uuid>
Tab: Mint (mint history list)
List all mints for a collection.
Proxies to: GET emprops-api/nft/collections/:collectionId/mints
Response:
{
"success": true,
"mints": [
{
"id": "deck-uuid",
"token_id": 42,
"mint_tx_hash": "0x...",
"mint_recipient": "0x...",
"mint_status": "minted",
"minted_at": "2026-02-11T..."
}
]
}5. Metadata Inspection
GET /api/nft/metadata
Tabs: Metadata, Mint, Collections
Fetch and parse metadata from on-chain contracts. Server-side fetch handles localhost URIs that browsers can't reach.
| Param | Type | Description |
|---|---|---|
chainId | number | Blockchain (default 31337) |
address | string | Contract address |
type | string | contract, token, or tokens |
tokenId | number | Required for type=token |
type=contract — ERC-7572 collection metadata:
{
"success": true,
"uri": "https://metadata.emprops.app/collections/uuid/metadata.json",
"metadata": {
"name": "My Collection",
"description": "...",
"image": "https://...",
"external_link": "https://..."
}
}type=token — ERC-721 token metadata:
{
"success": true,
"uri": "https://metadata.emprops.app/collections/uuid/tokens/42.json",
"owner": "0x...",
"metadata": {
"name": "Token #42",
"description": "...",
"image": "https://...",
"attributes": [
{ "trait_type": "Color", "value": "Red" }
]
}
}type=tokens — Enumerate all tokens (capped at 100):
{
"success": true,
"tokens": [
{ "tokenId": 1, "uri": "https://...", "owner": "0x..." },
{ "tokenId": 2, "uri": "https://...", "owner": "0x..." }
],
"totalSupply": 42
}6. Admin Actions
POST /api/nft/admin
Tab: Admin
Execute on-chain admin functions on a Diamond collection.
Request Body:
{
"chainId": 31337,
"collectionAddress": "0x...",
"action": "setMintActive",
"value": true
}| Action | Value Type | Contract Function |
|---|---|---|
setMintActive | boolean | MintFacet.setMintActive(bool) |
setMinter | address | MintFacet.setMinter(address) |
setMaxSupply | number string | MintFacet.setMaxSupply(uint256) |
setBaseURI | URL string | ERC721Facet.setBaseURI(string) |
setContractURI | URL string | ContractURIFacet.setContractURI(string) |
transferOwnership | address | OwnershipFacet.transferOwnership(address) |
Response:
{ "success": true, "txHash": "0x...", "action": "setMintActive", "value": true }7. Ownership (Ponder Indexer Data)
GET /api/nft/ownership?collectionId=<uuid>
Tab: Ownership
Fetch indexed token ownership data for a collection. Data is populated by the Ponder blockchain indexer.
Proxies to: GET emprops-api/nft/collections/:collectionId/ownership
Response:
{
"success": true,
"tokens": [
{
"id": "deck-uuid",
"token_id": 1,
"current_owner": "0x...",
"mint_recipient": "0x...",
"mint_status": "minted",
"last_transfer_at": "2026-02-25T..."
}
]
}| Field | Source | Description |
|---|---|---|
current_owner | Ponder indexer | Current on-chain owner (updated on each Transfer event) |
mint_recipient | Mint API | Original mint recipient (set at mint time) |
last_transfer_at | Ponder indexer | Timestamp of most recent transfer |
GET /api/nft/ownership/transfers?deckId=<uuid>
Tab: Ownership (expanded token row)
Fetch transfer history for a specific token/deck.
Proxies to: GET emprops-api/nft/decks/:deckId/transfers
Response:
{
"success": true,
"transfers": [
{
"id": "uuid",
"deck_id": "uuid",
"from_address": "0x0000000000000000000000000000000000000000",
"to_address": "0x...",
"block_number": "123456789",
"tx_hash": "0x...",
"log_index": 0,
"timestamp": "2026-02-25T..."
}
]
}Mint events have from_address = 0x0000...0000. Transfers are ordered by block_number descending.
GET /api/nft/ownership/indexer-status
Tab: Ownership (status banner)
Fetch Ponder indexer operational state.
Proxies to: GET emprops-api/nft/indexer/status
Response:
{
"success": true,
"state": {
"id": "singleton",
"status": "idle",
"last_indexed_block": "123456789",
"chain_id": 42161,
"started_at": "2026-02-25T...",
"updated_at": "2026-02-25T..."
}
}| Status | Meaning |
|---|---|
idle | Indexer is caught up to chain head |
indexing | Processing new events |
reindexing | Full reindex in progress — ownership data temporarily incomplete |
Shared Helpers
apps/monitor/src/lib/nft/api.ts:
getApiUrl()— returnsEMPROPS_API_URLenv varapiHeaders(contentType?)— returns{ "Content-Type": "application/json", "Authorization": "Bearer ..." }
Generation API (emprops-api direct)
The mint endpoint ultimately calls the same generation handler as:
POST /collections/:id/generations
Schema:
{
"variables": { "color": "red", "farcaster_pfp": "https://..." },
"workflow_id": "optional",
"workflow_priority": 100,
"job_type": "farcaster",
"social_org": "farcaster",
"social_identifier": "12345",
"deck_id": "optional-uuid",
"mint_state": {
"token_id": 42,
"tx_hash": "0x...",
"recipient": "0x...",
"metadata_uri": "https://..."
}
}variables— key/value overrides for collection variables + component fields (e.g.seed)deck_id— use existing deck instead of creating onemint_state— (optional) when present, creates the deck with on-chain mint data baked in. Used bymint-and-generate.
Response:
{
"data": { "jobId": "uuid", "outputId": "uuid", "deckId": "uuid" },
"error": null
}8. Image Upload
POST /api/nft/upload-image
Tab: Collections (Reference Images, Variable Image Upload)
Upload an image and create a flat_file record for asset tracking.
Proxies to: POST emprops-api/upload-base64 with create_flat_file: true
Request Body:
{
"base64Data": "iVBOR...",
"mimeType": "image/png",
"filename": "ref-scene.png",
"userId": "canonical-user-uuid"
}Response:
{
"success": true,
"url": "user-uploads/user-id/abc-123/def456.png",
"path": "user-uploads/user-id/abc-123/def456.png",
"flatFileId": 12345
}The returned url is a storage path that SmartImage resolves through the CDN (imgproxy). Store this path in variable values — the ComfyUI workflow's EmProps_Image_Loader will download from the CDN at runtime.
9. Samples
POST /api/nft/samples
Tab: Collections (Samples section)
Generate sample images for a collection. Supports free tier + paid (USDC) samples.
Proxies to: POST emprops-api/nft/collections/:collectionId/samples
Request Body:
{
"collectionId": "uuid",
"count": 3,
"wallet_address": "0x...",
"chain_id": 421614,
"variables": {
"farcaster_pfp": "https://imagedelivery.net/.../original"
},
"tx_hash": "0x... (required if paid samples)"
}| Field | Type | Required | Description |
|---|---|---|---|
collectionId | UUID | Yes | Collection to generate samples for |
count | number | Yes | Number of samples (1-10) |
wallet_address | string | Yes | Wallet address of requester |
chain_id | number | No | Blockchain network ID |
variables | object | No | Variable overrides — customizable variables accept any value, selectable variables pick from presets. Omitted variables are picked randomly from presets. |
tx_hash | string | No | USDC payment transaction hash (required when free samples exhausted) |
Variable Override Behavior:
farcaster_pfp— auto-populated with creator's social PFP in the monitor UI. At mint time, the minter's PFP is used instead.- Customizable variables — any value can be passed (free-form input)
- Selectable variables — value must be one of the preset options (dropdown in UI)
- Omitted or empty — random pick from preset values
Response:
{
"success": true,
"samples": [
{ "outputId": "uuid", "jobId": "uuid", "status": "pending", "is_free": true }
]
}Payment Flow:
- Check
free_remainingvia sample-status endpoint - If paid samples needed, transfer USDC to compute recipient on-chain
- Include
tx_hashin request — API verifies payment before generating
GET /api/nft/samples?collectionId=<uuid>
Tab: Collections (Samples section)
List all samples for a collection.
Proxies to: GET emprops-api/nft/collections/:collectionId/samples
Response:
{
"success": true,
"samples": [
{
"outputId": "uuid",
"url": "https://cdn.emprops.ai/...",
"status": "completed",
"is_free": true,
"created_at": "2026-03-17T...",
"jobId": "uuid",
"traits": [
{ "trait_type": "Color", "value": "Red" },
{ "trait_type": "Style", "value": "Abstract" }
]
}
]
}The traits array is extracted from resolved variable values after generation completes — only variables with is_feature: true appear as traits.
GET /api/nft/sample-status?collectionId=<uuid>
Tab: Collections (Samples section)
Get free/paid sample counts and pricing for a collection.
Response:
{
"success": true,
"free_remaining": 2,
"free_limit_per_collection": 3,
"free_used_collection": 1,
"free_limit_per_user": 10,
"free_used_user": 3,
"paid_count": 5,
"compute_cost_per_sample": 0.34
}Variable Schema
Variables define the randomizable/customizable parameters of a collection. Each variable has flags controlling how it behaves at generation and mint time:
{
"name": "color",
"type": "pick",
"value_type": "strings",
"is_feature": true,
"feature_name": "Color",
"is_selectable": false,
"is_user_customizable": false,
"required": false,
"value": [
{ "value": "red", "display_name": "Red", "weight": 1 },
{ "value": "blue", "display_name": "Blue", "weight": 1 }
]
}| Flag | Effect |
|---|---|
is_feature | Variable becomes an NFT trait attribute (shown in metadata) |
is_selectable | Minter picks from preset value options via dropdown at mint time |
is_user_customizable | Minter provides any value via free-form input at mint time |
required | (Only when customizable) Minter must provide a value — cannot be left empty |
Behavior when no override is provided: Value is randomly picked from the preset value array, weighted by weight.
Special variables:
farcaster_pfp— always present,is_user_customizable: true. Defaults to blank placeholder. At sample time, auto-populated with creator's PFP. At mint time, auto-populated with minter's Farcaster PFP.ref_image_1throughref_image_5— creator reference images, defaults to blank placeholder. Blank images are skipped by the Gemini node.
UI Tab → API Mapping
| Tab | Endpoints Used |
|---|---|
| Factory | GET /api/nft/deploy, POST /api/nft/deploy, GET /api/nft/factory |
| Collections | POST /api/nft/collections/create, GET /api/nft/collections/lookup, PUT /api/nft/collections/update, POST /api/nft/collections/reset, GET /api/nft/collections, POST /api/nft/collections, POST /api/nft/upload-image, POST /api/nft/samples, GET /api/nft/samples, GET /api/nft/sample-status |
| Mint | GET /api/nft/collections/lookup, POST /api/nft/mint, GET /api/nft/mint, GET /api/nft/metadata |
| Metadata | GET /api/nft/metadata (contract, token, tokens) |
| Admin | POST /api/nft/admin |
| Ownership | GET /api/nft/ownership, GET /api/nft/ownership/transfers, GET /api/nft/ownership/indexer-status |
