Push your sales events
Sales events are purchases and returns from your point of sale or data warehouse. Ingesting them powers RFM segmentation, analytics, and SMS attribution. This is usually the highest-volume integration, so it is built to be safe and forgiving.
Before you start: the store must exist
Section titled “Before you start: the store must exist”A sale references a store, and unlike a profile, a store is not auto-created. The store field is a reference (your store external_id or a canonical store_ id), and the call returns 404 if it resolves to no store. Sync your stores first, then push sales against them.
Ingest a sale
Section titled “Ingest a sale”POST /v1/sales-events records one transaction. Amounts are always in cents and never divided. A RETURN is recorded as its own separate event: it does not modify or void any earlier PURCHASE. Analytics net purchases and returns together; you never edit a past event.
curl https://api.sms.noticia.ai/v1/sales-events \ -X POST \ -H "x-api-key: ntca_REPLACE_ME" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 7f1c0b9e-2a4d-4c8e-9b1a-3e5f6a7b8c9d" \ -d '{ "external_id": "TICKET-2026-04-25-001234:PURCHASE", "type": "PURCHASE", "occurred_at": "2026-04-25T14:32:00+02:00", "store_id": "paris-rivoli-01", "currency": "EUR", "profile_id": "crm-customer-4815", "total_amount_cents": 12500, "total_discount_cents": 0, "total_tax_cents": 2083, "source": "Cegid POS v3", "line_items": [ { "external_id": "LINE-001", "product": { "external_id": "SKU-12345", "name": "White T-shirt M", "properties": { "category": "Menswear", "size": "M", "color": "white" } }, "quantity": 2, "unit_price_cents": 5000, "total_price_cents": 10000, "tax_cents": 1667 }, { "external_id": "LINE-002", "product": { "external_id": "SKU-67890", "name": "Tote bag" }, "quantity": 1, "unit_price_cents": 2500, "total_price_cents": 2500, "tax_cents": 416 } ] }'const sale = { external_id: 'TICKET-2026-04-25-001234:PURCHASE', type: 'PURCHASE', occurred_at: '2026-04-25T14:32:00+02:00', store: 'paris-rivoli-01', currency: 'EUR', profile: { external_id: 'crm-customer-4815' }, total_amount_cents: 12500, total_discount_cents: 0, total_tax_cents: 2083, source: 'Cegid POS v3', line_items: [ { external_id: 'LINE-001', product: { external_id: 'SKU-12345', name: 'White T-shirt M', properties: { category: 'Menswear', size: 'M', color: 'white' }, }, quantity: 2, unit_price_cents: 5000, total_price_cents: 10000, tax_cents: 1667, }, { external_id: 'LINE-002', product: { external_id: 'SKU-67890', name: 'Tote bag' }, quantity: 1, unit_price_cents: 2500, total_price_cents: 2500, tax_cents: 416, }, ],};
await fetch('https://api.sms.noticia.ai/v1/sales-events', { method: 'POST', headers: { 'x-api-key': 'ntca_REPLACE_ME', 'Idempotency-Key': '7f1c0b9e-2a4d-4c8e-9b1a-3e5f6a7b8c9d', 'Content-Type': 'application/json', }, body: JSON.stringify(sale),});import requests
sale = { "external_id": "TICKET-2026-04-25-001234:PURCHASE", "type": "PURCHASE", "occurred_at": "2026-04-25T14:32:00+02:00", "store_id": "paris-rivoli-01", "currency": "EUR", "profile_id": "crm-customer-4815", "total_amount_cents": 12500, "total_discount_cents": 0, "total_tax_cents": 2083, "source": "Cegid POS v3", "line_items": [ { "external_id": "LINE-001", "product": { "external_id": "SKU-12345", "name": "White T-shirt M", "properties": {"category": "Menswear", "size": "M", "color": "white"}, }, "quantity": 2, "unit_price_cents": 5000, "total_price_cents": 10000, "tax_cents": 1667, }, { "external_id": "LINE-002", "product": {"external_id": "SKU-67890", "name": "Tote bag"}, "quantity": 1, "unit_price_cents": 2500, "total_price_cents": 2500, "tax_cents": 416, }, ],}
requests.post( "https://api.sms.noticia.ai/v1/sales-events", headers={ "x-api-key": "ntca_REPLACE_ME", "Idempotency-Key": "7f1c0b9e-2a4d-4c8e-9b1a-3e5f6a7b8c9d", }, json=sale,)A 201 Created returns the stored sales event with its prefixed evt_ id (or 200 OK if this external_id was already ingested).
Only a few fields are strictly required: type, occurred_at, store, currency, total_amount_cents, total_discount_cents, and at least one line item with quantity and total_price_cents. Everything else (profile, product, payments, loyalty, source, metadata) is optional but makes the data far more useful.
Products and properties
Section titled “Products and properties”Each line item can carry a product. It is optional (omit it for a service, shipping or fee line), but for retail you almost always want it:
external_id: your SKU or product code.name: a human-readable label.properties: a free-form bag of scalar attributes (string, number or boolean), up to 50 keys and 4 KB total. This is where category, size, color, material, and similar product traits go.
Product properties need no setup: send whatever keys you have and they are stored as-is. Keep keys consistent across line items so analytics group cleanly. You can review your product taxonomy in the Noticia app under Integrations -> Product catalog.
Anonymous and unknown buyers
Section titled “Anonymous and unknown buyers”- Anonymous sale: omit
profileentirely. The sale is recorded but not attributed to a customer. - Unknown buyer: if
profile_idis a CRM id you have not pushed yet, Noticia auto-creates a light profile from it rather than rejecting the sale (a canonicalprof_id, by contrast, must already exist). Your POS can push tickets in real time, before the loyalty profile has synced; when your CRM later upserts the full profile, the history lines up on the sameexternal_id.
This means you can push sales and sync profiles in any order. Stores are the exception: they must exist first.
Amounts and currency
Section titled “Amounts and currency”- All monetary fields end in
Centsand are integers. Keep them raw; divide by 100 only when you display them. currencyis one ofEUR,GBP,USD,CHF.total_amount_centsis the amount actually paid, net of discount and tax-inclusive, always positive (aRETURNis the absolute amount).
Reliability and high volume
Section titled “Reliability and high volume”Send each event with a stable external_id: replaying a failed batch is safe because already-ingested events are recognized and not duplicated. Add an Idempotency-Key on retries as an extra transport safety net, and apply the recommended back-off for a robust pipeline.