Zum Inhalt springen
Leadtime
Deutsch
Esc
navigateopen⌘Jpreview

Create product

What this does: Creates a new catalog product with variants and options. Catalog products are standardized, reusable offerings that can be added to projects, tickets, quotes, and invoices.

Product structure: A product consists of:

  1. Product details: Name, category, logo, description, and base pricing
  2. Variants (optional): Different configurations or performance levels (e.g., Standard, Pro, Enterprise) with individual prices
  3. Options (optional): Extra services or add-ons that customers can optionally select (e.g., “Add workshop” or “Enable premium support”)

Pricing: You can set one or more pricing types:

  • Fixed price: One-time payment (e.g., for hardware or one-time services)
  • Subscription price: Recurring payment (e.g., monthly SaaS subscription)
  • Price per unit: Flexible billing by quantity (e.g., per user, per hour)

Description format:

  • Descriptions can be provided as HTML or Markdown
  • The system automatically converts them to internal format (IDoc) for storage
  • Supported formatting: tables, callouts, emojis, and basic text formatting

Logo upload: To add a logo to your product:

  1. First, call POST /api/public/workspace/upload to upload the image file
  2. The upload endpoint returns a file ID
  3. Use that file ID in the logoId field when creating the product

Validation:

  • Product name is required
  • Category ID must be a valid UUID
  • At least one variant must be provided (even if just one)
  • Options array is required (can be empty)
  • If pricePerUnit is set, priceUnitName is required
  • If pricePerUnit or priceSubscription is set, priceFrequency must be 1, 3, 6, or 12 (months)

Response: Returns the created product with all details, including:

  • Descriptions converted to HTML
  • Logo URL (if logo was uploaded)
  • All variants and options with their configurations
