Appearance
A sale represents a single line item (row) in a transaction with a customer.
A transaction is created within Custobar by combining all the information in the sale rows having the same sale_external_id. Each sale row holds information about the bought product, quantity and sale price.
The transaction-level information (e.g. the customer made the purchase) is supplied with one or multiple sale rows in addition to its product information.
Transaction-level fields
Transaction-level fields apply to the entire purchase regardless of which sale row the information was supplied, the transaction-level fields are prefixed with sale_.
Mandatory fields
| Field | Type | Description |
|---|---|---|
| sale_external_id | string | Your unique identifier for the entire transaction which includes all the sale rows. Must be included in all sale rows. |
| sale_date | datetime | The date and time of the sale. This can be provided in two formats: - Date only: e.g., 2025-08-28. If only the date is provided without a time and timezone, the system will default to using the timezone configured in Custobar's settings. - Date and time with timezone: e.g., 2025-08-28T14:10:00Z. If the datetime string includes a timezone (e.g., Z for UTC/GMT), that specified timezone will be used, and the system will not default to the timezone configured in Custobar's settings. Note: When a timezone is specified in the datetime string, it will override any default timezone settings. Ensure that the datetime is correctly formatted according to ISO 8601 standards, and be explicit with timezones to avoid any discrepancies in data processing. |
The customer making the transaction is identified by supplying at least one of the customer identifying fields external_id, phone_number or email. These fields should be prefixed too, thus email becoming sale_email, etc.
| Customer field | Sale field |
|---|---|
| external_id | sale_customer_id |
| phone_number | sale_phone_number |
| sale_email |
Optional fields
| Field | Type | Description |
|---|---|---|
| sale_currency | string | The currency in which the sale was made. Must be defined together with sale_exchange_rate. |
| sale_discount | money | Discount that applies to the purchase as a whole, e.g. a discount of 5.00 € should be passed as 500. |
| sale_discount_in_currency | integer | Discount amount in the sale currency (in cents). Can be used instead of sale_discount when sale_currency and sale_exchange_rate have been defined. |
| sale_exchange_rate | number | Exchange rate from sale_currency to the default currency in Custobar. Must be defined together with sale_currency. |
| sale_payment_method | list of string | Your identifier(s) of the payment method(s) used. |
| sale_shipping | money | The added shipping cost in cents. |
| sale_shipping_in_currency | integer | The added shipping cost in cents, expressed in sale_currency. Can be defined instead of sale_shipping when sale_currency and sale_exchange_rate have been defined. |
| sale_shipping_method | string | The shipping method used. |
| sale_coupon_codes | list of string | Any coupon codes that have been used in the sale. |
| sale_shop_id | string | external_id of a shop where the purchase was made. |
| sale_state | string | A sale state, one of NEW, PENDING_PAYMENT, PROCESSING, COMPLETE, CANCELLED, CLOSED or HOLDED. Sales in special states CANCELLED or CLOSED are not included in the statistics. If omitted, the sale is treated as COMPLETE. |
| sale_total | money | The total price of the transaction in cents, e.g. 950.00 € should be passed as 95000. If sale_total is missing, the sale total is calculated as sum(sale row total fields) - sale_discount. |
| sale_total_in_currency | integer | The total price of the transaction in cents, expressed in sale_currency. Can be used instead of sale_total when sale_currency and sale_exchange_rate have been defined. |
| sale_tags | set of string | Tags assigned to this sale at the transaction level. |
| sale_is_deleted | boolean | Marks the sale as deleted. |
Customer attribute fields
These fields allow customer attributes to be supplied inline with the sale, so that the customer record is created or updated at the same time as the transaction.
| Field | Type | Description |
|---|---|---|
| sale_first_name | string | Customer's first name. |
| sale_last_name | string | Customer's last name. |
| sale_customer_company | string | Company name associated with the customer. |
| sale_customer_language | string | Language preference of the customer. |
| sale_customer_can_email | boolean | Marketing consent for email. true means opted in. |
| sale_customer_can_sms | boolean | Marketing consent for SMS. true means opted in. |
Address fields
Shipping or billing address associated with the transaction.
| Field | Type | Description |
|---|---|---|
| sale_street_address | string | Street address. |
| sale_city | string | City. |
| sale_zip_code | string | Postal/ZIP code. |
| sale_country | country_code | Country code (ISO 3166-1 alpha-2). |
Conversion tracking fields
| Field | Type | Description |
|---|---|---|
| sale_conversion_url | url | The URL where the conversion (purchase) took place. |
| sale_conversion_user_agent | string | The browser user agent string at the time of conversion. |
| sale_datasource | string | An identifier for the data source or channel that produced this sale. |
Sale-row-level fields
Mandatory fields
| Field | Type | Description |
|---|---|---|
| product_id | string | external_id of a product sold. |
| quantity | number | Quantity of the product sold. Supplying double value is supported, but it is rounded to precision of two decimal places using the normal rounding rules, e.g. a value of 0.003 will become 0.00. |
| unit_price | money | The price of one unit in cents. Unit price of 1795.00 € should be passed as 179500. The unit price is the price of a single item, before discount has been applied. |
The products listed in the sales are linked to the products in Custobar's database by matching the product_id in the sale to the external_id of the imported products.
If there is no product in the database matching the product_id of the incoming sale, a product with that ID will be created along with the sale, and the details listed in the sale (title, price, etc.) will be copied to that new product.
In case a product_id is not included in the sale, Custobar tries to find a matching product using the product_sku and ean fields instead, and link the sale to that product.
If the sale does not include product_id, but includes a product_sku or ean, but a matching product is not found, Custobar will create the product, but it will give it a synthetic external_id composed of the ean/sku in the sale. Note however, that if the product matching that sku/ean is later imported to Custobar, it won't be matched to this product with the synthetic id. So, if the system that is importing sales to Custobar does not have access to the proper product_ids, it can use ean/sku instead to identify the sale. But, in this case, it's important to import the products first, so that the matching products will be found at sales import time.
You can also skip sending values in the unit_price field and instead send sale_total for the whole transaction.
Optional fields
| Field | Type | Description |
|---|---|---|
| external_id | string | Your unique identifier for this sale row. |
| ean | string | EAN barcode of the product. Used as a fallback identifier to match the product if product_id is not provided. |
| product_sku | string | An identifier which uniquely identifies the product. Used as a fallback if product_id is not provided; Custobar will try to match the sale to a product by product_sku or ean. |
| product_brand | string | Brand of the product. Copied to the product record if the product is created from this sale. |
| product_category | list of string | Category or categories of the product. Copied to the product record if the product is created from this sale. |
| product_title | string | Title of the product. Copied to the product record if the product is created from this sale. |
| product_type | string | Type of the product. Copied to the product record if the product is created from this sale. |
| product_vendor | string | Vendor of the product. Copied to the product record if the product is created from this sale. |
| discount | money | A discount applied to this sale row, in cents. E.g. a discount of 0.50 € should be passed as 50. Both positive and negative values are treated as a discount. |
| discount_in_currency | money | Discount for this sale row in cents, expressed in sale_currency. Can be used instead of discount when sale_currency and sale_exchange_rate have been defined. |
| total | money | The total price of this sale row in cents. |
| total_in_currency | integer | The total price of this sale row in cents, expressed in sale_currency. |
| unit | string | The unit of measurement for the product (e.g. kg, pcs). |
| unit_price_in_currency | money | If sale_currency is defined, the unit price may be given in that currency instead of unit_price. |
| is_deleted | boolean | Marks this sale row as deleted. |
| tags | set of string | Tags assigned to this sale row. |
If total is not supplied, the value of a sale row is calculated as (unit_price * quantity) - discount.
sale_rows nested format
In addition to the flat multi-row format shown in the examples below, sales can also be submitted using a nested sale_rows structure, where each transaction is a single object containing a list of row objects under the sale_rows key.
| Field | Type | Description |
|---|---|---|
| sale_rows | list of object | List of sale row objects belonging to this transaction. Each object accepts the same sale-row-level fields described above. |
Company-specific fields
You may add additional fields that are company specific by prefixing them with a company short name and a double underscore __, e.g. COMPANY__pos_id. The company specific fields apply to the entire transaction and are not sale row specific.
Company specific fields are searchable in the Custobar user interface.
Providing sales in currency other than the default currency
You may send sales in some other currency than the default currency defined in Custobar settings. To do so, you'll need to send:
sale_currency— The currency in which the sale occurred.sale_exchange_rate— The exchange rate to the default currency. The formula isunit_price_in_currency / sale_exchange_rate= the price in the default Custobar currency.unit_price_in_currency— The price of the sale row insale_currency.
The corresponding _in_currency variants (sale_discount_in_currency, sale_shipping_in_currency, sale_total_in_currency, discount_in_currency, total_in_currency) follow the same pattern and can be used in place of their default-currency counterparts when sale_currency and sale_exchange_rate are defined.
Canceled / Returned sales
While different ERP and eCommerce systems manage returns / canceled sales differently, our best practice is to mark the returned / canceled sales with the sale_state value CANCELLED. With this state, Custobar will not calculate the returned sale as a conversion and the revenue is removed from the aggregated total revenue.
Please note: This is the best practice for the case where the entire sale is canceled. If only one sale row of the sale is canceled / returned, then you can update the price of that sale row to 0 and mark the sale row with the tags value CANCELLED.
Example
To upload new sales, you may pass them to Custobar using a HTTP POST command, e.g.
bash
curl -X POST -u USER -H "Content-Type: application/json" \
--data-binary @sales.json https://COMPANY.custobar.com/api/sales/upload/The sale objects must be provided as a list, wrapped into a JSON object, with a key sales, as shown in the example below.
The example represents a single transaction with two sale order rows.
json
{
"sales": [
{
"sale_external_id": "1000",
"sale_customer_id": "A501",
"sale_date": "2017-12-01T13:11:23Z",
"sale_shop_id": "1",
"sale_discount": 0,
"sale_total": 5160,
"sale_payment_method": "Credit Card",
"external_id": "1000-1",
"product_id": "SKU0435",
"unit_price": 1200,
"quantity": 1,
"total": 1200,
"COMPANY__sales_person": "3"
},
{
"sale_external_id": "1000",
"external_id": "1000-2",
"product_id": "SKU3429",
"unit_price": 2080,
"quantity": 2,
"discount": 200,
"total": 3960
}
]
}This example is sent without a unique ID for the sale rows (external_id), which connects all rows to one sale using the sale_external_id field.
json
{
"sales": [
{
"sale_external_id": "1000",
"sale_customer_id": "A501",
"sale_date": "2017-12-01T13:11:23Z",
"sale_shop_id": "1",
"sale_discount": 0,
"sale_total": 5160,
"sale_payment_method": "Credit Card",
"product_id": "SKU0435",
"unit_price": 1200,
"quantity": 1,
"total": 1200
},
{
"sale_external_id": "1000",
"product_id": "SKU3429",
"unit_price": 2080,
"quantity": 2,
"discount": 200,
"total": 3960
}
]
}