Medialane
FeaturesIntegrateDocsConnect
Medialane

Creator capital markets. API and SDK access for assets, orders, licensing, drops, credentials, and real-time events.

Platform

  • Features
  • Integrate
  • Docs
  • API Reference
  • SDK

Community

  • Connect
  • Changelog
  • DAO ↗

Legal

  • Terms
  • Privacy

© 2026 Medialane. All rights reserved. · Powered by Starknet

    Documentation

    • Quick Start
    • Authentication
    • Credits & Billing
    • Error Codes
    • Agent Quickstart
    • SIWS Auth
    • Credit Billing (402)
    • Autonomous Topup
    • Orders
    • Collections
    • Minting
    • Tokens
    • Batch Tokens
    • Activities
    • Intents
    • Checkout Intent
    • Metadata
    • Search
    • Events
    • Comments
    • Counter-offers
    • Remix Licensing
    • Claims
    • Profiles
    • Portal
    • Webhooks
    • POP Protocol
    • Collection Drop
    • Health
    • Technical
    • Install
    • Configure
    • Minting
    • Marketplace
    • API Client
    • Comments
    • Counter-offers
    • Remix Licensing
    • POP Protocol
    • Collection Drop
    • Error Codes
    SDK

    @medialane/sdk

    Framework-agnostic TypeScript SDK for the Medialane API. Bundles a full REST client and on-chain marketplace helpers in one package.

    Install

    bash
    # bun
    bun add @medialane/sdk starknet
    
    # npm
    npm install @medialane/sdk starknet
    
    # yarn
    yarn add @medialane/sdk starknet

    Peer dependency: starknet@^6

    Configure

    Create a MedialaneClient with your network and API key.

    ts
    import { MedialaneClient } from "@medialane/sdk"
    
    const client = new MedialaneClient({
      network: "mainnet",        // "mainnet" | "sepolia"
      rpcUrl: "https://starknet-mainnet.g.alchemy.com/starknet/version/rpc/v0_7/YOUR_KEY",
      backendUrl: "https://medialane-backend-production.up.railway.app",
      apiKey: "ml_live_YOUR_KEY",
      // marketplaceContract — optional, defaults to mainnet contract
      // collectionContract  — optional, defaults to mainnet collection registry
      // Optional: configure retry for transient failures
      retryOptions: {
        maxAttempts: 3,      // default
        baseDelayMs: 300,    // default
        maxDelayMs: 5000,    // default
      },
    })

    The apiKey is sent as x-api-key on every request. Get your key at /account.

    Minting & Launchpad

    The SDK provides two ways to mint assets: direct on-chain calls (requires signer) and backend-orchestrated intents.

    Mint an asset into a collection

    ts
    // 1. Direct on-chain (client.marketplace)
    await client.marketplace.mint(account, {
      collectionId: "42",
      recipient: "0x0591...",
      tokenUri: "ipfs://...",
    })
    
    // 2. Via backend intent (client.api)
    // No SNIP-12 signing required for mint/create-collection intents
    const { intentId, calls } = await client.api.createMintIntent({
      owner: "0x0591...", // collection owner
      collectionId: "42",
      recipient: "0x0592...",
      tokenUri: "ipfs://...",
    })

    Register a new collection

    ts
    // 1. Direct on-chain
    await client.marketplace.createCollection(account, {
      name: "My Collection",
      symbol: "MYC",
      baseUri: "ipfs://...",
    })
    
    // 2. Via backend intent
    const { intentId, calls } = await client.api.createCollectionIntent({
      owner: "0x0591...",
      name: "My Collection",
      symbol: "MYC",
      baseUri: "ipfs://...",
    })

    Marketplace (on-chain)

    client.marketplace provides typed wrappers for direct contract calls via starknet.js.

    ts
    // Get order details directly from the contract
    const order = await client.marketplace.getOrderDetails("0x04f7a1...")
    
    // Get the current nonce for signing
    const nonce = await client.marketplace.getNonce("0x0591...")

    API Client (REST)

    client.api mirrors the full REST API surface.

    List open orders

    ts
    const orders = await client.api.getOrders({ status: "ACTIVE", limit: 20 })
    
    console.log(orders.data[0].orderHash, orders.data[0].price)

    Get a token with metadata

    ts
    const token = await client.api.getToken("0x05e7...", "42")
    
    console.log(token.data.metadata?.name)

    Get collections by owner

    ts
    // Fetch collections owned by a wallet address
    // Addresses are normalized automatically — pass any valid Starknet format
    const result = await client.api.getCollectionsByOwner("0x0591...")
    result.data.forEach((col) => {
      console.log(col.name, col.collectionId) // collectionId = on-chain registry ID
    })

    Create a listing intent

    ts
    import { toSignatureArray } from "@medialane/sdk"
    
    // 1. Create the intent — get typed data back
    const intent = await client.api.createListingIntent({
      nftContract: "0x05e7...",
      tokenId: "42",
      price: "500000",
      currency: "USDC",
      offerer: walletAddress,
      endTime: Math.floor(Date.now() / 1000) + 86400 * 30,
    })
    
    // 2. Sign with starknet.js
    import { Account } from "starknet"
    const account = new Account(provider, walletAddress, privateKey)
    const signature = await account.signMessage(intent.data.typedData)
    
    // 3. Submit the signature
    await client.api.submitIntentSignature(intent.data.id, toSignatureArray(signature))

    Search

    ts
    const results = await client.api.search("genesis", 10)
    results.data.tokens.forEach((t) => console.log(t.metadata?.name))
    results.data.collections.forEach((c) => console.log(c.name))

    Portal — manage keys

    ts
    // List your API keys
    const keys = await client.api.getApiKeys()
    
    // Create a new key
    const newKey = await client.api.createApiKey("Agent Key")
    console.log(newKey.data.key) // shown once — save it!
    
    // Get usage
    const usage = await client.api.getUsage()

    On-chain Comments

    ts
    // Fetch permanent on-chain comments for a token
    const result = await client.api.getTokenComments("0x05e7...", "42", { limit: 20 })
    result.data.forEach((c) => {
      console.log(c.author, c.content, c.postedAt)
    })

    Counter-offers

    ts
    // Seller creates a counter-offer in response to a buyer's bid
    const intent = await client.api.createCounterOfferIntent(
      {
        sellerAddress: "0x0591...",
        originalOrderHash: "0x04f7a1...",
        counterPrice: "750000",       // raw wei
        durationSeconds: 86400,       // 1 day
        message: "Best I can do!",
      },
      siwsToken
    )
    
    // Buyer fetches counter-offers for their bid
    const counters = await client.api.getCounterOffers({
      originalOrderHash: "0x04f7a1...",
    })
    console.log(counters.data[0].price)
    
    // Buyer accepts by fulfilling the counter-offer (it is a standard listing)
    await client.api.createFulfillIntent({ fulfiller: buyerAddress, orderHash: counters.data[0].orderHash })

    Remix Licensing

    ts
    import { OPEN_LICENSES } from "@medialane/sdk"
    
    // Check if a license is open (auto-approved remix)
    console.log(OPEN_LICENSES) // ["CC0", "CC BY", "CC BY-SA", "CC BY-NC"]
    
    // Request permission to remix a token (custom offer, SIWS session JWT required)
    const offer = await client.api.submitRemixOffer(
      {
        originalContract: "0x05e7...",
        originalTokenId: "42",
        licenseType: "CC BY-NC",
        commercial: false,
        derivatives: true,
        royaltyPct: 10,
        message: "Would love to remix this for my EP cover",
      },
      siwsToken
    )
    
    // Open-license tokens are auto-approved
    const autoOffer = await client.api.submitAutoRemixOffer(
      { originalContract: "0x05e7...", originalTokenId: "7", licenseType: "CC0" },
      siwsToken
    )
    
    // Creator approves a pending offer
    await client.api.confirmRemixOffer(offer.data.id, {
      approvedCollection: "0x06a3...",
      remixContract: "0x06a3...",
      remixTokenId: "1",
    }, siwsToken)
    
    // Creator rejects an offer
    await client.api.rejectRemixOffer(offer.data.id, siwsToken)
    
    // Owner records their own self-remix after minting
    await client.api.confirmSelfRemix(
      {
        originalContract: "0x05e7...",
        originalTokenId: "42",
        remixContract: "0x06a3...",
        remixTokenId: "1",
        licenseType: "CC BY",
        commercial: true,
        derivatives: true,
      },
      siwsToken
    )
    
    // List incoming / outgoing offers
    const incoming = await client.api.getRemixOffers({ role: "creator" }, siwsToken)
    const outgoing = await client.api.getRemixOffers({ role: "requester" }, siwsToken)
    
    // Get public remixes for a token (no auth needed)
    const remixes = await client.api.getTokenRemixes("0x05e7...", "42")
    remixes.data.forEach((r) => console.log(r.remixContract, r.remixTokenId, r.licenseType))

    CollectionSort — typed sort options

    ts
    import type { CollectionSort } from "@medialane/sdk"
    
    // "recent" | "supply" | "floor" | "volume" | "name"
    const sort: CollectionSort = "floor"
    await client.api.getCollections(1, 20, true, sort)

    POP Protocol (Proof of Participation)

    POP collections are event-based claim drops — conferences, workshops, hackathons, bootcamps. Each collection has one claimable token per eligible wallet. Use client.services.pop for on-chain interactions and client.api for eligibility checks.

    Check eligibility and claim

    ts
    // Check if a wallet is eligible to claim from a POP collection
    const status = await client.api.getPopEligibility(
      "0x00b32c...",   // POP collection address
      "0x0591...",     // wallet address
    )
    // status: { isEligible: boolean; hasClaimed: boolean; tokenId: string | null }
    
    if (status.isEligible && !status.hasClaimed) {
      // Claim on-chain (requires starknet.js AccountInterface)
      const { txHash } = await client.services.pop.claim(account, "0x00b32c...")
      console.log("Claimed:", txHash)
    }

    Batch eligibility check

    ts
    // Check up to 100 wallets in one request
    const results = await client.api.getPopEligibilityBatch(
      "0x00b32c...",          // POP collection address
      ["0x0591...", "0x06a3..."],
    )
    // results: Array<{ wallet, isEligible, hasClaimed, tokenId }>
    results.forEach((r) => console.log(r.wallet, r.isEligible))

    List POP collections

    ts
    // Fetch all POP Protocol collections
    const pops = await client.api.getPopCollections({ page: 1, limit: 20, sort: "recent" })
    pops.data.forEach((col) => console.log(col.name, col.source)) // source: "POP_PROTOCOL"

    Admin — mint and allowlist

    ts
    // Gift a token to a specific wallet (bypass eligibility check)
    await client.services.pop.adminMint(account, {
      collection: "0x00b32c...",
      recipient: "0x0591...",
      customUri: "ipfs://...",  // optional override
    })
    
    // Add a single wallet to the allowlist
    await client.services.pop.addToAllowlist(account, {
      collection: "0x00b32c...",
      address: "0x0591...",
    })
    
    // Add up to 200 wallets per tx
    await client.services.pop.batchAddToAllowlist(account, {
      collection: "0x00b32c...",
      addresses: ["0x0591...", "0x06a3...", /* ... */],
    })

    Deploy a new POP collection

    ts
    import type { CreatePopCollectionParams } from "@medialane/sdk"
    
    const params: CreatePopCollectionParams = {
      name: "ETHDenver 2026",
      symbol: "ETHDEN26",
      baseUri: "ipfs://...",
      claimEndTime: Math.floor(Date.now() / 1000) + 86400 * 7,  // 7 days
      eventType: "Conference",  // "Conference" | "Bootcamp" | "Workshop" | "Hackathon" | "Meetup" | "Course" | "Other"
    }
    const { txHash } = await client.services.pop.createCollection(account, params)
    console.log("Deployed:", txHash)

    Collection Drop

    Collection Drops are public minting campaigns with configurable claim conditions — price, supply cap, time window, and per-wallet limits. Use client.services.drop for on-chain interactions and client.api for status queries.

    Claim (public mint)

    ts
    // Check mint status for a wallet before claiming
    const status = await client.api.getDropMintStatus(
      "0x03587f...",   // Drop collection address
      "0x0591...",     // wallet address
    )
    // status: { mintedByWallet: number; totalMinted: number }
    
    // Claim 1 token (default)
    const { txHash } = await client.services.drop.claim(account, "0x03587f...")
    
    // Claim multiple tokens
    await client.services.drop.claim(account, "0x03587f...", 3)

    List Drop collections

    ts
    const drops = await client.api.getDropCollections({ page: 1, limit: 20, sort: "recent" })
    drops.data.forEach((col) => console.log(col.name, col.source)) // source: "COLLECTION_DROP"

    Deploy a new Drop

    ts
    import type { CreateDropParams, ClaimConditions } from "@medialane/sdk"
    
    const conditions: ClaimConditions = {
      startTime: Math.floor(Date.now() / 1000),          // open now
      endTime: Math.floor(Date.now() / 1000) + 86400 * 30, // closes in 30 days
      price: BigInt("1000000"),                           // 1 USDC (6 decimals). 0 = free mint
      paymentToken: "0x033068f6...",                      // USDC contract
      maxQuantityPerWallet: BigInt(5),                    // max 5 per wallet. 0 = unlimited
    }
    
    const params: CreateDropParams = {
      name: "Genesis Drop",
      symbol: "GEN",
      baseUri: "ipfs://...",
      maxSupply: BigInt(1000),
      initialConditions: conditions,
    }
    const { txHash } = await client.services.drop.createDrop(account, params)
    console.log("Drop deployed:", txHash)

    Manage an active Drop

    ts
    // Update claim conditions (price, time window, wallet limits)
    await client.services.drop.setClaimConditions(account, {
      collection: "0x03587f...",
      conditions: { startTime: 0, endTime: 0, price: 0n, paymentToken: "0x0", maxQuantityPerWallet: 0n },
    })
    
    // Pause or unpause minting
    await client.services.drop.setPaused(account, { collection: "0x03587f...", paused: true })
    
    // Enable allowlist gate
    await client.services.drop.setAllowlistEnabled(account, { collection: "0x03587f...", enabled: true })
    await client.services.drop.batchAddToAllowlist(account, {
      collection: "0x03587f...",
      addresses: ["0x0591...", "0x06a3..."],
    })
    
    // Withdraw ERC-20 proceeds
    await client.services.drop.withdrawPayments(account, { collection: "0x03587f..." })

    Error Handling

    The SDK throws MedialaneError for marketplace issues and MedialaneApiError for REST API failures. Both carry a typed .code field from the MedialaneErrorCode union.

    ts
    import { MedialaneError, MedialaneApiError } from "@medialane/sdk"
    
    try {
      await client.marketplace.mint(account, params)
    } catch (err) {
      if (err instanceof MedialaneError) {
        console.error(err.code, err.message) // e.g. "TRANSACTION_FAILED"
      }
      if (err instanceof MedialaneApiError) {
        console.error(err.code, err.status, err.message) // e.g. "TOKEN_NOT_FOUND", 404
      }
    }

    Error Codes

    All errors expose a MedialaneErrorCode typed union:

    ts
    type MedialaneErrorCode =
      | "TOKEN_NOT_FOUND"
      | "COLLECTION_NOT_FOUND"
      | "ORDER_NOT_FOUND"
      | "INTENT_NOT_FOUND"
      | "INTENT_EXPIRED"
      | "RATE_LIMITED"
      | "NETWORK_NOT_SUPPORTED"
      | "APPROVAL_FAILED"
      | "TRANSACTION_FAILED"
      | "INVALID_PARAMS"
      | "UNAUTHORIZED"
      | "UNKNOWN"
    CodeTrigger
    TOKEN_NOT_FOUND404 response or missing token
    COLLECTION_NOT_FOUND404 on collection lookup
    ORDER_NOT_FOUND404 on order lookup
    INTENT_NOT_FOUND404 on intent lookup
    INTENT_EXPIRED410 response — intent TTL exceeded
    RATE_LIMITED429 response — too many requests
    NETWORK_NOT_SUPPORTEDSepolia selected with no contract addresses
    APPROVAL_FAILEDNFT approval missing before listing
    TRANSACTION_FAILEDOn-chain call reverted
    INVALID_PARAMS400 response — bad request parameters
    UNAUTHORIZED401/403 — missing or invalid API key
    UNKNOWNUnexpected errors

    Note: 4xx errors are not retried automatically. Only transient network and 5xx errors trigger the retry logic configured via retryOptions.

    Full API reference — all REST endpoints, parameters, and response schemas are documented in the API Reference.

    Use Case Examples

    Common patterns you can build with the SDK:

    Fetch a creator's portfolio

    ts
    const portfolio = await client.api.getTokensByOwner({
      owner: "0x05f9...",
      limit: 20,
    });
    // portfolio.data → array of token objects with metadata, license terms, remixCount

    List open-license assets for remix

    ts
    import { OPEN_LICENSES } from "@medialane/sdk";
    
    const openAssets = await client.api.getTokens({
      licenseType: OPEN_LICENSES, // ["CC0", "CC BY", "CC BY-SA", "CC BY-NC"]
    });

    Submit a trade intent (no private key exposure)

    ts
    const intent = await client.api.createListingIntent({
      contractAddress: "0x04a...",
      tokenId: "42",
      price: "0.05",       // in ETH
      currency: "ETH",
      duration: 86400,     // seconds
    });
    
    // sign off-chain, submit on-chain
    const sig = await account.signMessage(intent.typedData);
    await client.api.submitOrder({ ...intent, signature: sig });

    Stream on-chain activity

    ts
    const activity = await client.api.getActivities({
      eventType: "TRANSFER",   // TRANSFER | ORDER_CREATED | ORDER_FULFILLED | ORDER_CANCELLED
      contractAddress: "0x04a...",
      limit: 50,
    });

    Built with the SDK

    Both Medialane consumer apps use the same SDK you're integrating:

    medialane.io

    Creator Launchpad

    Collections, Orders, Minting, Remix Licensing, POP, Collection Drop, On-chain Comments. Invisible wallet UX via ChipiPay.

    dapp.medialane.io

    Permissionless dApp

    Activities, Trade Intents, Asset Metadata. Direct starknet.js reads — no backend dependency for browsing.

    Full SDK documentation

    Complete method reference, type definitions, and advanced usage are on docs.medialane.io/docs/sdk.