Skip to content

Create a product

The products.create operation: POST /api/v1/products in the REST API and the products_create tool in the MCP server.

POST/api/v1/products

Creates a product in the brand's catalog, in the request's mode (test and live mode have separate catalogs).

taxCategoryId is required: it decides which tax rates apply to the product in every jurisdiction (for example saas, ebook or electronically_supplied_service). Products of kind: plan appear on the brand's public pricing table. entitlements grant values of the brand's features (features.create), such as seats.max = 10; each value must fit its feature's type. Subscriptions grant them while they're active; with lifetimeEntitlements: true, a one-time purchase grants them for good.

Add prices with prices.create. Images are https URLs.

At a glance

  • REST: POST /api/v1/products
  • MCP tool: products_create
  • Operation ID: products.create
  • Scope: Brand
  • Permission: catalog:write
  • Risk tier: write
  • Dry run: Supported (send Ownrecord-Dry-Run: true)
  • Idempotency: Idempotency-Key required
  • Modes: Test and live
  • Authentication: API key with the catalog:write scope
  • Since: M4

Request

Body

A JSON object (Content-Type: application/json):

FieldTypeRequiredDescription
namestringYesThe product's name, shown to customers.
descriptionstringNoShown on checkout pages and the pricing table.
taxCategoryIdone of 9 values, e.g. electronically_supplied_service, saas, ebook (all in the OpenAPI document)YesThe product's tax category (spec §7.2), e.g. saas or electronically_supplied_service.
kindone of standard, planNoplan products appear on the brand's pricing table; standard ones don't.
entitlementsarray of objectNoFeature values the product grants (spec §36.1), each feature once.
entitlements[].featureKeystringYesThe feature's key, e.g. seats.max.
entitlements[].valueboolean or number or stringYestrue/false for a boolean feature, a whole number for a limit, one of the values for an enum.
lifetimeEntitlementsbooleanNotrue: a one-time purchase of the product grants its entitlements for good (spec §36.1). Default false.
displayOrderinteger (0 to 10000)NoPosition on the pricing table, lowest first.
highlightedbooleanNoHighlight the plan on the pricing table.
imagesarray of stringNoImage URLs (https). On updates the whole list is replaced.
metadataobjectNoYour own key-value pairs (at most 50 keys). On updates the whole set is replaced.

Response

201 Created with a JSON object:

FieldTypeDescription
idprod_… ID
object"product"
brandIdbrd_… ID
livemodeboolean
namestring
descriptionstring or null
taxCategoryIdone of 9 values, e.g. electronically_supplied_service, saas, ebook (all in the OpenAPI document)
kindone of standard, plan
entitlementsarray of object
entitlements[].featureKeystring
entitlements[].valueboolean or number or string
lifetimeEntitlementsbooleanA one-time purchase of the product grants its entitlements for good (spec §36.1).
displayOrderinteger or null
highlightedboolean
activeboolean
imagesarray of string
metadataobject
createdAtdate-time
updatedAtdate-time

Examples

A plan with seats and export

HTTP
POST /api/v1/products
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "name": "Pro",
  "description": "For growing teams.",
  "taxCategoryId": "saas",
  "kind": "plan",
  "entitlements": [
    {
      "featureKey": "seats.max",
      "value": 10
    },
    {
      "featureKey": "feature.export",
      "value": true
    }
  ],
  "displayOrder": 2,
  "highlighted": true
}

Response (201):

JSON
{
  "id": "prod_01m4tx5nj8emwbyf7y9vcf47y0",
  "object": "product",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "livemode": false,
  "name": "Pro",
  "description": "For growing teams.",
  "taxCategoryId": "saas",
  "kind": "plan",
  "entitlements": [
    {
      "featureKey": "seats.max",
      "value": 10
    },
    {
      "featureKey": "feature.export",
      "value": true
    }
  ],
  "lifetimeEntitlements": false,
  "displayOrder": 2,
  "highlighted": true,
  "active": true,
  "images": [],
  "metadata": {},
  "createdAt": "2026-10-13T12:00:00.000Z",
  "updatedAt": "2026-10-13T12:00:00.000Z"
}

Errors

CodeHTTP statusWhenWhat to do
org_restricted403Live mode, while the organization's Ownrecord subscription is unpaid (§37.5).Pay the open Ownrecord invoice in Organization → Billing; the restriction lifts as soon as it is paid. Test mode keeps working.
validation_failed400A field is missing or malformed, e.g. an unknown tax category.Fix the fields listed in details.issues and send the request again.
feature_not_found400An entitlement names a feature the brand doesn't have in this mode.Create the feature first (features.create), or fix the feature key.
entitlement_value_invalid400An entitlement value doesn't fit its feature's type.Use true or false for boolean features, a whole number of 0 or more for limits, and one of the listed values for enum features.
permission_denied403You can't change this brand's catalog.Ask an organization owner or admin for a role or scope that allows this action.

It can also return these common errors: api_version_unsupported, invalid_json, unsupported_media_type, authentication_required, api_key_invalid, scope_required, resource_not_found, idempotency_key_required, idempotency_key_reused, idempotency_in_progress, approval_required, approval_rejected, approval_expired, stale_approval, approval_policy_denied, rate_limited, internal_error.