Products
The sales product catalog (hub_invoice_products table — legacy name; semantically = billing catalog per company). Used as an optional line-item source when creating quotes or invoices (line.product_id). Scopes: sales.products.read / sales.products.write / sales.products.delete.
Endpoints
GET
/api/workspace/v1/productsGET
/api/workspace/v1/products/{id}POST
/api/workspace/v1/productsPATCH
/api/workspace/v1/products/{id}DELETE
/api/workspace/v1/products/{id}POST
/api/workspace/v1/products/{id}/restorePOST
/api/workspace/v1/products/bulk-deleteThe product object
| Field | Type | Description |
|---|---|---|
| name* | string | Human-readable product name (required) |
| sku | string | Optional SKU / product code |
| description | string | Short description (<= 500 chars) |
| unit | enum | uur | stuk | dag | maand | km | pakket | sessie | licentie |
| default_price | number | Default unit price (excl. VAT) |
| cost_price | number | Cost price for margin calculations |
| category | string | Free-form category label |
| is_recurring | boolean | True if this represents a recurring subscription |
| recur_interval | enum | weekly | monthly | quarterly | yearly (required if is_recurring) |
| ledger_code | string | Optional ledger / accounting code |
| image_url | string | HTTPS product image URL |
| internal_notes | string | Notes visible only to workspace users |
| vat_rate | number | VAT percentage 0-100 (default 21) |
| active | boolean | False to hide from pickers (archive flag, keeps history) |
| sort_order | integer | Display order in the picker |
Create a product
curl "https://app.staffifyai.com/api/workspace/v1/products" \
-X POST \
-H "Authorization: Bearer sfy_wsp_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "AI Voice Agent — starter",
"sku": "AI-VOICE-STARTER",
"unit": "maand",
"default_price": 499.00,
"vat_rate": 21,
"is_recurring": true,
"recur_interval": "monthly",
"category": "Subscriptions"
}'Archive vs delete
Two orthogonal soft states exist on every product:
active = false— hidden from pickers but keeps history intact and appears in list queries with?active=falseor?active=any. No purge.deleted_at— recycle bin (30-day purge countdown). Requires the separatesales.products.deletescope. Historical invoice + quote lines keep their denormalised description/price after purge, onlyproduct_idgets nullified.
# Soft-delete
curl -X DELETE "https://app.staffifyai.com/api/workspace/v1/products/42" \
-H "Authorization: Bearer sfy_wsp_live_YOUR_KEY"
# Restore
curl -X POST "https://app.staffifyai.com/api/workspace/v1/products/42/restore" \
-H "Authorization: Bearer sfy_wsp_live_YOUR_KEY"
# Bulk-delete (max 500 ids)
curl -X POST "https://app.staffifyai.com/api/workspace/v1/products/bulk-delete" \
-H "Authorization: Bearer sfy_wsp_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "ids": [42, 43] }'Webhooks
product.createdproduct.updatedproduct.deletedproduct.restored