Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

019 - Internal Active Price Modifiers API

Status: ✅ Implementation Complete
Last Updated: September 24, 2026
Version: 1.0


Overview

GET /api/internal/v1/price_modifiers supplies trusted backend consumers with the active, denormalized PriceModifier records needed to evaluate package pricing outside Pricing Engine.

The endpoint is deliberately a data source, not a pricing calculator. It does not choose a winning modifier, apply a modifier to a price, or expose the complete database relation graph. The consuming service evaluates package, date/time, demand-capacity, priority, and value semantics from the returned definitions.

Endpoint and Authentication

GET /api/internal/v1/price_modifiers
X-Internal-Api-Key: <INTERNAL_API_KEY>

This is an Internal API endpoint. The key must match INTERNAL_API_KEY; an IP allowlist may also reject the request when it is enabled by deployment configuration. It must never be called from a browser or expose the key to a client application.

Request

restaurant_ids is required and accepts a single ID, comma-separated IDs, or an array.

GET /api/internal/v1/price_modifiers?restaurant_ids=1631,1640
GET /api/internal/v1/price_modifiers?restaurant_ids[]=1631&restaurant_ids[]=1640

Returned Modifier Selection

The endpoint returns only records meeting all of the following conditions:

  • The modifier belongs to one of the requested restaurants.
  • The modifier has status: "active".
  • Its own end date is open-ended or is today or later.
  • If it belongs to a parent PricingRule, that rule is active and its end date is open-ended or is today or later.

Future start_date values are intentionally returned. This lets a consumer cache or evaluate a modifier that becomes applicable later. The consumer must still compare its target date/time with the returned schedule before applying the modifier.

Snapshot and Pagination

The endpoint intentionally has no pagination. Each requested restaurant returns its complete matching modifier set so a consumer can atomically replace its cached snapshot; an empty data result means the cached set must be cleared.

PricingRule Kafka Refresh Event

Pricing Engine publishes an invalidation only after a PricingRule create, update, or delete commits. This includes a display-only rule_name update. It does not publish this event for a direct PriceModifier, DateTimeCondition, or ConditionItem change that does not save the parent PricingRule.

Topic and Key

The topic is derived from the final segment of NAMESPACE; it is not supplied by a separate environment variable.

NAMESPACETopic
locallocal.hh.pricing.modifiers.changed
hh-engineeringengineering.hh.pricing.modifiers.changed
unsetproduction.hh.pricing.modifiers.changed

The Kafka record key is the string restaurant_id, preserving order for one restaurant within a partition.

Event Contract

{
  "event_id": "b1e6cc76-39da-45de-bad7-7d70b28bd3ac",
  "event_type": "pricing_modifiers.changed",
  "schema_version": 1,
  "source": "pricing-engine",
  "restaurant_id": "1631",
  "pricing_rule_id": 45,
  "action": "updated",
  "occurred_at": "2026-09-25T08:00:00Z"
}

action is one of created, updated, or deleted. The event deliberately does not contain modifier definitions, package IDs, or price values. pricing_rule_id is diagnostic context only: consumers always refresh by restaurant_id.

Consumer Contract

Felidae (or another downstream evaluator) must:

  1. Subscribe to the namespace-derived topic using its own durable consumer group and read permission.
  2. On an event, call GET /api/internal/v1/price_modifiers with that single restaurant_id and its server-side internal API key.
  3. Atomically replace the restaurant’s cached modifier snapshot. An empty data response clears the cache for that restaurant.
  4. Treat repeated or out-of-order deliveries safely. Publishing is retried, so refreshing must be idempotent; the event is a re-fetch signal, not a delta.

Pricing Engine requires producer permission for this topic. The consuming service requires consumer permission and a deployed consumer/subscription; deploying Pricing Engine alone does not start a Felidae consumer.

Response Contract

{
  "success": true,
  "message": "",
  "data": [
    {
      "id": 123,
      "restaurant_id": "1631",
      "pricing_rule_id": 45,
      "rule_name": "Weekday dinner promotion",
      "strategy_type": "specific_date_time",
      "priority": 10,
      "weight": 50,
      "created_at": "2026-09-24T10:00:00Z",
      "date_type": "specific_date_time",
      "period_mode": "range",
      "start_date": "2026-09-23",
      "end_date": null,
      "is_no_end_date": true,
      "start_time": "18:00",
      "end_time": "20:00",
      "is_no_end_time": false,
      "is_anytime": false,
      "days_of_week": [1, 2, 3, 4, 5],
      "action_type": "decrease",
      "value_type": "percentage",
      "value": 10.0,
      "apply_to_all_packages": false,
      "package_ids": ["pkg_a", "pkg_b"],
      "demand_capacity": null
    }
  ],
  "meta": {
    "restaurant_ids": ["1631"],
    "count": 1
  }
}

Field Groups

GroupFieldsConsumer use
Identifierid, restaurant_idStable modifier identity and restaurant ownership.
Portal cross-checkpricing_rule_id, rule_nameLink the returned modifier to the Partner Portal/Admin pricing rule for investigation. They do not affect pricing evaluation.
Classification and precedencestrategy_type, priority, weight, created_atIdentify the modifier strategy and resolve precedence.
Scheduledate_type, period_mode, start_date, end_date, is_no_end_date, start_time, end_time, is_no_end_time, is_anytime, days_of_weekDetermine whether the modifier applies at the target date/time.
Price changeaction_type, value_type, valueDetermine how the price changes.
Package scopeapply_to_all_packages, package_idsDetermine whether the modifier applies to the package.
Capacity scopedemand_capacityEvaluate a demand-capacity modifier when present.

demand_capacity, when non-null, contains condition_type, date_type, period_mode, and capacity_condition_items. Each item provides relation_type, value_min, value_max, is_no_end_value, and is_anytime.

Intentionally Omitted Fields

The response does not expose database-only relation IDs such as date/time condition IDs, condition item IDs, demand-capacity condition IDs, or demand capacity time-window IDs. It also omits rule_type, source, and status: the first duplicates the strategy classification, while source/status are not needed after the endpoint has already selected active records.

Evaluation Order

Records are sorted by:

  1. restaurant_id ascending
  2. priority ascending
  3. weight descending
  4. created_at descending
  5. id ascending

The consumer should first select the relevant restaurant and package, evaluate the returned schedule and optional capacity predicates for the target booking, then use this order when resolving applicable modifiers. The endpoint does not define how a consuming service combines multiple applicable modifiers; it only provides the deterministic input order.

Errors

StatusMeaning
400 Bad Requestrestaurant_ids is missing or blank.
401 UnauthorizedThe API key is missing, invalid, or rejected by the configured IP allowlist.
500 Internal Server ErrorThe modifier definitions could not be loaded.