ModelPad API Reference Home Get API keys
ModelPad

API Reference

One API behind the content, the storefront and the customer records — so a publishing pipeline, a catalogue sync and a CRM export all talk to the same place.

Introduction

The ModelPad API is a REST API over JSON. The web application is a client of it, so anything the app can do is available to you: publishing content, managing a product catalogue, reading orders, and syncing contacts into whatever else you run.

All requests go to https://modelpad.app over HTTPS. Request and response bodies are JSON; there is no form encoding except for file uploads, which are multipart/form-data.

Resources are addressed by UUID. Creating a record is a PUT to its id, which means you choose the id client-side and retries are idempotent — sending the same PUT twice leaves one record, not two.

bash
curl https://modelpad.app/api/notes \
  -H "Authorization: Bearer $MODELPAD_TOKEN"

Authentication

Authenticate with an API key sent as a bearer token. Keys are prefixed mp_ and are shown once, when created — only a hash is stored, so a lost key is replaced rather than recovered.

Create one from the CLI with modelpad auth login, which walks you through the browser and stores the key locally, or from Account → API keys in the app. List and revoke keys at /api/keys.

Requests without a valid credential get 401. Every request is scoped to the account that owns the key; there is no cross-account access, and ids from another account read as though they do not exist.

Keep keys server-side. A key carries the full rights of the account. Do not ship one in a browser bundle or a mobile app — put your own endpoint in front of it.
bash
curl https://modelpad.app/api/contacts \
  -H "Authorization: Bearer mp_live_a1b2c3..."

# The CLI reads the same credential
export MODELPAD_TOKEN=mp_live_a1b2c3...
modelpad contacts list

Errors

Conventional HTTP status codes are used. A failure returns a JSON body with a single error key holding a human-readable message; there is no machine-readable error code, so branch on the status.

StatusMeaning
200Success.
400Malformed JSON, or a field failed validation.
401Missing or invalid credential.
403Authenticated, but not permitted — an admin-only operation, or content you have not purchased.
404No such record for this account.
413Upload exceeded the size limit for its media type.
500Something failed our side.
json
{
  "error": "Video uploads are restricted to admin accounts"
}

Pagination

List endpoints take page (1-based) and limit, and return a pagination object alongside the records. Limits are capped per endpoint — asking for more returns the cap rather than an error.

The array key is named for the resource rather than a generic data: notes come back under notes, contacts under contacts, media under media_items. Read the key you asked for.

Prefer has_more over comparing counts yourself; records created while you page will otherwise shift the arithmetic.

json
{
  "notes": [ { "id": "…", "title": "…" } ],
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 213,
    "has_more": true
  }
}

Conventions

Creating and updating. Most resources are upserted with PUT /api/<resource>/:id where you supply the UUID. The write replaces the whole record, so read it first and merge, or fields you omit are cleared. The CLI does that merge for you.

Timestamps are RFC 3339 in UTC (created_at, updated_at).

Money is integer cents on orders and tiers. Product prices are decimal strings.

Note bodies are Lexical editor JSON, not HTML or markdown. Build them by hand only if you must — the CLI converts markdown in both directions, which is usually what you want.

Deleting. References between records live inside opaque block JSON that no endpoint indexes, so nothing warns you that a page still points at the product you are about to remove. Run modelpad usage <id-or-slug> first.
bash
# Read, merge, write — the safe update pattern
curl https://modelpad.app/api/contacts/$ID \
  -H "Authorization: Bearer $MODELPAD_TOKEN" > contact.json

jq '.contact + {company: "Northwind"}' contact.json > updated.json

curl -X PUT https://modelpad.app/api/contacts/$ID \
  -H "Authorization: Bearer $MODELPAD_TOKEN" \
  -H "Content-Type: application/json" \
  --data @updated.json

Command line

Every endpoint is reachable from the modelpad CLI, which handles authentication, the read-merge-write dance on updates, and markdown conversion for note bodies. It prints JSON on stdout and human-facing text on stderr, so it pipes cleanly into jq.

Anything not modelled as a command is still reachable with modelpad api <METHOD> <path>, using the same credential.

bash
modelpad auth login
modelpad notes create --title "Q3 benchmark" --markdown report.md
modelpad notes list --query search=benchmark | jq '.notes[].title'
modelpad contacts list --query list=pricing-enquiries

# Escape hatch for anything else
modelpad api GET /api/dashboard/summary
Resource

Notes

