Recommendation engine
Product recommendations for a live conversation: given a customer's phone number and/or their purchase history, the engine returns ranked, in-stock product offers in well under a second.
Base URL: https://recommendation-engine.trywink.io
Requests carry an API key in the X-API-Key header. The engine currently
validates a dedicated service credential issued by Wink; issuing
recommendation_engine-scoped keys from Org Settings → API Keys is
rolling out — until then, use the credential Wink provisioned for you.
The service sits behind Cloudflare. Use a normal HTTP client user-agent —
default library UAs (e.g. raw urllib) can be challenged at the edge.
POST /recommend
Returns ranked recommendations for one customer.
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
phone | string | yes | — | Customer's number; US 10-digit (a leading 1 is stripped). Send "0000000000" when unavailable. |
purchased_skus | string[] | no | [] | Anchor SKUs for the session (e.g. what's being discussed). Unknown SKUs are silently ignored. |
num_items | int | no | 1 | 1–10. Values outside → 422. |
check_stock | bool | no | true | Filter to in-stock items. |
customer_order_history | object[] | no | — | [{sku?, product_title?, order_date?}]. Supplying it (even []) skips the engine's own history lookup and saves ~0.5–1.5 s. |
source | string | no | "IVR" | Free-text traffic tag for analytics. |
curl -s https://recommendation-engine.trywink.io/recommend \
-H 'Content-Type: application/json' \
-H "X-API-Key: $WINK_API_KEY" \
-d '{
"phone": "2015550100",
"purchased_skus": ["8151583"],
"customer_order_history": [
{"sku": "5066562", "product_title": "white topaz 4.90 ctw sterling silver ring", "order_date": "2026-05-02"}
],
"num_items": 10,
"check_stock": true,
"source": "my-integration"
}'
Response (abridged):
{
"recommendation_id": "0b8c7a4e-9f1d-4c56-a9a1-6d2f8e64c1aa",
"recommendations": [
{
"rank": 1,
"sku": "3086432",
"title": "white topaz sterling silver bracelet",
"web_price": 14.99,
"article_type": "Bracelet",
"size": 7.0,
"rerank_score": 0.9137,
"slot": 1
}
],
"recommendation": { "rank": 1, "sku": "3086432" },
"resolved_purchased": [8151583],
"history_size": 12,
"customer_found": true,
"normalized_phone": "2015550100",
"fallback": false,
"feature_schema_hash": "…",
"artifact_version": "…",
"catalog_version": "…"
}
Notes:
recommendationis an alias for the first item and can benullwhen nothing qualifies.- The three trailing hashes identify the model/catalog build that served the
request — log them with the
recommendation_idif you A/B or debug. customer_foundisnullwhen you suppliedcustomer_order_history(the engine didn't look the customer up itself).- Sized jewelry (rings, bracelets, bangles) is size-gated: a customer with no purchase history in a sized category receives no offers from it, by design — the engine won't guess a ring size.
- Stock can be up to ~30 minutes stale.
POST /purchase
Confirms that a recommendation converted, closing the loop for model training and attribution.
curl -s https://recommendation-engine.trywink.io/purchase \
-H 'Content-Type: application/json' \
-H "X-API-Key: $WINK_API_KEY" \
-d '{"phone": "2015550100", "recommendation_id": "0b8c7a4e-…", "purchased_skus": ["3086432"]}'
Response: {"purchase_id": "…", "recommendation_id": "…", "matched_skus": ["3086432"], "converted": true, "recorded": true}. A
recommendation_id the engine doesn't know returns 404.
Errors
| Status | Meaning |
|---|---|
401 | {"detail": "invalid or missing API key"} — wrong or absent X-API-Key. Do not retry. |
404 | /purchase only: unknown recommendation_id. |
422 | Validation error (num_items out of range, malformed body). Do not retry unchanged. |
5xx | Engine-side failure. Retry with backoff; the service self-heals. |
Performance
- p50 latency ≈ 0.35 s when
customer_order_historyis supplied; omitting it adds ~0.5–1.5 s for the lookup. - Wink's own clients use a 3-second timeout — treat that as the SLO.
- Sustained throughput is modest (single-digit req/s); this is a conversational API, not a batch one. Talk to us before bulk workloads.
Availability
GET /health (no auth) returns {"status": "ok" | "degraded", …} and
serves HTTP 503 when the engine is unhealthy — suitable for your
monitoring.