API Reference
Full endpoint reference for the Medialane REST API. Base URL: https://medialane-backend-production.up.railway.app
Health
Public uptime and system status. Use this to monitor indexer lag and database connectivity.
/healthGet system health status, including database connectivity and indexer lag.
cURL
curl "https://medialane-backend-production.up.railway.app/health"
Response
{
"status": "ok",
"timestamp": "2026-03-05T12:00:00Z",
"database": "ok",
"indexer": {
"lastBlock": "6205000",
"latestBlock": "6205005",
"lagBlocks": 5
}
}Orders
/v1/ordersList all open orders (listings and bids). Supports filtering, sorting, and pagination.
Parameters
statusstringFilter by status: OPEN | FULFILLED | CANCELLEDnftContractstringFilter by NFT contract addresscurrencystringFilter by payment token: USDC | USDT | ETH | STRK | WBTCsortstringSort field: priceRaw | createdAtorderstringasc | desc (default: desc)pagenumberPage number (default: 1)limitnumberItems per page (default: 20, max: 100)* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/orders?status=OPEN&limit=5" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"data": [
{
"orderHash": "0x04f7a1...",
"offerer": "0x0591...",
"nftContract": "0x05e7...",
"tokenId": "42",
"price": "500000",
"currency": "USDC",
"status": "OPEN",
"orderType": "LISTING",
"createdAt": "2026-03-01T10:00:00Z"
}
],
"meta": { "total": 128, "page": 1, "limit": 5 }
}/v1/orders/:hashGet a single order by its on-chain order hash.
Parameters
hashstring *The 0x-prefixed order hash* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/orders/0x04f7a1..." \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"orderHash": "0x04f7a1...",
"offerer": "0x0591...",
"nftContract": "0x05e7...",
"tokenId": "42",
"price": "500000",
"currency": "USDC",
"status": "OPEN"
}/v1/orders/token/:contract/:tokenIdGet all orders for a specific token.
Parameters
contractstring *NFT contract addresstokenIdstring *Token ID* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/orders/token/0x05e7.../42" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"data": [...],
"meta": { "total": 3, "page": 1, "limit": 20 }
}/v1/orders/user/:addressGet all orders created by a specific user address.
Parameters
addressstring *Starknet user address* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/orders/user/0x0591..." \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"data": [...],
"meta": { "total": 7, "page": 1, "limit": 20 }
}Minting
Directly mint assets into existing collections or register new collection contracts. These operations return fully-populated calldata for immediate on-chain execution.
/v1/intents/mintMint an NFT into an existing Medialane collection.
Parameters
ownerstring *Collection owner addresscollectionIdstring *Hex or decimal collection IDrecipientstring *Recipient addresstokenUristring *IPFS URI or metadata URLcollectionContractstringOptional: registry contract override* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/intents/mint" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "owner": "0x0591...", "collectionId": "42", "recipient": "0x0591...", "tokenUri": "ipfs://..." }'Response
{ "intentId": "clm_mnt123", "status": "SIGNED", "calls": [...] }/v1/intents/create-collectionRegister a new NFT collection contract on the Medialane registry.
Parameters
ownerstring *Requester addressnamestring *Collection namesymbolstring *Collection symbolbaseUristring *Base URI for tokenscollectionContractstringOptional: registry contract override* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/intents/create-collection" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "owner": "0x0591...", "name": "My Collection", "symbol": "MYC", "baseUri": "ipfs://..." }'Response
{ "intentId": "clm_coll123", "status": "SIGNED", "calls": [...] }Collections
/v1/collectionsList indexed NFT collections with floor price, volume, and token count.
Parameters
pagenumberPage numberlimitnumberItems per pageownerstringFilter by collection owner addressisKnownbooleantrue = featured collections onlysortstring"recent" (default) | "supply" | "floor" | "volume" | "name"* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/collections?owner=0x0591..." \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"data": [
{
"contract": "0x05e7...",
"collectionId": "1",
"name": "Mediolano Genesis",
"owner": "0x0591...",
"floorPrice": "100000",
"floorCurrency": "USDC",
"totalVolume": "5000000",
"tokenCount": 512
}
],
"meta": { "total": 14, "page": 1, "limit": 20 }
}/v1/collections/:contractGet metadata and statistics for a single collection.
Parameters
contractstring *NFT contract address* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/collections/0x05e7..." \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"contract": "0x05e7...",
"collectionId": "1",
"name": "Mediolano Genesis",
"owner": "0x0591...",
"floorPrice": "100000",
"totalVolume": "5000000",
"tokenCount": 512
}/v1/collections/:contract/tokensList tokens in a collection.
Parameters
contractstring *NFT contract addresspagenumberPage numberlimitnumberItems per page* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/collections/0x05e7.../tokens?limit=10" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"data": [...],
"meta": { "total": 512, "page": 1, "limit": 10 }
}Tokens
/v1/tokens/owned/:addressGet all tokens owned by a Starknet address.
Parameters
addressstring *Owner's Starknet address* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/tokens/owned/0x0591..." \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"data": [
{
"contract": "0x05e7...",
"tokenId": "42",
"owner": "0x0591...",
"metadata": { "name": "Genesis #42", "image": "ipfs://..." }
}
],
"meta": { "total": 3, "page": 1, "limit": 20 }
}/v1/tokens/:contract/:tokenIdGet a single token with resolved metadata. Use ?wait=true for JIT metadata resolution.
Parameters
contractstring *NFT contract addresstokenIdstring *Token IDwaitbooleanIf true, blocks up to 3s to resolve missing metadata* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/tokens/0x05e7.../42?wait=true" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"contract": "0x05e7...",
"tokenId": "42",
"owner": "0x0591...",
"metadata": {
"name": "Genesis #42",
"description": "...",
"image": "ipfs://..."
}
}/v1/tokens/:contract/:tokenId/historyGet transfer history for a token.
Parameters
contractstring *NFT contract addresstokenIdstring *Token ID* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/tokens/0x05e7.../42/history" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"data": [
{
"from": "0x0000...",
"to": "0x0591...",
"txHash": "0xabc...",
"blockNumber": 7000000,
"timestamp": "2026-03-01T10:00:00Z"
}
]
}Batch Tokens
Fetch up to 50 tokens in a single request by providing contract+tokenId pairs. More efficient than individual token lookups when hydrating a list or cart.
/v1/tokens/batchFetch multiple tokens by contract and tokenId pairs. Returns the same shape as the single token endpoint but as an array.
Parameters
itemsstring *Comma-separated contract:tokenId pairs — e.g. 0x05e7...:1,0x05e7...:2 (max 50 pairs)* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/tokens/batch?items=0x05e7...:1,0x05e7...:2" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"data": [
{
"contract": "0x05e7...",
"tokenId": "1",
"owner": "0x0591...",
"metadata": { "name": "Genesis #1", "image": "ipfs://..." }
},
{
"contract": "0x05e7...",
"tokenId": "2",
"owner": "0x0482...",
"metadata": { "name": "Genesis #2", "image": "ipfs://..." }
}
]
}Activities
/v1/activitiesList all indexed on-chain events (transfers, sales, listings, cancellations).
Parameters
typestringFilter: TRANSFER | SALE | LISTING | CANCELpagenumberPage numberlimitnumberItems per page* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/activities?type=SALE&limit=10" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"data": [
{
"type": "SALE",
"from": "0x0591...",
"to": "0x0482...",
"nftContract": "0x05e7...",
"tokenId": "42",
"price": "500000",
"currency": "USDC",
"txHash": "0xabc...",
"timestamp": "2026-03-01T10:00:00Z"
}
],
"meta": { "total": 441, "page": 1, "limit": 10 }
}/v1/activities/:addressGet all activities for a specific user address.
Parameters
addressstring *Starknet address* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/activities/0x0591..." \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"data": [...],
"meta": { "total": 12, "page": 1, "limit": 20 }
}Intents
Intents orchestrate SNIP-12 typed data for listings, offers, fulfillments, and cancellations. Create an intent, sign it client-side, then submit the signature.
/v1/intents/listingCreate a listing intent. Returns typed data for SNIP-12 signing.
Parameters
nftContractstring *NFT contract addresstokenIdstring *Token IDpricestring *Price in smallest denominationcurrencystring *USDC | USDT | ETH | STRK | WBTCoffererstring *Seller Starknet address* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/intents/listing" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"nftContract": "0x05e7...",
"tokenId": "42",
"price": "500000",
"currency": "USDC",
"offerer": "0x0591..."
}'Response
{
"intentId": "clm_abc123",
"typedData": {
"types": { ... },
"primaryType": "Order",
"domain": { "name": "Medialane", "version": "1", "revision": "1" },
"message": { ... }
}
}/v1/intents/offerCreate an offer (bid) intent for a specific token.
Parameters
nftContractstring *Target NFT contracttokenIdstring *Token IDpricestring *Offer amount in smallest denominationcurrencystring *USDC | USDT | ETH | STRK | WBTCoffererstring *Buyer Starknet address* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/intents/offer" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "nftContract": "0x05e7...", "tokenId": "42", "price": "400000", "currency": "USDC", "offerer": "0x0482..." }'Response
{ "intentId": "clm_def456", "typedData": { ... } }/v1/intents/fulfillCreate a fulfillment intent to accept an open order.
Parameters
orderHashstring *Hash of the order to fulfillfulfillerstring *Fulfiller Starknet address* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/intents/fulfill" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "orderHash": "0x04f7a1...", "fulfiller": "0x0482..." }'Response
{ "intentId": "clm_ghi789", "typedData": { ... } }/v1/intents/cancelCreate a cancellation intent for an open order.
Parameters
orderHashstring *Hash of the order to canceloffererstring *Original offerer address* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/intents/cancel" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "orderHash": "0x04f7a1...", "offerer": "0x0591..." }'Response
{ "intentId": "clm_jkl012", "typedData": { ... } }/v1/intents/:idGet the status of an intent.
Parameters
idstring *Intent ID* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/intents/clm_abc123" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"intentId": "clm_abc123",
"status": "PENDING_SIGNATURE",
"type": "LISTING"
}/v1/intents/:id/signatureSubmit the SNIP-12 signature for an intent to trigger on-chain execution.
Parameters
idstring *Intent IDsignaturestring[] *Starknet signature array [r, s]* required
cURL
curl -X PATCH "https://medialane-backend-production.up.railway.app/v1/intents/clm_abc123/signature" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "signature": ["0xaaa...", "0xbbb..."] }'Response
{
"intentId": "clm_abc123",
"status": "SUBMITTED",
"txHash": "0xabc..."
}Checkout Intent
Create fulfillment intents for multiple orders in a single request. Useful for cart-style checkout flows. Failed items return an error field rather than aborting the whole batch.
/v1/intents/checkoutBatch fulfill intent creation. Accepts up to 20 order hashes. Per-item error handling — failed items return { orderHash, error } instead of rejecting the entire request.
Parameters
fulfillerstring *Fulfiller Starknet addressorderHashesstring[] *Array of order hashes to fulfill (max 20)* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/intents/checkout" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"fulfiller": "0x0482...",
"orderHashes": ["0xabc...", "0xdef..."]
}'Response
{
"data": [
{
"id": "clm_xyz001",
"orderHash": "0xabc...",
"typedData": { "types": { ... }, "primaryType": "Order", "domain": { ... }, "message": { ... } },
"calls": [...],
"expiresAt": "2026-03-12T10:15:00Z"
},
{
"orderHash": "0xdef...",
"error": "Order no longer available"
}
]
}Metadata
/v1/metadata/signed-urlGet a pre-signed upload URL for pinning metadata to IPFS via Medialane CDN.
cURL
curl "https://medialane-backend-production.up.railway.app/v1/metadata/signed-url" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"url": "https://ipfs.io/ipfs/...",
"fields": { ... },
"expiresAt": "2026-03-01T10:30:00Z"
}/v1/metadata/uploadUpload JSON metadata. Returns an IPFS CID.
Parameters
metadataobject *ERC-721 compatible JSON metadata* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/metadata/upload" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "metadata": { "name": "My NFT", "description": "...", "image": "ipfs://..." } }'Response
{
"cid": "QmXyz...",
"uri": "ipfs://QmXyz..."
}/v1/metadata/upload-fileUpload a media file. Returns an IPFS CID and gateway URL.
Parameters
fileFile (multipart) *Image, audio, or video file* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/metadata/upload-file" \ -H "x-api-key: ml_live_YOUR_KEY" \ -F "file=@artwork.png"
Response
{
"cid": "QmAbc...",
"uri": "ipfs://QmAbc...",
"gateway": "https://gateway.pinata.cloud/ipfs/QmAbc..."
}/v1/metadata/resolveResolve and return the metadata JSON for an IPFS URI or on-chain token.
Parameters
uristring *ipfs:// URI or https:// URL* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/metadata/resolve?uri=ipfs%3A%2F%2FQmXyz..." \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"name": "My NFT",
"description": "...",
"image": "ipfs://QmAbc..."
}Search
/v1/searchFull-text search across tokens, collections, and users.
Parameters
qstring *Search query stringtypestringFilter: token | collection | userlimitnumberMax results (default: 10)* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/search?q=genesis" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"data": [
{ "type": "collection", "contract": "0x05e7...", "name": "Mediolano Genesis" },
{ "type": "token", "contract": "0x05e7...", "tokenId": "1", "name": "Genesis #1" }
]
}Events (SSE)
Subscribe to a real-time Server-Sent Events stream for transfers, order lifecycle events, and keepalive pings. Authentication uses a query parameter since browsers cannot send custom headers with the native EventSource API. PREMIUM plan recommended for sustained connections.
/v1/eventsOpen a Server-Sent Events stream. The server sends transfer, order.created, order.fulfilled, order.cancelled, and ping (keepalive every 15s) events. Automatically reconnects after 10 minutes via a reconnect event.
Parameters
apiKeystring *Your API key (query param — required because EventSource cannot send custom headers)sincestringISO 8601 timestamp — resume stream from this point in time* required
cURL
# Open the stream (cURL streams until closed) curl -N "https://medialane-backend-production.up.railway.app/v1/events?apiKey=ml_live_YOUR_KEY" # Resume from a specific timestamp curl -N "https://medialane-backend-production.up.railway.app/v1/events?apiKey=ml_live_YOUR_KEY&since=2026-03-12T10:00:00Z" # Resume using Last-Event-ID header (standard SSE resume) curl -N "https://medialane-backend-production.up.railway.app/v1/events?apiKey=ml_live_YOUR_KEY" \ -H "Last-Event-ID: evt_abc123"
Response
id: evt_001
event: transfer
data: {"contractAddress":"0x05e7...","tokenId":"42","from":"0x0000...","to":"0x0591...","txHash":"0xabc...","timestamp":"2026-03-12T10:00:01Z"}
id: evt_002
event: order.created
data: {"orderHash":"0x04f7a1...","nftContract":"0x05e7...","tokenId":"42","price":"500000","currency":"USDC","offerer":"0x0591..."}
id: evt_003
event: order.fulfilled
data: {"orderHash":"0x04f7a1...","fulfiller":"0x0482...","txHash":"0xdef..."}
id: evt_004
event: order.cancelled
data: {"orderHash":"0x04f7a1...","offerer":"0x0591..."}
id: evt_005
event: ping
data: {}
event: reconnect
data: {}Browser (native EventSource)
const url = `https://medialane-backend-production.up.railway.app/v1/events?apiKey=${YOUR_KEY}`
const source = new EventSource(url)
source.addEventListener("transfer", (e) => {
const transfer = JSON.parse(e.data)
console.log("Transfer:", transfer.contractAddress, transfer.tokenId)
})
source.addEventListener("order.fulfilled", (e) => {
const order = JSON.parse(e.data)
console.log("Order fulfilled:", order.orderHash)
})
source.addEventListener("order.created", (e) => {
const order = JSON.parse(e.data)
console.log("New listing:", order.orderHash, order.price, order.currency)
})
// Reconnect with resume on error
source.addEventListener("error", () => {
const lastId = source.lastEventId
source.close()
const resumeUrl = `https://medialane-backend-production.up.railway.app/v1/events?apiKey=${YOUR_KEY}${lastId ? `&since=${lastId}` : ""}`
// reconnect: new EventSource(resumeUrl)
})Node.js (eventsource npm package)
import EventSource from "eventsource"
const url = `https://medialane-backend-production.up.railway.app/v1/events?apiKey=${YOUR_KEY}`
const source = new EventSource(url)
source.addEventListener("order.fulfilled", (e) => {
const order = JSON.parse(e.data)
console.log("Order fulfilled:", order.orderHash)
})
source.addEventListener("ping", () => {
// keepalive — no action needed
})
source.addEventListener("reconnect", () => {
// server is closing after 10 min — reconnect
source.close()
new EventSource(`https://medialane-backend-production.up.railway.app/v1/events?apiKey=${YOUR_KEY}`)
})
// Resume from a known point using Last-Event-ID
const resumeSource = new EventSource(url, {
headers: { "Last-Event-ID": "evt_abc123" },
})Portal (Self-service)
Portal endpoints manage your tenant account: API keys, usage stats, and webhooks (PREMIUM). These calls never count toward your monthly quota.
/v1/portal/meGet your tenant profile: plan, quota usage, and key count.
cURL
curl "https://medialane-backend-production.up.railway.app/v1/portal/me" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"tenantId": "tnt_xxx",
"email": "you@example.com",
"plan": "FREE",
"quota": 50,
"usedThisMonth": 12
}/v1/portal/keysList all API keys for your account.
cURL
curl "https://medialane-backend-production.up.railway.app/v1/portal/keys" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"data": [
{ "id": "key_abc", "name": "Production", "prefix": "ml_live_abc...", "createdAt": "..." }
]
}/v1/portal/keysCreate a new API key (max 5 per account).
Parameters
namestring *A label for this key* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/portal/keys" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "My Agent Key" }'Response
{
"id": "key_new",
"name": "My Agent Key",
"key": "ml_live_FULL_KEY_SHOWN_ONCE",
"createdAt": "..."
}/v1/portal/keys/:idDelete an API key. This action is irreversible.
Parameters
idstring *Key ID* required
cURL
curl -X DELETE "https://medialane-backend-production.up.railway.app/v1/portal/keys/key_abc" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{ "success": true }/v1/portal/usageGet 30-day daily usage breakdown.
cURL
curl "https://medialane-backend-production.up.railway.app/v1/portal/usage" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"data": [
{ "date": "2026-03-01", "requests": 8 },
{ "date": "2026-02-28", "requests": 4 }
]
}/v1/portal/usage/recentGet the last N request log entries.
Parameters
limitnumberMax entries (default: 20)* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/portal/usage/recent?limit=5" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"data": [
{ "method": "GET", "path": "/v1/orders", "status": 200, "ts": "2026-03-01T10:01:00Z" }
]
}/v1/portal/webhooksList registered webhooks. PREMIUM only.
cURL
curl "https://medialane-backend-production.up.railway.app/v1/portal/webhooks" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"data": [
{ "id": "wh_abc", "url": "https://yourapp.com/hook", "events": ["ORDER_CREATED"], "active": true }
]
}/v1/portal/webhooksRegister a new webhook endpoint. PREMIUM only.
Parameters
urlstring *HTTPS endpoint to receive eventseventsstring[] *ORDER_CREATED | ORDER_FULFILLED | ORDER_CANCELLED | TRANSFER* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/portal/webhooks" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://yourapp.com/hook", "events": ["ORDER_CREATED", "TRANSFER"] }'Response
{
"id": "wh_new",
"url": "https://yourapp.com/hook",
"events": ["ORDER_CREATED", "TRANSFER"],
"secret": "whsec_SHOWN_ONCE"
}/v1/portal/webhooks/:idDelete a webhook.
Parameters
idstring *Webhook ID* required
cURL
curl -X DELETE "https://medialane-backend-production.up.railway.app/v1/portal/webhooks/wh_abc" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{ "success": true }Collection Claims
Claim ownership of an existing Starknet ERC-721 collection. Three verification paths available: automatic on-chain check (requires Clerk JWT), SNIP-12 signature challenge, or manual email review.
/v1/collections/claimPath 1: Auto-verify ownership on-chain. Requires both a tenant API key and a Clerk session JWT in the Authorization header. The API checks that the authenticated wallet is the on-chain owner of the contract.
Parameters
contractAddressstring *The ERC-721 contract address to claimwalletAddressstring *The Starknet wallet address claiming ownership* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/collections/claim" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Authorization: Bearer CLERK_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{ "contractAddress": "0x076c...", "walletAddress": "0x03d0..." }'Response
{
"verified": true,
"collection": { "contractAddress": "0x076c...", "name": "My Collection", "claimedBy": "0x03d0..." }
}
// If not verified:
{ "verified": false, "reason": "not_owner" }/v1/collections/claim/challengePath 2 (step 1): Request a SNIP-12 typed-data challenge for a contract address. Sign the returned typedData with your Starknet wallet, then submit to /verify.
Parameters
contractAddressstring *The ERC-721 contract address to claimwalletAddressstring *The wallet that will sign the challenge* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/collections/claim/challenge" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "contractAddress": "0x076c...", "walletAddress": "0x03d0..." }'Response
{
"challengeId": "chal_abc123",
"typedData": { "domain": { "name": "Medialane", "version": "1", "revision": "1" }, "..." },
"expiresAt": "2026-03-15T16:00:00Z"
}/v1/collections/claim/verifyPath 2 (step 2): Submit the SNIP-12 signature from the challenge step. If valid, the collection is marked as claimed by the wallet.
Parameters
challengeIdstring *Challenge ID from /claim/challengesignatureobject *{ r: string; s: string } — starknet.js signature object* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/collections/claim/verify" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "challengeId": "chal_abc123", "signature": { "r": "0x...", "s": "0x..." } }'Response
{
"verified": true,
"collection": { "contractAddress": "0x076c...", "claimedBy": "0x03d0..." }
}/v1/collections/claim/requestPath 3: Submit a manual claim request for admin review. No wallet signature required — our team will verify and reach out by email.
Parameters
contractAddressstring *The ERC-721 contract address to claimemailstring *Email address for review correspondencewalletAddressstringOptional: your Starknet wallet addressnotesstringOptional: context about your connection to the collection* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/collections/claim/request" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "contractAddress": "0x076c...", "email": "creator@example.com", "notes": "I deployed this contract in block 7488000" }'Response
{
"claim": {
"id": "clm_xyz",
"contractAddress": "0x076c...",
"status": "PENDING",
"verificationMethod": "MANUAL",
"createdAt": "2026-03-15T15:00:00Z"
}
}Profiles
Enriched display metadata for collections and creators. Collection profiles can only be updated by the wallet that claimed the collection (requires Clerk JWT). Creator profiles can be updated by the profile owner.
/v1/collections/:contract/profileGet the display profile for a collection (displayName, description, cover image, banner, social links). Returns null if no profile has been set.
Parameters
contractstring *NFT contract address* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/collections/0x076c.../profile" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"contractAddress": "0x076c...",
"chain": "STARKNET",
"displayName": "The Revenge of Shiroi",
"description": "Music · Video · Concept Art",
"image": "ipfs://bafybeif2a...",
"bannerImage": "ipfs://bafybeic...",
"websiteUrl": "https://shiroi.io",
"twitterUrl": "https://x.com/shiroi",
"discordUrl": null,
"telegramUrl": null,
"updatedAt": "2026-03-15T15:00:00Z"
}/v1/collections/:contract/profileUpdate the display profile for a collection. Requires a Clerk session JWT — the authenticated wallet must be the claimedBy address for this collection.
Parameters
contractstring *NFT contract address (URL param)displayNamestringDisplay name (overrides on-chain name)descriptionstringCollection descriptionimagestringCover image IPFS URI (ipfs://...)bannerImagestringBanner image IPFS URI (ipfs://...)websiteUrlstringWebsite URLtwitterUrlstringTwitter/X URLdiscordUrlstringDiscord server URLtelegramUrlstringTelegram URL* required
cURL
curl -X PATCH "https://medialane-backend-production.up.railway.app/v1/collections/0x076c.../profile" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Authorization: Bearer CLERK_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{ "displayName": "Shiroi Collection", "description": "Music & Concept Art", "websiteUrl": "https://shiroi.io" }'Response
{
"contractAddress": "0x076c...",
"displayName": "Shiroi Collection",
"description": "Music & Concept Art",
"websiteUrl": "https://shiroi.io",
"updatedAt": "2026-03-15T15:05:00Z"
}/v1/creators/:wallet/profileGet the display profile for a creator wallet (displayName, bio, avatar, banner, social links). Returns null if no profile has been set.
Parameters
walletstring *Starknet wallet address* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/creators/0x03d0.../profile" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"walletAddress": "0x03d0...",
"chain": "STARKNET",
"displayName": "Kalamaha",
"bio": "Visual artist on Starknet",
"avatarImage": "ipfs://bafkrei...",
"bannerImage": null,
"websiteUrl": "https://kalamaha.art",
"twitterUrl": "https://x.com/kalamaha",
"discordUrl": null,
"telegramUrl": null,
"updatedAt": "2026-03-15T15:00:00Z"
}/v1/creators/:wallet/profileUpdate a creator profile. Requires a Clerk session JWT — the authenticated wallet must match the wallet URL parameter.
Parameters
walletstring *Starknet wallet address (URL param)displayNamestringDisplay name or handlebiostringShort bioavatarImagestringAvatar IPFS URI (ipfs://...)bannerImagestringBanner IPFS URI (ipfs://...)websiteUrlstringWebsite URLtwitterUrlstringTwitter/X URLdiscordUrlstringDiscord URLtelegramUrlstringTelegram URL* required
cURL
curl -X PATCH "https://medialane-backend-production.up.railway.app/v1/creators/0x03d0.../profile" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Authorization: Bearer CLERK_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{ "displayName": "Kalamaha", "bio": "Visual artist on Starknet" }'Response
{
"walletAddress": "0x03d0...",
"displayName": "Kalamaha",
"bio": "Visual artist on Starknet",
"updatedAt": "2026-03-15T15:05:00Z"
}On-chain Comments
Permanent on-chain comments posted to the NFTComments contract on Starknet. Comments are indexed by the backend and surfaced here. The Cairo contract enforces a 60-second per-address rate limit and comments cannot be deleted on-chain, only hidden at the application layer after reports.
/v1/tokens/:contract/:tokenId/commentsList indexed on-chain comments for a token, newest first. Hidden comments (3+ reports) are excluded.
Parameters
contractstring *NFT contract addresstokenIdstring *Token IDpagenumberPage number (default: 1)limitnumberResults per page (default: 20, max: 100)* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/tokens/0x05e7.../42/comments?limit=20" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"data": [
{
"id": "cmt_01j...",
"chain": "starknet",
"contractAddress": "0x05e7...",
"tokenId": "42",
"author": "0x03d0...",
"content": "This is a permanent mark on Starknet.",
"txHash": "0x07a2...",
"blockNumber": "789123",
"postedAt": "2026-03-22T14:00:00Z"
}
],
"meta": { "page": 1, "limit": 20, "total": 5 }
}Counter-offers
Sellers can respond to buyer bids with a counter-offer — a new on-chain listing linked to the original bid. The original bid is marked COUNTER_OFFERED. The buyer can then accept (fulfill the counter listing) or ignore it.
/v1/orders/counter-offersList counter-offer listings. Pass originalOrderHash for the buyer's view (one counter per bid) or sellerAddress for the seller's view (all counters they have sent). At least one query param is required.
Parameters
originalOrderHashstringOriginal bid order hash — returns the counter-offer for this specific bidsellerAddressstringSeller address — returns all counter-offers sent by this sellerpagenumberPage number (default: 1)limitnumberResults per page (default: 20)* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/orders/counter-offers?originalOrderHash=0x04f7a1..." \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"data": [
{
"id": "ord_01j...",
"orderHash": "0x0a1b...",
"offerer": "0x0591...",
"status": "ACTIVE",
"parentOrderHash": "0x04f7a1...",
"counterOfferMessage": "Best I can do!",
"price": { "raw": "750000", "formatted": "0.75", "currency": "USDC", "decimals": 6 },
"endTime": "2026-03-25T00:00:00Z",
"token": { "name": "Genesis #42", "image": "ipfs://...", "description": null }
}
],
"meta": { "page": 1, "limit": 20, "total": 1 }
}/v1/intents/counter-offerCreate a counter-offer intent. The seller proposes a new price for the NFT in response to a buyer's active bid. Currency is derived server-side from the original bid token — do not pass a currency field. Requires a Clerk session JWT for authentication; the seller address must match the consideration.recipient of the original bid.
Parameters
sellerAddressstring *Seller's wallet addressoriginalOrderHashstring *Order hash of the original buyer bidcounterPricestring *Counter price as raw wei integer stringdurationSecondsnumber *Validity duration in seconds (3600–2592000)messagestringOptional seller message to buyer (max 500 chars)* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/intents/counter-offer" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Authorization: Bearer CLERK_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{
"sellerAddress": "0x0591...",
"originalOrderHash": "0x04f7a1...",
"counterPrice": "750000",
"durationSeconds": 86400,
"message": "Best I can do!"
}'Response
{
"data": {
"id": "int_01j...",
"typedData": { ... },
"calls": [ ... ],
"expiresAt": "2026-03-25T00:00:00Z"
}
}Remix Licensing
Creators can allow others to remix their NFTs under specific license terms. Open licenses (CC0, CC BY, CC BY-SA, CC BY-NC) are auto-approved. Custom terms require creator approval before the requester can mint. All endpoints require a Clerk JWT except the public remixes list.
/v1/tokens/:contract/:tokenId/remixesList public remixes of a token. Price and currency fields are omitted — this is a public endpoint. Returns minted remixes only.
Parameters
contractstring *Original NFT contract addresstokenIdstring *Original token IDpagenumberPage number (default: 1)limitnumberResults per page (default: 20)* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/tokens/0x05e7.../42/remixes" \ -H "x-api-key: ml_live_YOUR_KEY"
Response
{
"data": [
{
"id": "rxo_01j...",
"remixContract": "0x06a3...",
"remixTokenId": "1",
"licenseType": "CC BY",
"commercial": true,
"derivatives": true,
"createdAt": "2026-03-23T10:00:00Z"
}
],
"meta": { "page": 1, "limit": 20, "total": 3 }
}/v1/remix-offersSubmit a custom remix offer for a token. If the token's license is not open (CC0/CC BY/CC BY-SA/CC BY-NC), the creator must approve before the requester can mint. Requires Clerk JWT.
Parameters
originalContractstring *Original NFT contract addressoriginalTokenIdstring *Original token IDlicenseTypestring *Requested license (e.g. CC BY-NC)commercialboolean *Commercial use requestedderivativesboolean *Derivatives allowedroyaltyPctnumberRoyalty percentage (0–100)proposedPricestringProposed payment as raw wei integer stringproposedCurrencystringToken address of proposed payment currencymessagestringOptional message to the creator* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/remix-offers" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Authorization: Bearer CLERK_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{
"originalContract": "0x05e7...",
"originalTokenId": "42",
"licenseType": "CC BY-NC",
"commercial": false,
"derivatives": true,
"royaltyPct": 10,
"message": "Would love to remix this for my EP cover"
}'Response
{
"data": {
"id": "rxo_01j...",
"status": "PENDING",
"originalContract": "0x05e7...",
"originalTokenId": "42",
"creatorAddress": "0x0591...",
"requesterAddress": "0x03d0...",
"licenseType": "CC BY-NC",
"commercial": false,
"derivatives": true,
"royaltyPct": 10,
"approvedCollection": null,
"remixContract": null,
"remixTokenId": null,
"orderHash": null,
"expiresAt": "2026-04-23T10:00:00Z",
"createdAt": "2026-03-23T10:00:00Z"
}
}/v1/remix-offers/autoSubmit an auto remix offer for a token with an open license (CC0, CC BY, CC BY-SA, CC BY-NC). Auto-approved immediately — no creator action needed. Requires Clerk JWT.
Parameters
originalContractstring *Original NFT contract addressoriginalTokenIdstring *Original token IDlicenseTypestring *Open license type (must be CC0, CC BY, CC BY-SA, or CC BY-NC)* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/remix-offers/auto" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Authorization: Bearer CLERK_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{ "originalContract": "0x05e7...", "originalTokenId": "7", "licenseType": "CC0" }'Response
{ "data": { "id": "rxo_01j...", "status": "AUTO_PENDING", ... } }/v1/remix-offers/self/confirmRecord a self-remix — the token owner remixing their own asset. Call after the remix has been minted on-chain. Requires Clerk JWT.
Parameters
originalContractstring *Original NFT contract addressoriginalTokenIdstring *Original token IDremixContractstring *Remix NFT contract addressremixTokenIdstring *Remix token IDlicenseTypestring *License type applied to the remixcommercialboolean *Commercial usederivativesboolean *Further derivatives allowedroyaltyPctnumberRoyalty percentage* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/remix-offers/self/confirm" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Authorization: Bearer CLERK_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{ "originalContract": "0x05e7...", "originalTokenId": "42", "remixContract": "0x06a3...", "remixTokenId": "1", "licenseType": "CC BY", "commercial": true, "derivatives": true }'Response
{ "data": { "id": "rxo_01j...", "status": "SELF_MINTED", ... } }/v1/remix-offersList remix offers for the authenticated user. Pass role=creator to see incoming offers (you are the original creator), or role=requester to see offers you made. Requires Clerk JWT.
Parameters
rolestring *"creator" or "requester"pagenumberPage (default: 1)limitnumberResults per page (default: 20)* required
cURL
curl "https://medialane-backend-production.up.railway.app/v1/remix-offers?role=creator" \ -H "x-api-key: ml_live_YOUR_KEY" \ -H "Authorization: Bearer CLERK_SESSION_JWT"
Response
{
"data": [ { "id": "rxo_01j...", "status": "PENDING", "requesterAddress": "0x03d0...", ... } ],
"meta": { "page": 1, "limit": 20, "total": 2 }
}/v1/remix-offers/:id/confirmCreator approves a pending remix offer and records the minted remix on-chain coordinates. Requires Clerk JWT — caller must be the creator of the original token.
Parameters
idstring *Remix offer ID (URL param)approvedCollectionstring *Collection contract where the remix will be mintedremixContractstring *Remix NFT contract address (usually same as approvedCollection)remixTokenIdstring *Minted remix token IDorderHashstringMarketplace order hash if a payment was arranged* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/remix-offers/rxo_01j.../confirm" \
-H "x-api-key: ml_live_YOUR_KEY" \
-H "Authorization: Bearer CLERK_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{ "approvedCollection": "0x06a3...", "remixContract": "0x06a3...", "remixTokenId": "1" }'Response
{ "data": { "id": "rxo_01j...", "status": "APPROVED", "remixContract": "0x06a3...", "remixTokenId": "1", ... } }/v1/remix-offers/:id/rejectCreator rejects a pending remix offer. Requires Clerk JWT — caller must be the creator of the original token.
Parameters
idstring *Remix offer ID (URL param)* required
cURL
curl -X POST "https://medialane-backend-production.up.railway.app/v1/remix-offers/rxo_01j.../reject" \ -H "x-api-key: ml_live_YOUR_KEY" \ -H "Authorization: Bearer CLERK_SESSION_JWT"
Response
{ "data": { "id": "rxo_01j...", "status": "REJECTED", ... } }Technical Details
SNIP-12 Domain
Medialane uses SNIP-12 for off-chain message signing. If you are building your own signer, use the following domain:
{
"name": "Medialane",
"version": "1",
"revision": "1"
}Address Normalization
The API normalizes all addresses server-side to 64-character lowercase hex strings (prefixed with 0x). You can pass any valid Starknet address format — short, long, or mixed-case — and the API will handle normalization automatically. The @medialane/sdk also normalizes addresses before every API call.