Articles and documents. Notes are the unit of long-form content: they can be nested into a tree, published to a public URL, and gated behind an access rule.

List notes

GET /api/notes

Returns notes owned by the account, newest first.

ParameterDescription
search
string
optional
Full-text match against title and body.
parent
uuid
optional
Only direct children of this note.
page
integer
optional
1-based page number. Defaults to 1.
limit
integer
optional
Records per page. Defaults to 50.
Request
curl https://modelpad.app/api/notes?limit=2 \
  -H "Authorization: Bearer $MODELPAD_TOKEN"
CLI
modelpad notes list --query limit=2
Response
{
  "notes": [
    {
      "id": "8f14e45f-…",
      "title": "Procurement checklist",
      "slug": "procurement-checklist",
      "is_shared": true,
      "parent": null,
      "tags": ["guides"],
      "updated_at": "2026-08-02T09:14:00Z"
    }
  ],
  "pagination": { "page": 1, "limit": 2, "total": 37, "has_more": true }
}

Retrieve a note

GET /api/notes/:id

Returns one note including its full body as Lexical JSON.

Request
curl https://modelpad.app/api/notes/8f14e45f-… \
  -H "Authorization: Bearer $MODELPAD_TOKEN"
CLI
modelpad notes get 8f14e45f-…
modelpad notes get 8f14e45f-… --as-markdown

Create or update a note

PUT /api/notes/:id

Upserts the note at that id. The write replaces the record, so send the whole thing.

ParameterDescription
title
string
Required
Display title.
body
object
optional
Lexical document JSON.
slug
string
optional
URL segment for the public page. Derived from the title when omitted.
parent
uuid
optional
Parent note, for nesting.
tags
array
optional
Slash-separated tag paths, e.g. guides/procurement.
Request
curl -X PUT https://modelpad.app/api/notes/8f14e45f-… \
  -H "Authorization: Bearer $MODELPAD_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title":"Procurement checklist","tags":["guides"]}'
CLI
modelpad notes create --title "Procurement checklist" --markdown checklist.md

Publish or unpublish

PATCH /api/notes/:id/share

Toggles whether the note is served at its public URL. Publishing is what makes it indexable.

ParameterDescription
is_shared
boolean
Required
True to publish.
Request
curl -X PATCH https://modelpad.app/api/notes/8f14e45f-…/share \
  -H "Authorization: Bearer $MODELPAD_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"is_shared": true}'
CLI
modelpad notes share 8f14e45f-… --set is_shared=true

List child notes

GET /api/notes/:id/children

Direct children only. Use /descendants for the whole subtree.

CLI
modelpad notes children 8f14e45f-…

Move a note

PATCH /api/notes/:id/parent

Reparents the note. Pass a null parent to move it to the top level.

CLI
modelpad notes move 8f14e45f-… --set parent=<uuid>

Delete a note

DELETE /api/notes/:id

Permanent. Check what references it first.

CLI
modelpad usage 8f14e45f-… && modelpad notes delete 8f14e45f-…
Resource

Pages

Landing pages, the account home page, and the template pages that render every note or product. A page is an ordered array of blocks, each naming a block type and its props.

List pages

GET /api/landing-pages

All landing pages except the home page, which has its own endpoint.

CLI
modelpad pages list
Response
{
  "landing_pages": [
    {
      "id": "3c6e0b8a-…",
      "title": "Enterprise plan",
      "slug": "enterprise",
      "published": true,
      "meta_description": "Volume pricing and onboarding."
    }
  ],
  "pagination": { "page": 1, "limit": 50, "total": 6, "has_more": false }
}

Retrieve the home page

GET /api/landing-pages/home

The page served at the account root. Provisioned on first access, so this never 404s.

CLI
modelpad pages home

Create or update a page

PUT /api/landing-pages/:id

Upserts the page. Blocks are replaced wholesale — read, modify, write back.

ParameterDescription
title
string
Required
Page title, used in the <title> tag.
slug
string
optional
URL segment under /pages/.
blocks
array
optional
Ordered blocks. Each has instance_id, section_type and props.
meta_description
string
optional
Meta description for search results.
published
boolean
optional
Whether the public URL serves it.
Request
curl -X PUT https://modelpad.app/api/landing-pages/3c6e0b8a-… \
  -H "Authorization: Bearer $MODELPAD_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Enterprise plan",
    "slug": "enterprise",
    "published": true,
    "blocks": [
      { "instance_id": "hero-1", "section_type": "hero",
        "props": { "heading": "Built for volume" } }
    ]
  }'
