POST

Products.Add

Create a product in The route mirrors the dashboard product-report product tab, accepts the real product form fields, scopes the row to the active owner, applies safe defaults for omitted DB-required fields, and emits realtime products.created. Product Type (type) options match the dashboard Type dropdown: product (Physical Product), subscription, service, donation. Default type=product when omitted. Main Image uses the same biz1upload/site/ storage as dashboard Create Product (multipart main_image file, or site_logo_old / main_image_old path).

Permissions

  • User must be logged in with a valid bearer token.
  • Returned or changed records are limited by the user permissions, team access, folder access, and account settings.

Action

  • Creates a Products record or sends the requested data after validation.

Push Service

Pushes event products.created to users who should see this change.

Automation

Runs automation event products.created with the record id and submitted parameters after the action succeeds.

Special Instructions

  • Product type (type) is a fixed dropdown — there is no separate ProductTypes.List API.
  • Allowed type values (dashboard Type select): product = Physical Product; subscription = Subscription; service = Service; donation = Donation.
  • Default when type is omitted: product.
  • When type=subscription, use months, endless, and price_after like the dashboard subscription fields.
  • Main category: cat_id from Categories.List (parent=0). Sub-category: sub_cat_id from Categories.List with parent=<cat_id>.
  • Supplier: suppliers_id is a multi-select array like dashboard Create Product (select name="suppliers_id[]"). Stored as JSON e.g. ["267","170"]. Send array, suppliers_id[], JSON string, or a single id (wrapped to ["8"]). Default when omitted: []. Use Suppliers.List with for_product=1.
  • Default warehouse: default_storage_id from Storage.List (storage.id — not storage_products.id).
  • Main Image (dashboard Create Product image box): multipart/form-data field main_image as a file. Stored with the same FTP path as the dashboard: biz1upload/site/{owner}-{random}{time}.{ext}. Response main_image_url uses https://files.biz1.co.il/ (same as dashboard config file_url). Path aliases: site_logo_old, main_image_old.
  • Optional second image: multipart sub_image (or path sub_image_old), same site folder.
  • Existing JSON / x-www-form-urlencoded Products.Add calls without images still work unchanged.

Required Parameters

NameTypeSampleExplanation
product_name
post
stringMembershipProduct name.

Optional Parameters

NameTypeSampleWhat it gives
product_enname
post
stringMembershipProduct English name when stored.
type
post
stringproductProduct Type — same as dashboard Type select. Allowed values: product (Physical Product), subscription (Subscription), service (Service), donation (Donation). Default: product.
cat_id
post
int17963Main category id from Categories.List. Defaults to 0 when omitted. Alias: category.
sub_cat_id
post
int17964Sub-category id from Categories.List. Defaults to 0 when omitted.
team_member_id
post
int47Assigned team member id when the table stores team_member_id. Defaults to the logged-in user when omitted.
suppliers_id
post
array["8","12"]Supplier ids array — same as dashboard suppliers_id[] multi-select. Stored as JSON ["8","12"]. Also accepts a single id "8" (saved as ["8"]), or repeated form field suppliers_id[]. From Suppliers.List (for_product=1). Default: [].
suppliers_id[]
post
array8Dashboard-style repeated form field for suppliers_id (multipart / form-urlencoded). Same as suppliers_id array.
default_storage_id
post
int3Default warehouse id from Storage.List (storage.id).
main_image
file
fileproduct.jpgMain product image file (dashboard Main Image). Use multipart/form-data. Stored as biz1upload/site/{owner}-{random}{time}.{ext}. Allowed: jpg, jpeg, png, gif, webp, bmp, tif, tiff.
site_logo_old
post
stringbiz1upload/site/14-ab12cd341712345678.jpgDashboard alias: already-uploaded main image path (cropper writes site_logo_old). Used when not sending a new main_image file. Alias: main_image_old.
sub_image
file
fileproduct-alt.jpgOptional secondary image file. Same biz1upload/site/ storage as dashboard.
sub_image_old
post
stringbiz1upload/site/14-xy98ef761712345679.jpgAlready-uploaded secondary image path when not sending a new sub_image file.
product_price
post
number99.00Product price when the account stores product_price.
main_price
post
number99.00Dashboard alias for price.
price
post
number99.00Product price when the account stores price.
cost
post
number40.00Internal product cost when stored.
cost_price
post
number40.00Dashboard/native column name for internal product cost.
description
post
stringMonthly membershipProduct description.
product_sku
post
stringMEM-001Stored product SKU field.
sku
post
stringMEM-001Product SKU when stored.
product_internal_sku
post
stringMEM-INT-001Internal product SKU when stored.
discount_price
post
number10.00Discount amount/price when stored. Alias: allowed_discount.
stock
post
int10Initial stock quantity. Defaults to 0.
min_stock
post
int2Minimum stock warning threshold. Defaults to 0.
sold_stock
post
int0Sold stock counter when stored. Defaults to 0.
left_stock
post
int10Remaining stock when stored. Defaults to stock on create.
pay_coin
post
stringusdDashboard alias for the currency/coin field.
coin
post
stringusdCurrency/coin field when stored.
endless
post
int0Subscription endless flag (used when type=subscription).
months
post
int12Subscription month count (used when type=subscription).
price_after
post
number79.00Subscription price after the first period (used when type=subscription).
status
post
int1Sets active/inactive status when stored.

