Pricing Recommendation API
Overview
The Pricing Recommendation API provides partner-portal endpoints for AI-driven pricing recommendations. It integrates with RapidCanvas to:
- Preview predicted revenue for a restaurant without exposing the upstream API key to the frontend.
- Generate initial or refreshed recommendations for active AI-driven pricing rules.
- Convert RapidCanvas recommendations into pending price modifiers that require partner approval before becoming active.
- Send approve/reject decisions back to RapidCanvas as feedback.
The flow is:
Partner Portal → GET /pricing/recommendations/predict
(preview predicted revenue)
Partner Portal → POST /pricing/recommendations/initial
(trigger initial recommendation for a rule)
Daily Job → RecommendationFetchJob
(fetches recommendations for all active ai_driven rules)
Backend → PriceModifier::CreateFromRecommendation
(creates pending price modifiers)
Partner Portal → POST /price_modifiers/:id/approve
Partner Portal → POST /price_modifiers/:id/reject
(approve or reject pending modifiers)
Backend → Recommendation::NotifyDecision
(sends decision feedback to RapidCanvas)
Authentication
- Requires Partner API bearer token authentication.
- Restaurant access is validated using the authenticated partner context.
restaurant_idis normalized from the authorized restaurant context.
Required Environment Variables
RAPIDCANVAS_API_KEY- RapidCanvas API key.
Optional Environment Variables
RAPIDCANVAS_BASE_URL- Defaults tohttps://app.rapidcanvas.ai.RAPIDCANVAS_RECOMMENDATION_DECISION_PATH- Optional explicit feedback endpoint for approve/reject callbacks. If omitted, the backend builds a default decision path based on the recommendation ID.
Internal Defaults (Code Constants)
- Initial recommendation path:
RapidCanvas::Client::INITIAL_RECOMMENDATION_PATH - Refresh recommendation path:
RapidCanvas::Client::REFRESH_RECOMMENDATION_PATH - Predicted revenue path:
RapidCanvas::Client::PREDICTED_REVENUE_PATH - Request timeout:
RapidCanvas::Client::DEFAULT_TIMEOUT_SECONDS(20) - Open timeout:
RapidCanvas::Client::DEFAULT_OPEN_TIMEOUT_SECONDS(10)
Retry Policy
Handled by Http::RetryWithBackoff:
- Maximum retries:
3 - Exponential backoff:
500ms,1000ms,2000ms - Retried only for:
- timeout
- network errors
- HTTP
429 - HTTP
5xx
- Not retried for other
4xxresponses
Endpoints
1. Predicted Revenue Preview
GET /api/partner/v1/pricing/recommendations/predict
Loads predicted revenue data for the AI-driven rule form. This endpoint does not
call RapidCanvas recommendation generation and does not send goal_revenue or
global_cap.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
restaurant_id | integer | yes | Restaurant context used by partner auth/access checks. |
target_period_type | string | no | Target period selector: day_end, week_end, or month_end. |
target_revenue / target_sale | float | no | Optional revenue goal used to compute delta_to_goal. |
Example Request
GET /api/partner/v1/pricing/recommendations/predict?restaurant_id=370&target_period_type=month_end&target_revenue=800000
Example Success Response
{
"success": true,
"message": "Predicted revenue loaded successfully",
"data": {
"restaurant_id": 370,
"target_period_type": "month_end",
"prediction": {
"organic_projection_revenue_amount": 671773.0,
"confidence": "high"
},
"history": {
"last_month_revenue_amount": 650000.25,
"last_year_same_month_revenue_amount": 710000.75
},
"constraints": {
"demand_ceiling_revenue_amount": 950500.5
},
"predicted_revenue": {
"revenue_amount": 671773.0,
"observed_so_far_amount": 120000.0,
"projected_remaining_amount": 551773.0
},
"window": {
"days_in_window": 30,
"days_observed": 5,
"days_remaining": 25
},
"goal": {
"target_revenue": 800000.0,
"delta_to_goal": 128227.0
}
}
}
2. Initial Recommendation
POST /api/partner/v1/pricing/recommendations/initial
Triggers an initial RapidCanvas recommendation request for a single AI-driven
pricing rule. Internally it uses the same Recommendation::GetPredictedRevenue
flow as the predict preview (the endpoint is currently aliased to the same
handler).
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
restaurant_id | integer | yes | Restaurant context. |
target_period_type | string | no | day_end, week_end, month_end, or custom. |
target_revenue / target_sale | float | no | Revenue goal. |
Example Request
POST /api/partner/v1/pricing/recommendations/initial
Content-Type: application/json
{
"restaurant_id": 370,
"target_period_type": "month_end",
"target_revenue": 800000
}
Response
Same response contract as GET /pricing/recommendations/predict.
3. Approve Pending Price Modifier
POST /api/partner/v1/price_modifiers/:id/approve
Approves a pending AI-generated price modifier, changing its status to
active. The backend notifies RapidCanvas of the approval decision.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | integer | Price modifier ID. |
Response
Returns the approved price modifier and optional RapidCanvas feedback result.
{
"success": true,
"message": "Price modifier approved",
"data": {
"modifier": { /* price modifier details */ },
"rapidcanvas": { /* upstream feedback response */ }
}
}
4. Reject Pending Price Modifier
POST /api/partner/v1/price_modifiers/:id/reject
Rejects a pending AI-generated price modifier, changing its status to
rejected. The backend notifies RapidCanvas of the rejection decision.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | integer | Price modifier ID. |
Response
{
"success": true,
"message": "Price modifier rejected",
"data": {
"modifier": { /* price modifier details */ },
"rapidcanvas": { /* upstream feedback response */ }
}
}
5. Pending Modifier Count
GET /api/partner/v1/price_modifiers/pending_count
Returns the number of pending AI-generated price modifiers for the authorized restaurant.
Background Jobs
RecommendationFetchJob
- Schedule: Daily (configured in
config/recurring.yml). - Queue:
recommendations. - Optional parameter:
date(ISO date string, defaults to today in ICT).
The job delegates to Recommendation::FetchActiveRules, which:
- Finds active
ai_drivenpricing rules valid on the target date. - Builds RapidCanvas payloads from the saved AI setup context
(
AiRecommendation). - Calls
/initialfor new rules or/refreshfor rules with existing recommendations. - Converts RapidCanvas responses into pending price modifiers via
PriceModifier::CreateFromRecommendation.
Recommendation Lifecycle
- Setup - A pricing rule is created with
rule_type: "ai_driven"and an associatedAiRecommendationcontext (target_sale,target_sale_period, etc.). - Fetch -
RecommendationFetchJobor the initial endpoint requests a recommendation from RapidCanvas. - Adapt -
Recommendation::RapidCanvas::ResponseAdapternormalizes the upstream response into a price modifier shape. - Create -
PriceModifier::CreateFromRecommendationcreates one or more pendingPriceModifierrecords linked to the pricing rule. - Decide - Partner approves or rejects the pending modifier.
- Feedback -
Recommendation::NotifyDecisionsends the decision to RapidCanvas for model feedback.
Global Cap Builder
Recommendation::RapidCanvas::GlobalCapBuilder reads the restaurant’s global
pricing settings and builds the global_cap payload sent to RapidCanvas:
{
"increase": { "type": "percent", "value": 20.0 },
"decrease": { "type": "percent", "value": 15.0 }
}
Supported cap types: percent and amount. Amount caps require a currency.
Error Handling
All recommendation endpoints return standardized error responses.
Example Error Response
{
"success": false,
"message": "RapidCanvas upstream failure: Service temporarily unavailable",
"data": null
}
Validation errors include an errors object:
{
"success": false,
"message": "Invalid predicted revenue request payload",
"errors": {
"restaurant_id": ["must be greater than 0"]
}
}
Error Codes
| Scenario | HTTP Status |
|---|---|
| Invalid request payload | 422 Unprocessable Content |
| RapidCanvas timeout | 504 Gateway Timeout |
| RapidCanvas network failure | 502 Bad Gateway |
| RapidCanvas upstream error | 502 Bad Gateway |
| Unexpected RapidCanvas response | 502 Bad Gateway |
| Missing RapidCanvas configuration | 503 Service Unavailable |
Notes
- The backend keeps the RapidCanvas API key server-side only.
- Validation happens before any upstream call.
- Unexpected upstream payloads are rejected with a standardized error response.
- Retry logging includes retry attempt, reason, and status when available.
- Approve/reject notifications to RapidCanvas are best-effort; modifier status is updated even if the upstream feedback call fails.