mdxui

Product Schema

Product Noun type definition and data structure

Product Schema

The Product Noun represents anything a business sells - from SaaS applications to APIs, datasets, and headless services. Every Product has a defined schema that determines its behavior, pricing, and rendering.


Product Type Definition

export interface Product {
  id: string
  name: string
  business?: string | Business
  type: 'SaaS' | 'API' | 'Data' | 'Headless'
  stripeProductId?: string | null
  description?: string | null
  isActive?: boolean | null

  // Categorization
  industries?: (string | Industry)[] | null
  productCategories?: (string | ProductCategory)[] | null

  // Features & Pricing
  features?: {
    docs?: (string | Feature)[]
    hasNextPage?: boolean
    totalDocs?: number
  }
  prices?: (string | Price)[] | null

  // Interface Types
  interfaces?: {
    hasAPI?: boolean
    hasRPC?: boolean
    hasMCP?: boolean
    hasSDK?: boolean
    hasCLI?: boolean
  }

  // Metadata
  updatedAt: string
  createdAt: string
}

Product Types

Each product type has different characteristics and business models:

TypeRevenue ModelInterfaceExample
SaaSMRR (subscription)Web app + APIFigma, Notion
APIUsage-basedREST/GraphQL/tRPCStripe API, OpenAI API
DataMRR + UsageAPI + downloadsClearbit, PeopleDataLabs
HeadlessUsage-basedAPI + MCP onlyAI services, automation tools

MDXLD Schema

Products can be defined declaratively in MDXLD:

---
$context: https://schema.org.ai
$type: Product
$id: https://acme.com/products/api
name: Acme API
type: API
business: https://acme.com
description: RESTful API for data enrichment
isActive: true

# Categorization
industries:
  - B2B SaaS
  - Data & Analytics
productCategories:
  - API Platform
  - Data Enrichment

# Interfaces
interfaces:
  hasAPI: true
  hasMCP: true
  hasSDK: true
  hasCLI: false

# Pricing
prices:
  - $id: price_basic
    amount: 0.001
    currency: USD
    type: usage
    interval: request
  - $id: price_pro
    amount: 0.0008
    currency: USD
    type: usage
    interval: request
    minVolume: 1000000
---

# {name}

{description}

## Features

<Features products={[$id]} />

## Pricing

<Pricing prices={prices} />

## Documentation

<APIReference product={$id} />

Schema Properties

Core Fields

FieldTypeRequiredDescription
idstringYesUnique identifier
namestringYesProduct display name
typeenumYesProduct type (SaaS, API, Data, Headless)
businessreferenceYesOwning business
descriptionstringNoProduct description
isActivebooleanNoWhether product is currently offered

Categorization

FieldTypeDescription
industriesIndustry[]Market verticals this product serves
productCategoriesProductCategory[]Product classification (CRM, Analytics, etc.)

Features & Pricing

FieldTypeDescription
featuresFeature[]Product capabilities and features
pricesPrice[]Pricing plans and tiers

Interface Types

Products expose different interface types:

InterfaceProtocolUse Case
APIREST/GraphQLProgrammatic access
RPCtRPC/gRPCType-safe remote calls
MCPModel Context ProtocolAI agent tools
SDKLanguage-specificClient libraries
CLICommand-lineTerminal interface

Feature

Individual product capabilities:

interface Feature {
  id: string
  name: string
  description?: string
  product: string | Product
  isEnterprise?: boolean
}

Price

Pricing structure for a product:

interface Price {
  id: string
  product: string | Product
  stripePriceId?: string
  amount: number
  currency: string
  type: 'subscription' | 'usage' | 'one_time'
  interval?: 'month' | 'year' | 'request' | 'seat'
  minVolume?: number
}

ProductCategory

Product classification:

interface ProductCategory {
  id: string
  name: string
  slug: string
  description?: string
  parent?: string | ProductCategory
}

Examples: CRM, Analytics Platform, Data Enrichment, Payment Gateway


Payload CMS Collection

Products are stored in the products Payload collection:

// Query via REST API
GET /api/products
GET /api/products/:id
POST /api/products
PATCH /api/products/:id
DELETE /api/products/:id

// Query params
?where[type][equals]=API
?where[isActive][equals]=true
?where[business][equals]=123
&limit=20&page=1

GraphQL

query {
  Products(where: { type: { equals: API } }) {
    docs {
      id
      name
      type
      business {
        name
      }
      features {
        docs {
          name
          description
        }
      }
      prices {
        amount
        currency
        type
      }
    }
  }
}

Auto-Wiring

Product components auto-wire to the Payload collection:

// Automatically queries products collection
<ProductList type="API" />
<ProductCard product={productId} />
<ProductPage product={productId} />

// Full product application
<ProductApp product={productId} />

Validation

Zod Schema

import { z } from 'zod'

export const ProductSchema = z.object({
  name: z.string().min(1),
  type: z.enum(['SaaS', 'API', 'Data', 'Headless']),
  business: z.string(),
  description: z.string().optional(),
  isActive: z.boolean().default(true),
  stripeProductId: z.string().optional(),
  industries: z.array(z.string()).optional(),
  productCategories: z.array(z.string()).optional(),
})

Type Guards

export function isSaaSProduct(product: Product): boolean {
  return product.type === 'SaaS'
}

export function hasAPI(product: Product): boolean {
  return product.interfaces?.hasAPI === true
}

export function isUsageBased(product: Product): boolean {
  return product.type === 'API' || product.type === 'Headless'
}

Business Logic

Revenue Model by Type

export function getRevenueModel(product: Product): string {
  switch (product.type) {
    case 'SaaS':
      return 'MRR (Monthly Recurring Revenue)'
    case 'API':
      return 'Usage-based (pay-per-call)'
    case 'Data':
      return 'MRR + Usage'
    case 'Headless':
      return 'Usage-based'
  }
}

Interface Requirements

export function getRequiredInterfaces(type: Product['type']): string[] {
  switch (type) {
    case 'SaaS':
      return ['Web App', 'API']
    case 'API':
      return ['REST API']
    case 'Data':
      return ['API', 'Downloads']
    case 'Headless':
      return ['API', 'MCP']
  }
}

Next Steps

On this page