Sales API
Record e-commerce orders and attribute revenue to calls, agents, and marketing sources for complete ROI tracking.
Introduction #
The Optico Sales API (Customer Order API) records e-commerce orders and maps them to calls for conversion tracking. Your call center can then attribute revenue and conversions to specific calls, agents, keywords, and marketing sources, so you know what each call is worth.
https://www.optico.fr
Transport & Conventions
| Method | All endpoints use POST |
| Content-Type | application/x-www-form-urlencoded |
| Response format | application/json |
| Timeout | 5 seconds recommended |
Getting Started #
The Sales API is the final step in the Optico call center integration. It connects your e-commerce order management system with the call tracking platform to close the attribution loop between marketing spend and revenue.
visit_id values via the optico_visit_id cookie.- A domain with the Call Center feature enabled in Optico.
- A Customer API key, available from domain settings in the Optico backend.
- Order statuses configured in Optico's call center settings (some statuses marked as "is_sale" trigger conversion tracking).
Authentication #
Every request must include your key as a POST field. This is the Customer API key for your domain, the same key used by the Cart API.
key=cust_b7e2c8a14f9dcdef01234567
Errors #
Response envelope
Every request returns 200 OK with a JSON envelope. Branch on the status field rather than the HTTP status code.
Success
{ "status": "success" }
Error
{ "status": "error",
"message": "Invalid api key" }
Common error messages
| Message | Cause | Fix |
|---|---|---|
Invalid api key | The key is missing, revoked, or not registered for the calling domain. | Check the Customer API key in your domain settings. |
Missing required parameter | One of id, status, or key is absent or empty. | Log the request payload and confirm the required fields are present. |
Unknown status | The submitted status is not declared in your Optico call center settings. | Declare the status under Call Center > Statuses, then retry. See Order Status Configuration. |
Invalid products format | The products field is not valid JSON. | JSON-encode the product array before sending. See Products object. |
Idempotency
The endpoint is idempotent on your id: re-submitting the same order id updates the existing record rather than creating a duplicate. Submit on order creation and on every status change.
Retries & timeouts
Use a 5-second client timeout. Network timeouts and HTTP 5xx responses are safe to retry with exponential backoff (1s, 2s, 4s) because the endpoint is idempotent. Do not retry on a JSON {"status":"error"} body. Fix the payload and resubmit.
Rate limits
No fixed public rate limit is enforced for normal order traffic. For a bulk backfill (migrations, historical imports) contact Optico support so the channel can be sized for your account.
Endpoints #
| Endpoint | Method | Purpose |
|---|---|---|
/api/customer-order | POST | Create or update an order record, linked to a call or visitor session. |
POST /api/customer-order #
Creates or updates an order in Optico. If an order with the same id already exists for the domain, it is updated, making this endpoint idempotent. Submit on order creation and on every status change.
Parameters
| Parameter | Type | Description | |
|---|---|---|---|
key | string | Required | Customer API key for the domain. |
id | string | Required | External order ID from your e-commerce system. Used to match subsequent updates to the same order. |
status | string | Required | Order status string. Must match a status configured in Optico's call center settings. Determines whether the order counts as a sale. See Order Status Configuration. |
cart_id | string | Optional | External cart ID linking this order to a previously submitted cart (from the Cart API). |
visit_id | integer | Optional | Optico visit ID from the optico_visit_id cookie. Links the order to the visitor session. |
call_id | integer | Optional | Optico call ID. Directly associates the order with a specific call record. |
agent | string | Optional | Call center agent username who processed this order. |
currency | string | Optional | ISO 4217 currency code (e.g. EUR, USD, GBP). Defaults to domain currency. |
products | JSON string | Optional | JSON-encoded array of product objects. See Products object. |
Example request
curl -X POST https://www.optico.fr/api/customer-order \ --max-time 5 \ -d "key=cust_b7e2c8a14f9dcdef01234567" \ -d "id=ORDER-5678" \ -d "cart_id=CART-1234" \ -d "visit_id=3617898c651df0f8617f70a0" \ -d "status=confirmed" \ -d "currency=EUR" \ -d "agent=agent_john" \ --data-urlencode 'products=[{"id":"PROD-001","name":"Blue Widget","quantity":2,"cost_per_unit":29.99},{"id":"PROD-007","name":"Red Gadget","quantity":1,"cost_per_unit":59.00}]'
$visitId = intval($_COOKIE['optico_visit_id'] ?? 0); $params = [ 'key' => 'cust_b7e2c8a14f9dcdef01234567', 'id' => 'ORDER-5678', 'cart_id' => 'CART-1234', 'visit_id' => $visitId, 'status' => 'confirmed', 'currency' => 'EUR', 'agent' => 'agent_john', 'products' => json_encode([ ['id' => 'PROD-001', 'name' => 'Blue Widget', 'quantity' => 2, 'cost_per_unit' => 29.99], ['id' => 'PROD-007', 'name' => 'Red Gadget', 'quantity' => 1, 'cost_per_unit' => 59.00], ]), ]; $ch = curl_init('https://www.optico.fr/api/customer-order'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query($params), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 5, ]); $response = json_decode(curl_exec($ch), true); curl_close($ch);
const products = [ { id: 'PROD-001', name: 'Blue Widget', quantity: 2, cost_per_unit: 29.99 }, { id: 'PROD-007', name: 'Red Gadget', quantity: 1, cost_per_unit: 59.00 }, ]; const body = new URLSearchParams({ key: 'cust_b7e2c8a14f9dcdef01234567', id: 'ORDER-5678', cart_id: 'CART-1234', visit_id: visitId, status: 'confirmed', currency: 'EUR', agent: 'agent_john', products: JSON.stringify(products), }); const res = await fetch('https://www.optico.fr/api/customer-order', { method: 'POST', body, signal: AbortSignal.timeout(5000), }); const data = await res.json();
Products object #
The products parameter must be a JSON-encoded string containing an array of product objects.
| Field | Type | Description |
|---|---|---|
id | string | External product ID from your catalogue. |
name | string | Product display name. |
quantity | integer | Number of units ordered. |
cost_per_unit | float | Unit price. Optico computes total_cost = sum(quantity × cost_per_unit). |
details | object | Key-value pairs for any additional product attributes. |
Order statuses #
The status field must match one of the statuses configured in your Optico account under Call Center > Statuses. Each status can optionally be flagged as is_sale.
| Status type | Example values | Effect in Optico |
|---|---|---|
| Sale status | confirmed, paid, shipped | Order counts as a conversion. Google Analytics e-commerce event fired automatically. Revenue attributed to the call's keyword/source/agent. |
| Non-sale status | pending, cancelled, refunded | Order is saved and visible in reports but does not count as a conversion. No GA event fired. |
pending → confirmed). Subsequent updates do not re-fire it.Order lifecycle #
The following flow shows how the Sales API fits into a complete Optico call center integration:
visit_id nor call_id is provided, the order is saved but not attributed to any call or visitor session. Attribution, conversion rates, and Google Analytics events will be missing. Always include visit_id from the optico_visit_id cookie.