How Recommendations Work
The Recommendations feature provides a structured way to surface AI-generated or manually created suggestions for improving the performance of a shop. Recommendations can target any entity — menu items, menu sections, campaigns, journeys, communications, and more. Each suggestion is stored as a pending recommendation that the shop owner can review and approve before any change is applied.
Core Concepts
What Is a Recommendation?
A recommendation is a structured suggestion to either update or remove a specific entity. It always has:
- A type — either
create,update(change one or more fields) orremove(delete the entry) - A category — the broad area the recommendation belongs to (e.g.
menu,campaign,journey) - A subcategory — optional further classification within the category
- A rationale — a plain-language explanation of why the change is being suggested
- A status — tracks where the recommendation is in its lifecycle
- A data payload — the target entity ID and the specific field values to change (for updates)
Recommendation Lifecycle
Every recommendation moves through the following statuses:
| Status | Meaning |
|---|---|
pending | Newly created, awaiting review |
accepted | Accepted via the /accept endpoint |
rejected | Dismissed via the /reject endpoint — no change will be applied |
Recommendation Fields
| Field | Type | Required | Description |
|---|---|---|---|
id | integer | — | Unique identifier |
placeId | integer | — | The place this recommendation belongs to |
type | string | Yes | create, update or remove |
category | string | Yes | The area this recommendation targets (e.g. menu, campaign, journey) |
subcategory | string | No | Optional further classification within the category |
rationale | string | Yes | Human-readable explanation of why this change is recommended (10–500 characters) |
data | object | No | The target entity and proposed changes — structure depends on category (see below) |
status | string | — | pending, accepted or rejected. Defaults to pending. |
created | integer | — | Unix timestamp (seconds) when the recommendation was created |
updated | integer | — | Unix timestamp (seconds) of the last update |
The data Object
The shape of data is flexible and depends on what is being recommended. At minimum it identifies the target entity. For update recommendations it also contains the proposed field changes.
Example — an update recommendation:
{
"id": 1042,
"type": "item",
"title": "Crispy Chicken Burger",
"description": "Juicy fried chicken with pickles and sriracha mayo on a toasted brioche bun."
}Example — a remove recommendation:
{
"id": 2071,
"type": "campaign"
}Only fields that differ from the current values are included in update payloads — if the suggested value already matches the existing value, it is omitted.
API Reference
All endpoints are under /places/{placeId}/recommendations and require a valid Api-Key header. The authenticated user must have cashier access to the place.
| Method | Path | Description |
|---|---|---|
GET | /places/{placeId}/recommendations | List all recommendations for a place (paginated) |
GET | /places/{placeId}/recommendations/{id} | Get a single recommendation by ID |
POST | /places/{placeId}/recommendations | Create a recommendation manually |
PUT | /places/{placeId}/recommendations/{id} | Update a recommendation (e.g. change status) |
DELETE | /places/{placeId}/recommendations/{id} | Delete a recommendation |
POST | /places/{placeId}/recommendations/{id}/accept | Accept a recommendation (sets status to accepted) |
POST | /places/{placeId}/recommendations/{id}/reject | Reject a recommendation (sets status to rejected) |
Listing Recommendations
Supports standard pagination query parameters: page, limit, where (URL-encoded JSON filter), order, and include.
Fetch all pending recommendations for a place:
GET /places/123/recommendations?where={"status":"pending"}Filter by category:
GET /places/123/recommendations?where={"category":"campaign","status":"pending"}Creating a Recommendation Manually
Required fields: type, category, rationale. Optional: data, subcategory.
POST /places/123/recommendations
Content-Type: application/json
Api-Key: <your-key>
{
"type": "update",
"category": "journey",
"rationale": "This journey has not been updated in over 6 months and the entry trigger no longer matches current customer behaviour.",
"data": {
"id": 77,
"type": "journey"
}
}Common Workflows
Reviewing recommendations
GET /places/{placeId}/recommendations?where={"status":"pending"}— fetch all pending suggestions- Read each recommendation's
rationaleanddatato understand what is proposed and why - Use
POST /{id}/acceptto accept the recommendation - Use
POST /{id}/rejectto reject the recommendation
Filtering by category
Use the where parameter to scope the list to a specific category:
GET /places/123/recommendations?where={"category":"menu","type":"update"}API Examples
Fetch all pending recommendations
GET /places/123/recommendations?where={"status":"pending"}
Api-Key: <your-key>Fetch recommendations for a specific category
GET /places/123/recommendations?where={"category":"campaign","status":"pending"}
Api-Key: <your-key>Create a menu item update recommendation
POST /places/123/recommendations
Content-Type: application/json
Api-Key: <your-key>
{
"type": "update",
"category": "menu",
"rationale": "The item title is generic and does not describe the dish. A more descriptive title will improve discoverability.",
"data": {
"id": 1042,
"type": "item",
"title": "Crispy Chicken Burger",
"description": "Juicy fried chicken with pickles and sriracha mayo on a toasted brioche bun."
}
}Create a menu section removal recommendation
POST /places/123/recommendations
Content-Type: application/json
Api-Key: <your-key>
{
"type": "remove",
"category": "menu",
"rationale": "This section contains only one item and has not received any orders in the past 90 days. Removing it will reduce menu clutter.",
"data": {
"id": 88,
"type": "section"
}
}Create a campaign update recommendation
POST /places/123/recommendations
Content-Type: application/json
Api-Key: <your-key>
{
"type": "update",
"category": "campaign",
"rationale": "The campaign end date has passed but the campaign is still active. It should be deactivated.",
"data": {
"id": 55,
"type": "campaign",
"active": false
}
}Create a journey recommendation
POST /places/123/recommendations
Content-Type: application/json
Api-Key: <your-key>
{
"type": "update",
"category": "journey",
"subcategory": "win-back",
"rationale": "The win-back journey targets customers inactive for 30 days, but the average re-engagement window for this place is 60 days. Adjusting the trigger delay will improve conversion.",
"data": {
"id": 12,
"type": "journey"
}
}Accept a recommendation
POST /places/123/recommendations/456/accept
Api-Key: <your-key>Reject a recommendation
POST /places/123/recommendations/456/reject
Api-Key: <your-key>Delete a recommendation
DELETE /places/123/recommendations/456
Api-Key: <your-key>List recommendations with pagination and sorting
Fetch the 10 most recently created pending recommendations:
GET /places/123/recommendations?where={"status":"pending"}&limit=10&order=-created
Api-Key: <your-key>
Comments