CLI
modelpad pages update 3c6e0b8a-… --set 'blocks=[{"section_type":"hero","props":{}}]'

Render blocks without saving

POST /api/landing-pages/preview

Renders an arbitrary block array to HTML. This is what the builder's preview pane calls; it returns a fragment, not a full document.

CLI
modelpad api POST /api/landing-pages/preview --data '{"blocks":[]}'

Make a page the home page

POST /api/landing-pages/:id/set-home

Promotes this page to the account root. The previous home page becomes an ordinary page.

CLI
modelpad pages set-home 3c6e0b8a-…
Resource

Block types

The building blocks pages are made of. Each block type carries a prop schema, which drives the editor's form, and a Go template that renders it. System types are read-only; duplicate one to get an editable copy.

List block types

GET /api/section-types

System types plus any this account owns.

CLI
modelpad blocks list

Create or update a block type

PUT /api/section-types/:id

Defines a reusable block. The template is Go html/template with the block's props in scope.

ParameterDescription
slug
string
Required
Stable identifier referenced by page blocks.
name
string
Required
Display name in the block picker.
category
string
Required
Grouping in the picker.
prop_schema
array
Required
Prop definitions: key, label, type.
go_template
string
Required
Markup for the block.
CLI
modelpad blocks update <id> --file block.json

Entity fields available to templates

GET /api/entity-fields

What a block template can read off a linked record — useful when authoring prop schemas that point at products, notes or media.

CLI
modelpad blocks fields
Resource

Media

Images, video and files. Uploading is two steps: create the record, then post the bytes to it. Images get responsive variants generated automatically.

List media

GET /api/media

Note the array key is media_items.

ParameterDescription
search
string
optional
Match against name and description.
page
integer
optional
1-based page number.
limit
integer
optional
Records per page, max 200.
CLI
modelpad media list
Response
{
  "media_items": [
    {
      "id": "b1946ac9-…",
      "name": "warehouse.jpg",
      "file_url": "https://cdn.modelpad.app/media/…/warehouse.jpg",
      "file_type": "image/jpeg",
      "file_size": 284119,
      "thumbnails": {
        "thumb":  { "url": "…", "width": 150,  "height": 100 },
        "medium": { "url": "…", "width": 800,  "height": 533 }
      }
    }
  ],
  "pagination": { "page": 1, "limit": 50, "total": 12, "has_more": false }
}

Create a media record

PUT /api/media/:id

Step one of an upload: reserve the id and set the name. The record exists with no file until you upload one.

CLI
modelpad media create --set id=<uuid> --set name=warehouse.jpg

Upload the file

POST /api/media/:id/upload

Step two: multipart/form-data with the bytes in a field named file. Images get thumbnail variants generated synchronously; other types are stored as-is.

ParameterDescription
file
file
Required
The file, as multipart/form-data.
Request
curl -X POST https://modelpad.app/api/media/b1946ac9-…/upload \
  -H "Authorization: Bearer $MODELPAD_TOKEN" \
  -F "[email protected]"
Response
{
  "media": {
    "id": "b1946ac9-…",
    "file_url": "https://cdn.modelpad.app/media/…/warehouse.jpg",
    "file_type": "image/jpeg",
    "file_size": 284119,
    "thumbnails": { "thumb": { "url": "…", "width": 150, "height": 100 } }
  }
}

Regenerate thumbnails

POST /api/media/:id/regenerate-thumbnails

Re-derives image variants from the stored original. Rejects non-images with 400.

CLI
modelpad media regenerate-thumbnails b1946ac9-…
Resource

Products

Everything you sell: physical goods, digital downloads, and access passes. Products carry taxonomy values so catalogues can be filtered.

List products

GET /api/products
CLI
modelpad products list
Response
{
  "products": [
    {
      "id": "d3d94468-…",
      "name": "Compliance Toolkit",
      "slug": "compliance-toolkit",
      "price": 149,
      "product_type": "digital",
      "published": true
    }
  ],
  "pagination": { "page": 1, "limit": 50, "total": 9, "has_more": false }
}

Create or update a product

PUT /api/products/:id
ParameterDescription
name
string
Required
Product name.
price
number
Required
Price as a number, e.g. 149.00.
description
string
optional
Shown on the product page and in meta tags.
product_type
string
optional
digital or physical.
slug
string
optional
URL segment.
published
boolean
optional
Whether the public page serves it.
CLI
modelpad products create --name "Compliance Toolkit" --set price=149.00

