Products & quotes
The product catalog (by hand, or kept in sync from any system in bulk) and quotes your customers accept online.
Products feed deal line items and quotes. Catalogs synced from an ERP or a shop carry a source and an external_id; POST /products/bulk lets any system keep its catalog in Meetzy up to date. A quote gets a public page (https://meetzy.me/quote/{public_token}) where the customer reads, accepts or declines it — opening it moves sent to viewed.
/productsList productsGET/products/gridWhole catalog, leanPOST/productsCreate a productPOST/products/bulkCreate or update products in bulkGET/products/{id}Get a productPATCH/products/{id}Update a productDELETE/products/{id}Delete a productGET/quotesList quotesPOST/quotesCreate a quoteGET/quotes/{id}Get a quotePATCH/quotes/{id}Update a quote or its statusDELETE/quotes/{id}Delete a quote/productsList products
Products sorted by name, paginated (catalogs can hold tens of thousands of items).
Query parameters
qstring- Search in name and SKU.
activestringtrueorfalse.sourcestring- Only products of a source (
sap_b1,shopify,feed,custom:…), ormanualfor those made by hand. countsstringtrueaddscounts: { active, archived }.limitinteger- Page size, 1–1000. Default 1000.
offsetinteger- Rows to skip.
curl "https://meetzy.me/api/v1/products?q=licence&active=true" \ -H "Authorization: Bearer $MEETZY_API_KEY"
const res = await fetch("https://meetzy.me/api/v1/products?q=licence&active=true", {
headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}` },
});
const data = await res.json();import os, requests
res = requests.get(
"https://meetzy.me/api/v1/products?q=licence&active=true",
headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
)
data = res.json(){
"products": [
{
"id": "088e684a-acb8-4b45-aeaf-698701641993",
"user_from": "acme",
"name": "Licence",
"sku": "LIC-1",
"description": null,
"unit_price": 1200,
"cost": null,
"currency": "EUR",
"billing": "one_time",
"tax_pct": 20,
"active": true,
"created_at": "2026-10-07T13:22:58.735611+00:00",
"updated_at": "2026-10-07T13:22:58.735611+00:00",
"source": null,
"external_id": null,
"image_url": null,
"barcode": null,
"category": null,
"brand": null,
"stock_qty": null,
"unit": null,
"url": null,
"extra": {},
"synced_at": null
},
{
"id": "2506fc7b-fe8d-4b6c-a426-bbd8dc2e2bb0",
"user_from": "acme",
"name": "Support",
"sku": "LIC-2",
"description": null,
"unit_price": 300,
"cost": null,
"currency": "EUR",
"billing": "one_time",
"tax_pct": 20,
"active": true,
"created_at": "2026-10-07T13:09:39.60981+00:00",
"updated_at": "2026-10-07T13:15:18.216623+00:00",
"source": "pricelist",
"external_id": "LIC-2",
"image_url": null,
"barcode": null,
"category": null,
"brand": null,
"stock_qty": null,
"unit": null,
"url": null,
"extra": {},
"synced_at": "2026-10-07T13:15:18.079+00:00"
}
],
"total": 3
}/products/gridWhole catalog, lean
Every product (up to 60,000) with the columns of the spreadsheet view: price, cost, stock (available, committed, incoming), supplier, brand, category, source and sync dates. Not paginated.
curl "https://meetzy.me/api/v1/products/grid" \ -H "Authorization: Bearer $MEETZY_API_KEY"
const res = await fetch("https://meetzy.me/api/v1/products/grid", {
headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}` },
});
const data = await res.json();import os, requests
res = requests.get(
"https://meetzy.me/api/v1/products/grid",
headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
)
data = res.json(){
"products": [
{
"id": "088e684a-acb8-4b45-aeaf-698701641993",
"name": "Licence",
"sku": "LIC-1",
"barcode": null,
"category": null,
"brand": null,
"unit_price": 1100,
"cost": null,
"currency": "EUR",
"stock_qty": null,
"unit": null,
"active": true,
"source": null,
"image_url": null,
"updated_at": "2026-10-07T13:23:01.736987+00:00",
"synced_at": null,
"supplier": null,
"available": null,
"committed": null,
"incoming": null,
"group": null,
"created_sap": null
},
{
"id": "1f354d71-029d-40d5-ae4b-03d0dfa85de4",
"name": "Support",
"sku": "LIC-2",
"barcode": null,
"category": null,
"brand": null,
"unit_price": 300,
"cost": null,
"currency": "EUR",
"stock_qty": null,
"unit": null,
"active": true,
"source": "pricelist",
"image_url": null,
"updated_at": "2026-10-07T13:22:59.521788+00:00",
"synced_at": "2026-10-07T13:22:59.395+00:00",
"supplier": null,
"available": null,
"committed": null,
"incoming": null,
"group": null,
"created_sap": null
}
],
"total": 3
}/productsCreate a product
Body
namestringrequired- Product name.
skustring- Reference / SKU.
descriptionstring- Description.
unit_pricenumber- Price excl. tax.
costnumber- Cost price (for margins).
currencystring- ISO currency. Default
EUR. billingstringone_time,monthlyoryearly— recurring products feed MRR on deals.tax_pctnumber- Tax rate in percent (e.g.
20). activeboolean- Shown in pickers. Archived products are
false. categorystring- Category.
brandstring- Brand.
barcodestring- EAN / UPC.
stock_qtynumber- Quantity in stock.
unitstring- Unit of sale (
pcs,kg,hour…). image_urlstring- Picture URL.
urlstring- Product page URL.
curl -X POST "https://meetzy.me/api/v1/products" \ -H "Authorization: Bearer $MEETZY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Licence", "sku": "LIC-1", "unit_price": 1200, "billing": "yearly", "tax_pct": 20, "category": "Software" }'
const res = await fetch("https://meetzy.me/api/v1/products", { method: "POST", headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ "name": "Licence", "sku": "LIC-1", "unit_price": 1200, "billing": "yearly", "tax_pct": 20, "category": "Software" }), }); const data = await res.json();
import os, requests
res = requests.post(
"https://meetzy.me/api/v1/products",
headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
json={
"name": "Licence",
"sku": "LIC-1",
"unit_price": 1200,
"billing": "yearly",
"tax_pct": 20,
"category": "Software"
},
)
data = res.json(){
"id": "088e684a-acb8-4b45-aeaf-698701641993",
"user_from": "acme",
"name": "Licence",
"sku": "LIC-1",
"description": null,
"unit_price": 1200,
"cost": null,
"currency": "EUR",
"billing": "one_time",
"tax_pct": 20,
"active": true,
"created_at": "2026-10-07T13:22:58.735611+00:00",
"updated_at": "2026-10-07T13:22:58.735611+00:00",
"source": null,
"external_id": null,
"image_url": null,
"barcode": null,
"category": null,
"brand": null,
"stock_qty": null,
"unit": null,
"url": null,
"extra": {},
"synced_at": null
}/products/bulkCreate or update products in bulk
Up to 5,000 products per call from any system. Each product is matched by external_id in your source, then by SKU (so products created by hand are adopted, not duplicated). Field names are flexible (price, prix, reference, ean…). Send complete: true on the last batch to archive the products of this source you did not send, or keep_only with the full list of ids still alive.
Body
productsobject[]required- Products:
external_idorsku,name, and any product field (unit_price,stock_qty,brand,category,image_url…). sourcestringrequired- Name of your system (
erp,pim,shopify…). Notmanualorimport. currencystring- Currency for prices sent without one.
completeboolean- This batch is the whole catalog: archive what it does not contain.
keep_onlystring[]- External ids that still exist; everything else of this source is archived.
curl -X POST "https://meetzy.me/api/v1/products/bulk" \ -H "Authorization: Bearer $MEETZY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "source": "erp", "currency": "EUR", "products": [ { "external_id": "A-1001", "sku": "PJ-DENIM-32", "name": "Denim jacket", "unit_price": 89, "stock_qty": 140, "brand": "Pepe Jeans", "category": "Jackets" } ] }'
const res = await fetch("https://meetzy.me/api/v1/products/bulk", { method: "POST", headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ "source": "erp", "currency": "EUR", "products": [ { "external_id": "A-1001", "sku": "PJ-DENIM-32", "name": "Denim jacket", "unit_price": 89, "stock_qty": 140, "brand": "Pepe Jeans", "category": "Jackets" } ] }), }); const data = await res.json();
import os, requests
res = requests.post(
"https://meetzy.me/api/v1/products/bulk",
headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
json={
"source": "erp",
"currency": "EUR",
"products": [
{
"external_id": "A-1001",
"sku": "PJ-DENIM-32",
"name": "Denim jacket",
"unit_price": 89,
"stock_qty": 140,
"brand": "Pepe Jeans",
"category": "Jackets"
}
]
},
)
data = res.json(){
"received": 1,
"created": 0,
"updated": 1,
"archived": 0,
"skipped": 0
}curl "https://meetzy.me/api/v1/products/e43414d6-f56f-42be-a64c-a836d6015efc" \ -H "Authorization: Bearer $MEETZY_API_KEY"
const res = await fetch("https://meetzy.me/api/v1/products/e43414d6-f56f-42be-a64c-a836d6015efc", {
headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}` },
});
const data = await res.json();import os, requests
res = requests.get(
"https://meetzy.me/api/v1/products/e43414d6-f56f-42be-a64c-a836d6015efc",
headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
)
data = res.json(){
"id": "088e684a-acb8-4b45-aeaf-698701641993",
"user_from": "acme",
"name": "Licence",
"sku": "LIC-1",
"description": null,
"unit_price": 1200,
"cost": null,
"currency": "EUR",
"billing": "one_time",
"tax_pct": 20,
"active": true,
"created_at": "2026-10-07T13:22:58.735611+00:00",
"updated_at": "2026-10-07T13:22:58.735611+00:00",
"source": null,
"external_id": null,
"image_url": null,
"barcode": null,
"category": null,
"brand": null,
"stock_qty": null,
"unit": null,
"url": null,
"extra": {},
"synced_at": null
}/products/{id}Update a product
Path parameters
idstringrequired- Id of the record (UUID).
Body
namestring- Product name.
skustring- Reference / SKU.
descriptionstring- Description.
unit_pricenumber- Price excl. tax.
costnumber- Cost price (for margins).
currencystring- ISO currency. Default
EUR. billingstringone_time,monthlyoryearly— recurring products feed MRR on deals.tax_pctnumber- Tax rate in percent (e.g.
20). activeboolean- Shown in pickers. Archived products are
false. categorystring- Category.
brandstring- Brand.
barcodestring- EAN / UPC.
stock_qtynumber- Quantity in stock.
unitstring- Unit of sale (
pcs,kg,hour…). image_urlstring- Picture URL.
urlstring- Product page URL.
curl -X PATCH "https://meetzy.me/api/v1/products/e43414d6-f56f-42be-a64c-a836d6015efc" \ -H "Authorization: Bearer $MEETZY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "unit_price": 1100, "active": true }'
const res = await fetch("https://meetzy.me/api/v1/products/e43414d6-f56f-42be-a64c-a836d6015efc", { method: "PATCH", headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ "unit_price": 1100, "active": true }), }); const data = await res.json();
import os, requests
res = requests.patch(
"https://meetzy.me/api/v1/products/e43414d6-f56f-42be-a64c-a836d6015efc",
headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
json={
"unit_price": 1100,
"active": True
},
)
data = res.json(){
"id": "088e684a-acb8-4b45-aeaf-698701641993",
"user_from": "acme",
"name": "Licence",
"sku": "LIC-1",
"description": null,
"unit_price": 1100,
"cost": null,
"currency": "EUR",
"billing": "one_time",
"tax_pct": 20,
"active": true,
"created_at": "2026-10-07T13:22:58.735611+00:00",
"updated_at": "2026-10-07T13:23:01.736987+00:00",
"source": null,
"external_id": null,
"image_url": null,
"barcode": null,
"category": null,
"brand": null,
"stock_qty": null,
"unit": null,
"url": null,
"extra": {},
"synced_at": null
}/products/{id}Delete a product
Path parameters
idstringrequired- Id of the record (UUID).
curl -X DELETE "https://meetzy.me/api/v1/products/e43414d6-f56f-42be-a64c-a836d6015efc" \ -H "Authorization: Bearer $MEETZY_API_KEY"
const res = await fetch("https://meetzy.me/api/v1/products/e43414d6-f56f-42be-a64c-a836d6015efc", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}` }, }); const data = await res.json();
import os, requests
res = requests.delete(
"https://meetzy.me/api/v1/products/e43414d6-f56f-42be-a64c-a836d6015efc",
headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
)
data = res.json(){
"deleted": true
}/quotesList quotes
Quotes, newest first, with their deal and company names.
Query parameters
statusstringdraft,sent,viewed,acceptedordeclined.deal_iduuid- Quotes of a deal.
limitinteger- Default 200.
curl "https://meetzy.me/api/v1/quotes?status=sent" \ -H "Authorization: Bearer $MEETZY_API_KEY"
const res = await fetch("https://meetzy.me/api/v1/quotes?status=sent", {
headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}` },
});
const data = await res.json();import os, requests
res = requests.get(
"https://meetzy.me/api/v1/quotes?status=sent",
headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
)
data = res.json(){
"quotes": [
{
"id": "340e92a8-0d78-406a-aea3-01dba31a3ef4",
"number": "Q-2026-0001",
"title": "Proposal",
"status": "draft",
"total": 12000,
"currency": "EUR",
"deal_id": "d74acfa5-9877-4d7f-a369-3a505bac042a",
"company_id": "166c436d-a874-4730-a0d8-c8d7fa090268",
"lead_id": "8028ff76-1da4-451f-a0e7-dd91db772c20",
"valid_until": "2026-11-06",
"sent_at": null,
"accepted_at": null,
"created_at": "2026-10-07T13:23:03.299681+00:00",
"public_token": "tok_…",
"deal": {
"id": "d74acfa5-9877-4d7f-a369-3a505bac042a",
"name": "Payfit — rollout"
},
"company": {
"id": "166c436d-a874-4730-a0d8-c8d7fa090268",
"name": "Payfit"
}
}
]
}/quotesCreate a quote
Numbered automatically (Q-2026-0001). From a deal, the deal’s line items (or its amount) become the lines, and its company and contact are used. Each line: name, quantity, unit_price, optional product_id, description, discount_pct, tax_pct, billing. Subtotal, discounts, tax and total are computed.
Body
deal_iduuid- Deal the quote is for.
company_iduuid- Customer company.
lead_iduuid- Customer contact.
titlestring- Title. Default
Proposal. line_itemsobject[]- Lines (default: from the deal).
currencystring- Currency.
valid_daysinteger- Valid for N days. Default 30.
valid_untildate- Or an explicit end date.
termsstring- Payment terms.
notesstring- Notes shown on the quote.
curl -X POST "https://meetzy.me/api/v1/quotes" \ -H "Authorization: Bearer $MEETZY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "deal_id": "5eecc043-d70d-4b94-a604-7e1a35d92edf", "title": "Rollout proposal", "valid_days": 15 }'
const res = await fetch("https://meetzy.me/api/v1/quotes", { method: "POST", headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ "deal_id": "5eecc043-d70d-4b94-a604-7e1a35d92edf", "title": "Rollout proposal", "valid_days": 15 }), }); const data = await res.json();
import os, requests
res = requests.post(
"https://meetzy.me/api/v1/quotes",
headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
json={
"deal_id": "5eecc043-d70d-4b94-a604-7e1a35d92edf",
"title": "Rollout proposal",
"valid_days": 15
},
)
data = res.json(){
"id": "340e92a8-0d78-406a-aea3-01dba31a3ef4",
"user_from": "acme",
"deal_id": "d74acfa5-9877-4d7f-a369-3a505bac042a",
"company_id": "166c436d-a874-4730-a0d8-c8d7fa090268",
"lead_id": "8028ff76-1da4-451f-a0e7-dd91db772c20",
"number": "Q-2026-0001",
"title": "Proposal",
"status": "draft",
"currency": "EUR",
"line_items": [
{
"name": "Licence",
"total": 12000,
"billing": "one_time",
"tax_pct": 0,
"position": 0,
"quantity": 12,
"product_id": null,
"unit_price": 1000,
"description": "",
"discount_pct": 0
}
],
"subtotal": 12000,
"discount_total": 0,
"tax_total": 0,
"total": 12000,
"valid_until": "2026-11-06",
"terms": "Payment within 30 days of invoice. Prices exclude VAT unless stated.",
"notes": null,
"public_token": "tok_…",
"sent_at": null,
"viewed_at": null,
"accepted_at": null,
"declined_at": null,
"signer_name": null,
"signer_email": null,
"created_by": "olivia@acme.com",
"created_at": "2026-10-07T13:23:03.299681+00:00",
"updated_at": "2026-10-07T13:23:03.299681+00:00"
}/quotes/{id}Get a quote
The quote with its lines and totals, public_token, and the dates it was sent, viewed, accepted or declined.
Path parameters
idstringrequired- Id of the record (UUID).
curl "https://meetzy.me/api/v1/quotes/e43414d6-f56f-42be-a64c-a836d6015efc" \ -H "Authorization: Bearer $MEETZY_API_KEY"
const res = await fetch("https://meetzy.me/api/v1/quotes/e43414d6-f56f-42be-a64c-a836d6015efc", {
headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}` },
});
const data = await res.json();import os, requests
res = requests.get(
"https://meetzy.me/api/v1/quotes/e43414d6-f56f-42be-a64c-a836d6015efc",
headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
)
data = res.json(){
"quote": {
"id": "340e92a8-0d78-406a-aea3-01dba31a3ef4",
"user_from": "acme",
"deal_id": "d74acfa5-9877-4d7f-a369-3a505bac042a",
"company_id": "166c436d-a874-4730-a0d8-c8d7fa090268",
"lead_id": "8028ff76-1da4-451f-a0e7-dd91db772c20",
"number": "Q-2026-0001",
"title": "Proposal",
"status": "draft",
"currency": "EUR",
"line_items": [
{
"name": "Licence",
"total": 12000,
"billing": "one_time",
"tax_pct": 0,
"position": 0,
"quantity": 12,
"product_id": null,
"unit_price": 1000,
"description": "",
"discount_pct": 0
}
],
"subtotal": 12000,
"discount_total": 0,
"tax_total": 0,
"total": 12000,
"valid_until": "2026-11-06",
"terms": "Payment within 30 days of invoice. Prices exclude VAT unless stated.",
"notes": null,
"public_token": "tok_…",
"sent_at": null,
"viewed_at": null,
"accepted_at": null,
"declined_at": null,
"signer_name": null,
"signer_email": null,
"created_by": "olivia@acme.com",
"created_at": "2026-10-07T13:23:03.299681+00:00",
"updated_at": "2026-10-07T13:23:03.299681+00:00"
}
}/quotes/{id}Update a quote or its status
Change the content, or the status: sent, accepted and declined save their date and are logged on the deal’s timeline (and fire quote.* events). Each line: name, quantity, unit_price, optional product_id, description, discount_pct, tax_pct, billing. Subtotal, discounts, tax and total are computed.
Path parameters
idstringrequired- Id of the record (UUID).
Body
titlestring- Title.
line_itemsobject[]- Lines (totals recomputed).
termsstring- Terms.
notesstring- Notes.
valid_untildate- End of validity.
currencystring- Currency.
statusstring- New status.
signer_namestring- Who accepted.
signer_emailstring- Their email.
curl -X PATCH "https://meetzy.me/api/v1/quotes/e43414d6-f56f-42be-a64c-a836d6015efc" \ -H "Authorization: Bearer $MEETZY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "status": "sent" }'
const res = await fetch("https://meetzy.me/api/v1/quotes/e43414d6-f56f-42be-a64c-a836d6015efc", { method: "PATCH", headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ "status": "sent" }), }); const data = await res.json();
import os, requests
res = requests.patch(
"https://meetzy.me/api/v1/quotes/e43414d6-f56f-42be-a64c-a836d6015efc",
headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
json={
"status": "sent"
},
)
data = res.json(){
"id": "340e92a8-0d78-406a-aea3-01dba31a3ef4",
"user_from": "acme",
"deal_id": "d74acfa5-9877-4d7f-a369-3a505bac042a",
"company_id": "166c436d-a874-4730-a0d8-c8d7fa090268",
"lead_id": "8028ff76-1da4-451f-a0e7-dd91db772c20",
"number": "Q-2026-0001",
"title": "Proposal",
"status": "sent",
"currency": "EUR",
"line_items": [
{
"name": "Licence",
"total": 12000,
"billing": "one_time",
"tax_pct": 0,
"position": 0,
"quantity": 12,
"product_id": null,
"unit_price": 1000,
"description": "",
"discount_pct": 0
}
],
"subtotal": 12000,
"discount_total": 0,
"tax_total": 0,
"total": 12000,
"valid_until": "2026-11-06",
"terms": "Payment within 30 days of invoice. Prices exclude VAT unless stated.",
"notes": null,
"public_token": "tok_…",
"sent_at": "2026-10-07T13:23:05.623+00:00",
"viewed_at": null,
"accepted_at": null,
"declined_at": null,
"signer_name": null,
"signer_email": null,
"created_by": "olivia@acme.com",
"created_at": "2026-10-07T13:23:03.299681+00:00",
"updated_at": "2026-10-07T13:23:05.646702+00:00"
}/quotes/{id}Delete a quote
Path parameters
idstringrequired- Id of the record (UUID).
curl -X DELETE "https://meetzy.me/api/v1/quotes/e43414d6-f56f-42be-a64c-a836d6015efc" \ -H "Authorization: Bearer $MEETZY_API_KEY"
const res = await fetch("https://meetzy.me/api/v1/quotes/e43414d6-f56f-42be-a64c-a836d6015efc", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}` }, }); const data = await res.json();
import os, requests
res = requests.delete(
"https://meetzy.me/api/v1/quotes/e43414d6-f56f-42be-a64c-a836d6015efc",
headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
)
data = res.json(){
"deleted": true
}