POST/administration/product-catalog
Authorization
AuthorizationOAuth2 access token · headerrequired
Scopes:api:write
or
AuthorizationBearer token (JWT) · headerrequired
Query parameters
fieldsToReturnstring
Comma-separated list of top-level response fields to return. Overrides the endpoint compact default unless responseShape=full is used.
responseShapestring
Advanced override. Omit for the endpoint compact default. Use full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
Allowed:compactfull
Header parameters
LT-Response-Shapestring
Advanced override. Set to full only when you need the complete endpoint response, including nested fields that are not selectable with fieldsToReturn.
Allowed:full
Request body
requiredapplication/json
categoryIdstringrequired
UUID of the product category this product belongs to. Categories help organize products into groups (e.g., "Hardware", "Software", "Services").
contractPeriodMonthsnumber
Contract period in months. Required when pricePerUnit or priceSubscription is greater than 0. Defaults to 12 months if not specified.
descriptionstringrequired
Product description explaining what the product includes, its benefits, and key features. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer. For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably. Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>. In HTML you can use: ## Node Types and Marks ### Nodes **paragraph** Type: block Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…) Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers. Atts: - extraPlaceholder: null ```html <p class="paragraph-base" extraplaceholder="null/some-value"></p> ``` **heading** Type: block Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…) Atts: - level: 1 ```html <h1 class="heading-base"></h1> ``` **bulletList** Type: block list Content: listItem+ Atts: none ```html <ul class="list-base"><li></li></ul> ``` **hardBreak** Type: inline Atts: none ```html <br> ``` **horizontalRule** Type: block Atts: none ```html <hr class="hr-base"> ``` **orderedList** Type: block list Content: listItem+ Atts: - start: 1 - type: null ```html <ol class="order-list-base" type="null/some-value"><li></li></ol> ``` **listItem** Content: (paragraph|list)* (paragraphs and/or lists, in any order) Atts: none ```html <li></li> ``` **blockquote** Type: block Content: block+ (one or more block child nodes (e.g. paragraph, list, …)) Atts: none ```html <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote> ``` **table** Type: block Content: tableRow+ Atts: none ```html <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table> ``` **tableRow** Content: (tableCell | tableHeader)* Atts: none ```html <tr></tr> ``` **tableHeader** Content: block+ (one or more block child nodes (e.g. paragraph, list, …)) Atts: - colspan: 1 - rowspan: 1 - colwidth: null - align: null ```html <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th> ``` **tableCell** Content: block+ (one or more block child nodes (e.g. paragraph, list, …)) Atts: - colspan: 1 - rowspan: 1 - colwidth: null - align: null ```html <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td> ``` **callout** Type: block Content: block+ (one or more block child nodes (e.g. paragraph, list, …)) Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout". Atts: - icon: :information_source: ```html <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div> ``` **emoji** Type: inline Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render. Atts: - icon: :smile: ```html <span data-type="emoji" data-icon=":smile:"></span> ``` ### Marks **link** Atts: - href: null - target: _blank - rel: noopener noreferrer nofollow - class: null - title: null ```html <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a> ``` **bold** Atts: none ```html <strong>bold text example</strong> ``` **code** Atts: none ```html <code>code text example</code> ``` **italic** Atts: none ```html <em>italic text example</em> ``` **strike** Atts: none ```html <s>strike text example</s> ``` **underline** Atts: none ```html <u>underline text example</u> ```
logoIdobject | null
File ID of the product logo image. To upload a logo, first call POST /api/public/workspace/upload to upload the image file, then use the returned file ID here. Set to null if no logo is needed.
namestringrequired
Name of the product or service. This is the primary identifier shown in the catalog, quotes, and invoices.
optionsProductOptionApiDto[]required
List of product options. Options are extra services or add-ons that customers can optionally select (e.g., "Add workshop", "Enable premium support"). Options are organized into groups with multiple selectable values. Can be an empty array if no options are needed.
Show properties
Array of ProductOptionApiDto
idstring
Option ID. Include this when updating an existing option. Omit when creating a new option.
isMultiplebooleanrequired
Whether customers can select multiple values from this option. If true, customers can select multiple values (e.g., "Add workshop" AND "Enable premium support"). If false, only one value can be selected.
isRequiredbooleanrequired
Whether this option must be selected. If true, customers must choose at least one value from this option before proceeding.
namestringrequired
Name of the option group (e.g., "Additional Services", "Storage Options", "Support Level"). This groups related option values together.
valuesProductOptionValueApiDto[]required
List of available values for this option. Each value can have its own pricing. At least one value must be provided.
Show properties
Array of ProductOptionValueApiDto
extraPriceFixedobject | null
Additional one-time fixed price added when this option value is selected. Use this for one-time add-ons or upgrades.
extraPricePerUnitobject | null
Additional per-unit price added when this option value is selected. Use this for quantity-based add-ons.
extraPriceSubscriptionobject | null
Additional recurring subscription price added when this option value is selected. Use this for recurring add-ons (e.g., monthly premium support).
idstring
Option value ID. Include this when updating an existing option value. Omit when creating a new option value.
namestringrequired
Name of the option value (e.g., "Add workshop", "Enable premium support", "Extra 10GB storage"). This is what customers see when selecting options.
priceFixedobject | null
One-time fixed price for the product. Use this for one-time purchases (e.g., hardware, installation fees, one-time services). Can be combined with subscription or per-unit pricing. Use 0 or null when the product has no fixed charge.
priceFrequencynumber
Billing frequency in months for subscription or per-unit pricing. Required when pricePerUnit or priceSubscription is greater than 0. Valid values: 1 (monthly), 3 (quarterly), 6 (semi-annually), 12 (annually).
Allowed:13612
pricePerUnitobject | null
Price per unit for flexible quantity-based billing. Use this when pricing depends on quantity (e.g., per user, per hour, per license, per GB). Requires priceUnitName to be set. Use 0 or null when the product has no per-unit charge.
priceSubscriptionobject | null
Recurring subscription price for the product. Use this for recurring revenue models (e.g., monthly SaaS subscription, annual maintenance). The billing frequency is controlled by priceFrequency. Use 0 or null when the product has no subscription charge.
priceUnitNamestring
Name of the unit for per-unit pricing (e.g., "user", "hour", "license", "GB"). Required when pricePerUnit is greater than 0. This is displayed in quotes and invoices.
renewalModestring
Renewal mode for the product subscription. Automatic means the subscription will automatically renew, Stops means it will stop at the end of the contract period. Required when pricePerUnit or priceSubscription is greater than 0. Defaults to Automatic.
Allowed:AutomaticStops
variantsProductVariantApiDto[]required
List of product variants. Variants represent different configurations or performance levels of the same product (e.g., Standard, Pro, Enterprise). Each variant can have its own pricing and description. At least one variant must be provided.
Show properties
Array of ProductVariantApiDto
descriptionobject | nullrequired
Variant description explaining what this variant includes or offers. Accepts Markdown or HTML for complex formatting. This content is rendered by the Leadtime rich editor, not by a plain Markdown viewer. For quick/simple text, Markdown is acceptable. For polished user-facing content from an agent or integration, prefer structured HTML because it preserves editor blocks, links, and layout more predictably. Use only the nodes and marks documented below for this field. Supported editor features differ by endpoint and field. Choose formatting for readability, not decoration: use headings for real sections, paragraphs for narrative text, lists or tables for structured facts, blockquotes for quoted context, callouts for important outcomes/risks/notes when supported, and explicit anchors for links. Do not rely on bare URLs or Markdown links when the link must be clickable; use explicit anchors such as <a href="https://example.com" target="_blank" rel="noopener noreferrer nofollow">link text</a>. In HTML you can use: ## Node Types and Marks ### Nodes **paragraph** Type: block Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…) Default paragraph. extraPlaceholder is an internal structure for the editor to show a ghost placeholder in empty “template” lines (ProseMirror JSON fragment or null). For normal agent output leave extraPlaceholder as null. Only set it if you are intentionally mirroring a field placeholder the user already has in the open editor; do not set random placeholder data for free-form answers. Atts: - extraPlaceholder: null ```html <p class="paragraph-base" extraplaceholder="null/some-value"></p> ``` **heading** Type: block Content: inline* (inline only — do not use block tags such as <p> inside; use e.g. <br>, <strong>, <em>, <span>…) Atts: - level: 1 ```html <h1 class="heading-base"></h1> ``` **bulletList** Type: block list Content: listItem+ Atts: none ```html <ul class="list-base"><li></li></ul> ``` **hardBreak** Type: inline Atts: none ```html <br> ``` **horizontalRule** Type: block Atts: none ```html <hr class="hr-base"> ``` **orderedList** Type: block list Content: listItem+ Atts: - start: 1 - type: null ```html <ol class="order-list-base" type="null/some-value"><li></li></ol> ``` **listItem** Content: (paragraph|list)* (paragraphs and/or lists, in any order) Atts: none ```html <li></li> ``` **blockquote** Type: block Content: block+ (one or more block child nodes (e.g. paragraph, list, …)) Atts: none ```html <blockquote class="blockquote-base"><p class="paragraph-base"></p></blockquote> ``` **table** Type: block Content: tableRow+ Atts: none ```html <table style="width: 0px;"><colgroup></colgroup><tbody><tr></tr></tbody></table> ``` **tableRow** Content: (tableCell | tableHeader)* Atts: none ```html <tr></tr> ``` **tableHeader** Content: block+ (one or more block child nodes (e.g. paragraph, list, …)) Atts: - colspan: 1 - rowspan: 1 - colwidth: null - align: null ```html <th colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></th> ``` **tableCell** Content: block+ (one or more block child nodes (e.g. paragraph, list, …)) Atts: - colspan: 1 - rowspan: 1 - colwidth: null - align: null ```html <td colspan="1" rowspan="1" colwidth="null/some-value" style="text-align: null/some-value;"><p class="paragraph-base"></p></td> ``` **callout** Type: block Content: block+ (one or more block child nodes (e.g. paragraph, list, …)) Callout / info box. The icon attr is an emoji shortcode (e.g. :information_source:, :warning:); it appears on the callout. Body is block content (paragraphs, lists, etc.) — use normal block HTML such as <p class="paragraph-base"> inside the callout <div>. data-type is "callout". Atts: - icon: :information_source: ```html <div data-type="callout" icon=":information_source:"><p class="paragraph-base"></p></div> ``` **emoji** Type: inline Inline emoji. icon is a colon shortcode (e.g. :thumbsup:, :white_check_mark:) stored in data-icon; body is often empty. Use names your emoji set supports; avoid inventing invalid shortcodes in final HTML if you need them to render. Atts: - icon: :smile: ```html <span data-type="emoji" data-icon=":smile:"></span> ``` ### Marks **link** Atts: - href: null - target: _blank - rel: noopener noreferrer nofollow - class: null - title: null ```html <a target="_blank" rel="noopener noreferrer nofollow" href="">link text example</a> ``` **bold** Atts: none ```html <strong>bold text example</strong> ``` **code** Atts: none ```html <code>code text example</code> ``` **italic** Atts: none ```html <em>italic text example</em> ``` **strike** Atts: none ```html <s>strike text example</s> ``` **underline** Atts: none ```html <u>underline text example</u> ```
idstring
Variant ID. Include this when updating an existing variant. Omit when creating a new variant.
namestringrequired
Name of the variant (e.g., "Standard", "Pro", "Enterprise"). Used to distinguish different performance levels or configurations of the same product.
priceFixedobject | null
One-time fixed price for this variant. Use this for one-time purchases (e.g., hardware, installation). Can be combined with subscription or per-unit pricing. Use 0 or null when the variant has no fixed charge.
pricePerUnitobject | null
Price per unit for this variant. Use this for flexible billing by quantity (e.g., per user, per hour, per license). Requires priceUnitName to be set at the product level. Use 0 or null when the variant has no per-unit charge.
priceSubscriptionobject | null
Recurring subscription price for this variant. Use this for recurring revenue models (e.g., monthly SaaS subscription). The billing frequency is set at the product level via priceFrequency. Use 0 or null when the variant has no subscription charge.
Responses
200
categoryIdstringrequired
UUID of the product category this product belongs to
descriptionHtmlstringrequired
Product description converted to HTML format for easy display
idstringrequired
Unique identifier of the product
logoIdobject | nullrequired
File ID of the product logo. Null if no logo is set.
logoUrlobject | nullrequired
Public URL to access the product logo image. Constructed from logoId if a logo is set. Null if no logo is available.
namestringrequired
Name of the product or service
optionsstring[]required
List of all product options with their values and configurations. Structure matches ProductOptionApiDto but with additional internal fields.
priceFixednumberrequired
One-time fixed price for the product
priceFrequencyobject | nullrequired
Billing frequency in months (1, 3, 6, or 12). Null if subscription or per-unit pricing is not used.
pricePerUnitnumberrequired
Price per unit for quantity-based billing
priceSubscriptionnumberrequired
Recurring subscription price for the product
priceUnitNameobject | nullrequired
Name of the unit for per-unit pricing (e.g., "user", "hour"). Null if per-unit pricing is not used.
variantsIProductVariantApiResponse[]required
List of all product variants with their pricing and descriptions
Show properties
Array of IProductVariantApiResponse
descriptionHtmlobject | nullrequired
Variant description converted to HTML format. Null if no description is set.
idstringrequired
Unique identifier of the variant
isActivatedbooleanrequired
Whether this variant is currently activated and available for selection
namestringrequired
Name of the variant (e.g., "Standard", "Pro", "Enterprise")
priceFixednumberrequired
One-time fixed price for this variant
pricePerUnitnumberrequired
Price per unit for this variant
priceSubscriptionnumberrequired
Recurring subscription price for this variant
400Validation errors
errorsobject
messagestring
statusCodenumber
401Unauthorized - Invalid or missing authentication token
403Forbidden - Insufficient API scopes or permissions
Request
curl -X POST "https://leadtime.app/api/public/administration/product-catalog" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "categoryId": "123e4567-e89b-12d3-a456-426614174000",
  "contractPeriodMonths": 12,
  "description": "string",
  "logoId": "123e4567-e89b-12d3-a456-426614174000",
  "name": "Website Hosting Package",
  "options": [
    {
      "id": "string",
      "isMultiple": false,
      "isRequired": false,
      "name": "string",
      "values": [
        {
          "extraPriceFixed": 50,
          "extraPricePerUnit": 2.5,
          "extraPriceSubscription": 10,
          "id": "string",
          "name": "string"
        }
      ]
    }
  ],
  "priceFixed": 999.99,
  "priceFrequency": 1,
  "pricePerUnit": 5,
  "priceSubscription": 29.99,
  "priceUnitName": "user",
  "renewalMode": "Automatic",
  "variants": [
    {
      "description": {},
      "id": "string",
      "name": "string",
      "priceFixed": 999.99,
      "pricePerUnit": 5,
      "priceSubscription": 29.99
    }
  ]
}'
Response
{
  "categoryId": "string",
  "descriptionHtml": "string",
  "id": "string",
  "logoId": {},
  "logoUrl": {},
  "name": "string",
  "options": [
    "string"
  ],
  "priceFixed": 0,
  "priceFrequency": {},
  "pricePerUnit": 0,
  "priceSubscription": 0,
  "priceUnitName": {},
  "variants": [
    {
      "descriptionHtml": {},
      "id": "string",
      "isActivated": true,
      "name": "string",
      "priceFixed": 0,
      "pricePerUnit": 0,
      "priceSubscription": 0
    }
  ]
}