List attached files

GET /api/products/:id/downloads

The media records a buyer receives after purchase.

CLI
modelpad products downloads d3d94468-…

Attach a file

POST /api/products/:id/downloads

Links a media record to the product as a deliverable. Buyers get signed, expiring links.

ParameterDescription
media_id
uuid
Required
The media record to deliver.
CLI
modelpad products add-download d3d94468-… --set media_id=<uuid>

Set taxonomy values

PUT /api/products/:id/taxonomy

Assigns facet values so the product appears under the right catalogue filters.

CLI
modelpad products set-taxonomy d3d94468-… --file values.json
Resource

Contacts

People. Created by hand, by a lead form on a page, or through the API. Each contact accumulates the submissions, orders and notes attached to them.

List contacts

GET /api/contacts
ParameterDescription
search
string
optional
Match against name, email and company.
list
string
optional
Only contacts on this list, by slug.
page
integer
optional
1-based page number.
limit
integer
optional
Records per page.
Request
curl "https://modelpad.app/api/contacts?list=pricing-enquiries" \
  -H "Authorization: Bearer $MODELPAD_TOKEN"
CLI
modelpad contacts list --query list=pricing-enquiries
Response
{
  "contacts": [
    {
      "id": "c9f0f895-…",
      "name": "Dana Whitfield",
      "email": "[email protected]",
      "company": "Northwind Logistics",
      "title": "Head of Operations",
      "created_at": "2026-07-30T11:02:14Z"
    }
  ],
  "pagination": { "page": 1, "limit": 50, "total": 148, "has_more": true }
}

Create or update a contact

PUT /api/contacts/:id
ParameterDescription
name
string
Required
Full name.
email
string
optional
Email address.
phone
string
optional
Phone number.
company
string
optional
Organisation.
title
string
optional
Job title.
website
string
optional
URL.
notes
string
optional
Freeform internal notes.
Request
curl -X PUT https://modelpad.app/api/contacts/c9f0f895-… \
  -H "Authorization: Bearer $MODELPAD_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Dana Whitfield","email":"[email protected]"}'
CLI
modelpad contacts create --name "Dana Whitfield" --set [email protected]

List interactions

GET /api/contact-interactions

Form submissions and other recorded touches. Filter to one person with contact_id.

ParameterDescription
contact_id
uuid
optional
Restrict to one contact.
CLI
modelpad contacts interactions c9f0f895-…

List transactions

GET /api/contact-transactions

What this contact has paid you — useful for building an account view.

CLI
modelpad api GET /api/contact-transactions --query contact_id=c9f0f895-…

Add an internal note

POST /api/contact-notes

Freeform note attached to a contact. Never shown publicly.

CLI
modelpad api POST /api/contact-notes --set contact_id=<uuid> --set body="Asked about volume pricing"
Resource

Contact lists

Segments. A lead form block names a list by slug, and every submission through that form joins it — which is how a page becomes a source of qualified contacts.

List contact lists

GET /api/contact-lists

Includes a member count per list.

CLI
modelpad lists list
Response
{
  "contact_lists": [
    {
      "id": "6c8349cc-…",
      "name": "Pricing enquiries",
      "slug": "pricing-enquiries",
      "member_count": 148
    }
  ]
}

Create or update a list

PUT /api/contact-lists/:id
ParameterDescription
name
string
Required
Display name. The slug derives from it when not given.
slug
string
optional
What a lead form block references.
CLI
modelpad lists create --name "Pricing enquiries"

List members

GET /api/contact-lists/:id/members
CLI
modelpad lists members pricing-enquiries

Add a contact to a list

POST /api/contact-lists/:id/members
ParameterDescription
contact_id
uuid
Required
The contact to add.
CLI
modelpad lists add pricing-enquiries --set contact_id=<uuid>

Remove a contact from a list

DELETE /api/contact-lists/:id/members/:contactId

Removes the membership. The contact itself is untouched.

Resource

Orders

Purchases, created by checkout rather than by you. Read-only apart from fulfilment status.

List orders

GET /api/orders
ParameterDescription
status
string
optional
Filter by fulfilment status.
page
integer
optional
1-based page number.
limit
integer
optional
Records per page.
CLI
modelpad orders list
Response
{
  "orders": [
    {
      "id": "a87ff679-…",
      "email": "[email protected]",
      "total_cents": 14900,
      "currency": "usd",
      "status": "paid",
      "created_at": "2026-08-01T16:41:09Z"
    }
  ],
  "pagination": { "page": 1, "limit": 50, "total": 63, "has_more": true }
}