Authentication

Send the bearer token returned by Login in the request header.

Authorization: Bearer YOUR TOKEN

Sample Request

{
    "url": "\/app\/Products.Add",
    "method": "POST",
    "headers": {
        "Authorization": "Bearer YOUR TOKEN"
    },
    "body": {
        "product_name": "Membership new product",
        "type": "product",
        "cat_id": "17963",
        "suppliers_id": [
            "8"
        ],
        "price": "99.00",
        "main_image": "@product.jpg"
    },
    "curl_multipart": "curl --location 'https:\/\/YOUR_HOST\/app\/Products.Add' \\\n--header 'Authorization: Bearer YOUR_TOKEN' \\\n--form 'product_name=Membership new product' \\\n--form 'type=product' \\\n--form 'price=99.00' \\\n--form 'main_image=@\/path\/to\/product.jpg'",
    "url_user": "https:\/\/{user}.bull36.com\/app\/Products.Add",
    "url_domain": "https:\/\/{domain}\/app\/Products.Add"
}

Endpoint

POST /app/Products.Add
POST https://{user}.bull36.com/app/Products.Add
POST https://{domain}/app/Products.Add

Sample Output

{
    "success": 1,
    "id": "123",
    "message": "Record added successfully.",
    "main_image": "biz1upload\/site\/14-ab12cd341712345678.jpg",
    "main_image_url": "https:\/\/files.biz1.co.il\/biz1upload\/site\/14-ab12cd341712345678.jpg",
    "sub_image": "",
    "sub_image_url": "",
    "automation_event": "products.created",
    "socket_event": "products.created"
}

JavaScript Example

const token = 'YOUR TOKEN';
// Without image (unchanged)
const body = new URLSearchParams({
  product_name: 'Membership new product',
  type: 'product',
  cat_id: '17963',
  price: '99.00'
});
const res = await fetch('/app/Products.Add', { method: 'POST', headers: { Authorization: 'Bearer ' + token }, body });
console.log(await res.json());

// With Main Image (multipart — same as dashboard Create Product)
const form = new FormData();
form.append('product_name', 'Membership new product');
form.append('type', 'product');
form.append('price', '99.00');
form.append('main_image', fileInput.files[0]);
const res2 = await fetch('/app/Products.Add', {
  method: 'POST',
  headers: { Authorization: 'Bearer ' + token },
  body: form
});
console.log(await res2.json());

Error Example

{
    "success": 0,
    "message": "No supported fields were sent."
}