Create Product
Create a new product in your store.
status is optional — omit it to create as draft. Creating a product with
status: published requires an active Easytools plan. If the store has none, the call
returns 422 and no product is created. status: draft is always accepted. See
Active plan required.
Checkout appearance is not on this body. After create, set styles, settings, and
gallery with the nested endpoints — see
Checkout appearance. Sending styles, settings,
or media here is ignored. The response leaves those keys null.
Checkout and waitlist field definitions are also nested — see
Custom Fields. Sending custom_fields or
waitlist_custom_fields here is ignored. The response leaves those keys null.
Checkout order bumps are nested — see Order Bumps. Sending
order_bumps here is ignored. After-purchase cross-sells are nested — see
Cross-sells. Sending cross_sells here is ignored.
Request
POST /products
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
status | string | No | Product status (draft or published). Omit to create as draft. |
name | string | Yes | Product name (max 255 characters) |
description | string | No | Product description (max 10,000 characters) |
image_url | string | No | URL to the product image (max 2048 characters). The image is downloaded and stored — see Product images. |
currency | string | Yes | ISO currency code (3 characters, e.g., "usd", "eur") |
slug | string | No | Custom URL slug. Must match pattern ^[a-z0-9-]+$ and be unique per store. |
active_from | string | No | Start selling date (ISO 8601 format) |
active_until | string | No | Stop selling date (ISO 8601 format). Must be after or equal to active_from. |
show_active_until_counter | boolean | No | Show countdown timer on checkout page. Defaults to false. |
redirect_url | string | No | Post-purchase redirect URL (max 1000 characters) |
redirect_time | integer | No | Redirect delay in seconds. Defaults to null. |
add_redirect_params | boolean | No | Include order parameters in redirect URL. Defaults to true. |
webhook_url | string | No | Webhook URL for order events (max 255 characters) |
terms_url | string | No | Terms & conditions URL (max 255 characters) |
policy_url | string | No | Privacy policy URL (max 255 characters) |
contact_email | string | No | Product contact email (max 255 characters) |
refund_days | integer | No | Refund period in days. Set to 0 to disable refunds. |
show_discount_field | boolean | No | Show promo code field on checkout page |
delegable | boolean | No | Allow order delegation/gifting |
hide_tax_id_field | boolean | No | Hide tax ID field on checkout page. Defaults to false. |
require_tax_id | boolean | No | Require tax ID on checkout page. Defaults to false. |
photo_zoomable | boolean | No | Enable image zoom on checkout page. Defaults to false. |
show_confetti | boolean | No | Show confetti animation after purchase. Defaults to false. |
generate_qr_code | boolean | No | Generate QR code for product. Defaults to false. |
show_delegation | boolean | No | Show the access-delegation control on checkout. Defaults to false. |
delegation_label | string | No | Label for the access-delegation control (max 250 characters). |
delegable_if_multiple | boolean | No | Allow delegation only when buying more than one. Defaults to false. |
has_shipping | boolean | No | Collect a delivery address at checkout. Defaults to false. Distinct from delivery_enabled. |
delivery_enabled | boolean | No | Enable parcel delivery. Defaults to false. When true, delivery_service and a complete delivery_options object are required. |
delivery_service | string | No | Carrier slug. See Checkout form. Required when delivery_enabled is true. |
delivery_address_optional | boolean | No | Whether the delivery address is optional. Defaults to true. |
delivery_options | object | No | Parcel package. See Delivery Options Object. Required in full when delivery_enabled is true. |
shipping_phone_required | boolean | No | Require a phone number with the delivery address. Defaults to false. |
phone_tooltip | string | No | Helper text for the shipping phone field (max 250 characters). |
mandatory_consent | string | No | Label for the mandatory checkout consent (max 500 characters). Omit or null turns it off. |
optional_consent | string | No | Label for the optional checkout consent (max 500 characters). |
digital_consent | string | No | Label for the digital-content consent (max 500 characters). |
newsletter_consent | string | No | Label for the newsletter opt-in (max 500 characters). |
newsletter_consent_required | boolean | No | Whether the newsletter opt-in is required. Defaults to false. |
newsletter_discount | object | No | Discount offered with newsletter opt-in. See Newsletter Discount Object. Do not send currency. |
invoice_remarks | string | No | Remarks printed on invoices (max 250 characters). |
vat_rate | string | No | VAT rate as a string (23, zw, np). Not a number. See Invoice, tax, portal, waitlist, and QR. |
gtu_code | integer | No | GTU code 1–13 as stored, not a GTU_01 label. |
flat_rate | number | No | Flat-rate tax percent: 17, 15, 14, 12.5, 12, 10, 8.5, 5.5, 3, or 2. |
exempt_tax_kind | string | No | Exemption basis when VAT is zw (max 255 characters). Not required when vat_rate is zw. |
tax_category | string | No | Tax category: saas, ebook, course, webinar, digital, consulting, physical, or exempt. Distinct from variant tax_behavior. |
dashboard_header | string | No | Self-service portal header (max 250 characters). |
long_description | string | No | Self-service portal body (max 25,000 characters). Distinct from description. |
cta_text | string | No | Self-service portal call-to-action label (max 50 characters). |
cta_url | string | No | Self-service portal call-to-action URL (max 255 characters). |
contact_phone | string | No | Product contact phone (max 50 characters). |
contact_website_url | string | No | Product contact website (max 255 characters). Distinct from the store website_url. |
contact_additional_info | string | No | Extra product contact copy (max 5,000 characters). |
footer_text | string | No | Checkout footer copy (max 500 characters). Omit persists as "", not JSON null. |
waitlist_enabled | boolean | No | Whether the waitlist is enabled. Defaults to false. |
waitlist_require_first_name | boolean | No | Whether the waitlist requires a first name. Defaults to false. |
waitlist_require_last_name | boolean | No | Whether the waitlist requires a last name. Defaults to false. |
qr_code_info | string | No | Extra text shown with the QR code (max 250 characters). |
credits | integer | No | QR credits. Omit or null means credits are off, not zero. Minimum 0, maximum 4294967295 (422 if exceeded). |
reset_credits | boolean | No | Whether credits reset after use. Defaults to false. |
file | object | No | Single downloadable file. Legacy shortcut equivalent to files: [file]. See File Object. |
files | array | No | Up to 5 downloadable files. Each entry is a File Object. When provided, this supersedes file. |
Every URL in the body must start with http:// or https:// and include a host. See
URL fields for what is accepted and what returns 422.
File Object
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name shown in the customer portal (max 100 characters) |
url | string | Yes | The file's location (max 2048 characters): a URL to attach the file by link, or the url of a file read back from the API |
Reads add a read-only third property, download_url — see
File Object. Requests take name and url only.
A product can have up to 5 downloadable files. Use files (array) to attach more than one. The singular file field is a backward-compatible shortcut for a single file; if both are sent, files wins.
Example Request
Basic product creation — status omitted, so the product is created as draft:
curl -X POST "https://cart.easy.tools/api/v1/products" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Premium Coffee Beans",
"description": "High-quality arabica coffee beans from Colombia",
"image_url": "https://example.com/images/coffee-beans.jpg",
"currency": "usd",
"file": {
"name": "My ebook.pdf",
"url": "https://mysite.com/ebook.pdf"
}
}'
Product with multiple downloadable files:
curl -X POST "https://cart.easy.tools/api/v1/products" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"status": "published",
"name": "Premium Coffee Bundle",
"currency": "usd",
"files": [
{ "name": "My ebook.pdf", "url": "https://mysite.com/ebook.pdf" },
{ "name": "Bonus chapter.pdf", "url": "https://mysite.com/bonus-chapter.pdf" }
]
}'
Product with advanced options:
curl -X POST "https://cart.easy.tools/api/v1/products" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"status": "published",
"name": "Premium Coffee Beans",
"description": "High-quality arabica coffee beans from Colombia",
"image_url": "https://example.com/images/coffee-beans.jpg",
"currency": "usd",
"slug": "premium-arabica-coffee",
"active_from": "2026-02-01T00:00:00Z",
"active_until": "2026-03-01T00:00:00Z",
"show_active_until_counter": true,
"redirect_url": "https://mysite.com/thank-you",
"redirect_time": 5,
"add_redirect_params": true,
"terms_url": "https://mysite.com/terms",
"contact_email": "support@mysite.com",
"refund_days": 14,
"show_discount_field": true,
"delegable": true,
"hide_tax_id_field": false,
"require_tax_id": false,
"photo_zoomable": true,
"show_confetti": true,
"generate_qr_code": false
}'
Product with delivery address collection and a newsletter discount:
curl -X POST "https://cart.easy.tools/api/v1/products" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Premium Coffee Beans",
"currency": "usd",
"has_shipping": true,
"shipping_phone_required": true,
"mandatory_consent": "I agree to the terms of sale",
"newsletter_consent": "Send me roasting notes and offers",
"newsletter_discount": {
"amount": 500,
"duration": "once"
}
}'
Product with VAT, waitlist names, and QR credits:
curl -X POST "https://cart.easy.tools/api/v1/products" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Premium Coffee Beans",
"currency": "usd",
"vat_rate": "23",
"tax_category": "digital",
"waitlist_enabled": true,
"waitlist_require_first_name": true,
"credits": 10
}'
Response
Success Response (201)
Returns the created product with its empty variants array and all fields. The example
matches the advanced request (status: published). A body with only name and
currency returns status: draft and the checkout-flag defaults from the table above.
{
"id": "0632bef5-c308-42cd-9cba-89a7a4f722bb",
"status": "published",
"name": "Premium Coffee Beans",
"description": "High-quality arabica coffee beans from Colombia",
"slug": "premium-arabica-coffee",
"image_url": "https://example.com/images/coffee-beans.jpg",
"checkout_url": "https://cart.easy.tools/checkout/0632bef5-c308-42cd-9cba-89a7a4f722bb",
"currency": "usd",
"file": null,
"files": [],
"active_from": "2026-02-01T00:00:00+00:00",
"active_until": "2026-03-01T00:00:00+00:00",
"show_active_until_counter": true,
"redirect_url": "https://mysite.com/thank-you",
"redirect_time": 5,
"add_redirect_params": true,
"webhook_url": null,
"terms_url": "https://mysite.com/terms",
"policy_url": null,
"contact_email": "support@mysite.com",
"refund_days": 14,
"delegable": true,
"show_discount_field": true,
"hide_tax_id_field": false,
"require_tax_id": false,
"photo_zoomable": true,
"show_confetti": true,
"generate_qr_code": false,
"show_delegation": false,
"delegation_label": null,
"delegable_if_multiple": false,
"has_shipping": false,
"delivery_enabled": false,
"delivery_service": null,
"delivery_address_optional": true,
"delivery_options": null,
"shipping_phone_required": false,
"phone_tooltip": null,
"mandatory_consent": null,
"optional_consent": null,
"digital_consent": null,
"newsletter_consent": null,
"newsletter_consent_required": false,
"newsletter_discount": null,
"invoice_remarks": null,
"vat_rate": null,
"gtu_code": null,
"flat_rate": null,
"exempt_tax_kind": null,
"tax_category": null,
"dashboard_header": null,
"long_description": null,
"cta_text": null,
"cta_url": null,
"contact_phone": null,
"contact_website_url": null,
"contact_additional_info": null,
"footer_text": "",
"waitlist_enabled": false,
"waitlist_require_first_name": false,
"waitlist_require_last_name": false,
"qr_code_info": null,
"credits": null,
"reset_credits": false,
"variants": [],
"custom_fields": null,
"waitlist_custom_fields": null,
"styles": null,
"settings": null,
"media": null,
"has_draft": false
}
Response Fields
See Get Product for a complete list of response fields.
Error Responses
Validation Error (422 Unprocessable Entity)
{
"message": "The given data was invalid.",
"errors": {
"name": [
"This field is required."
],
"status": [
"The status field must be one of: draft, published."
],
"currency": [
"The currency field must be exactly 3 characters."
]
}
}
Slug validation errors:
{
"message": "The given data was invalid.",
"errors": {
"slug": [
"The slug 'existing-slug' is already in use by another product."
]
}
}
{
"message": "The given data was invalid.",
"errors": {
"slug": [
"The slug field format is invalid."
],
"active_until": [
"The active until field must be a date after or equal to active from."
]
}
}
Sellability fields use the same 422 envelope. See
Checkout form for the full matrix. Invoice, tax, portal,
waitlist, and QR fields use it too — see
Invoice, tax, portal, waitlist, and QR.
Sending delivery_enabled without a carrier:
{
"message": "The given data was invalid.",
"errors": {
"delivery_service": [
"The delivery service field is required when delivery enabled is true."
]
}
}
Publishing Not Allowed (422 — No Active Plan)
Returned when status is published and the store has no active Easytools plan. No
product is created. Send status: draft instead, or activate a plan and retry.
This body carries a message and no errors key, so a 422 from this endpoint is
distinguished by whether errors is present.
{
"message": "Product cannot be published without an active Easytools plan"
}