Update fulfilment status

PATCH /api/orders/:id/status

For physical goods — mark an order shipped once it leaves.

ParameterDescription
status
string
Required
New fulfilment status.
CLI
modelpad api PATCH /api/orders/a87ff679-…/status --set status=shipped

Revenue summary

GET /api/dashboard/summary

Aggregate revenue, subscriber and order figures behind the seller dashboard.

CLI
modelpad api GET /api/dashboard/summary
Resource

Experiments

Block-level A/B tests. An experiment attaches to one block on one page and splits traffic between weighted variants, counting impressions and conversions.

List experiments

GET /api/experiments
ParameterDescription
status
string
optional
e.g. running, stopped.
CLI
modelpad experiments list --query status=running

Retrieve results

GET /api/experiments/:id/stats

Impressions and conversions per variant. Conversions count against the same records the rest of the platform uses, so a result is expressed in orders rather than clicks.

CLI
modelpad experiments stats <id>
Response
{
  "stats": [
    { "variant_id": "control", "impressions": 4120, "conversions": 96 },
    { "variant_id": "b",       "impressions": 4088, "conversions": 131 }
  ]
}

List variants

GET /api/experiments/:id/variants
CLI
modelpad experiments variants <id>
Resource

Access & tiers

Who can see what. An access rule binds an item to a way of reaching it — free, registration, one-time purchase, or a subscription tier. An item can carry several.

List access rules

GET /api/access-rules
CLI
modelpad access-rules list

Create or update an access rule

PUT /api/access-rules/:id
ParameterDescription
item_id
uuid
Required
The note, page or product being gated.
item_type
string
Required
notes, landing_pages or products.
rule_type
string
Required
How access is granted: free, registered, purchase or tier.
tier_id
uuid
optional
Required when rule_type is tier.
CLI
modelpad access-rules create --file rule.json

List subscription tiers

GET /api/tiers
CLI
modelpad tiers list

List collections

GET /api/collections

Bundles of items sold or granted together.

CLI
modelpad collections list
Resource

Taxonomy

Facets and their values — the attributes buyers filter a catalogue by. Define the facet once, then assign values to products.

List facets

GET /api/taxonomy/facets
CLI
modelpad facets list

Create or update a facet

PUT /api/taxonomy/facets/:id
ParameterDescription
name
string
Required
Display name, e.g. Material.
slug
string
optional
Query-string key on catalogue URLs.
CLI
modelpad facets create --name Material

Reorder facet values

PUT /api/taxonomy/facets/:id/values/order

Sets the order values appear in filter UI.

Resource

Search, keys & the rest

Cross-cutting endpoints, and the ones that manage the credential you are using.

Search across everything

GET /api/search/entities

One query over notes, pages, products, contacts, media and more. This is what the app's command palette calls.

ParameterDescription
q
string
Required
Search text.
limit
integer
optional
Maximum results.
Request
curl "https://modelpad.app/api/search/entities?q=procurement&limit=5" \
  -H "Authorization: Bearer $MODELPAD_TOKEN"
CLI
modelpad search procurement
Response
{
  "results": [
    { "id": "8f14e45f-…", "title": "Procurement checklist",
      "type": "notes", "subtitle": "guides" }
  ]
}

The current account

GET /api/auth/me

Resolves the credential to an account. Handy as a health check for an integration.

CLI
modelpad whoami
Response
{
  "user": {
    "id": "0cc175b9-…",
    "email": "[email protected]",
    "account_name": "northwind",
    "is_admin": false
  }
}

List API keys

GET /api/keys

Metadata only — the key itself is shown once, at creation.

CLI
modelpad keys list

Create an API key

POST /api/keys

Returns the raw key exactly once. Store it before you close the response.

ParameterDescription
label
string
optional
How the key shows up in the list.
CLI
modelpad api POST /api/keys --set label="ci pipeline"

Revoke an API key

DELETE /api/keys/:id

Immediate. Requests using it start failing with 401.

CLI
modelpad keys delete <id>

Storefront settings

GET /api/storefront-settings

Branding, custom domain and which items appear on the public storefront.

CLI
modelpad api GET /api/storefront-settings

Something missing?

The reference covers the resources most integrations reach for. Every other endpoint the web app uses is reachable with the same credential — modelpad api <METHOD> <path> from the CLI, or a plain HTTP request with your bearer token.

Create an account