Add price lists
Creates a new Price List and Expected Forward Cost List for a market
POST/https://europe-west1-mpower-apps-production.cloudfunctions.net/api-add-price-lists
Headers
Content-Type
application/json
string
x-api-key
<API_KEY>
string
Body Parameters
info.market_id
Required, not empty.
ABC
string
info.boq_list_id
Required, not empty. This is the Base44 price list ID.
base44-list-id
string
info.currency_iso
Required 3-letter code. Normalized to uppercase.
EUR
string
info.date
Required strict MM/YYYY format, with month between 01 and 12.
06/2026
string
info.name
Required, not empty.
Some name for the price list
string
products
Required non-empty object. Each key is a Firestore product document ID.
{ product_id: ... }
Record<string, ProductPrices>
products.{PRODUCT_ID}
PRODUCT_ID must be a valid Firestore document ID. See Product ID rules below.
{ price_list_price, expected_forward_cost_price }
ProductPrices
products.*.price_list_price
Required finite number greater than or equal to 0.
100
number
products.*.expected_forward_cost_price
Required finite number greater than or equal to 0.
120
number
cURL
curl -i \
-X POST \
-H "Content-Type: application/json" \
-H "x-api-key: <API_KEY>" \
-d '{
"info": {
"market_id": "ABC",
"boq_list_id": "base44-list-id",
"currency_iso": "EUR",
"date": "06/2026",
"name": "Some name for the price list"
},
"products": {
"product_id": {
"price_list_price": 100,
"expected_forward_cost_price": 120
}
}
}' \
https://europe-west1-mpower-apps-production.cloudfunctions.net/api-add-price-listsResponse
200 Success - Market found (simplified response)
{
"success": true,
"message": "Payload is valid and stored",
"products_count": 1,
"document_id": "apiTestDocumentId",
"market": {
"id": "marketDocumentId",
"name": "Switzerland",
"iso": "CHE"
},
"expected_forward_cost": {
"id": "expectedForwardCostListId",
"status": "ready",
"created_at": 1780000000000,
"updated_at": 1780000000000,
"currency": "CHF",
"market_id": "marketDocumentId",
"market_iso": "CHE",
"name": "June 2026 expected costs",
"new_date": {
"year": 2026,
"month": 6
}
},
"price_list": {
"id": "priceListId",
"status": "ready",
"created_at": 1780000000000,
"updated_at": 1780000000000,
"currency": "CHF",
"market_id": "marketDocumentId",
"market_iso": "CHE",
"name": "June 2026 price list",
"date": "06/2026",
"new_date": {
"year": 2026,
"month": 6
}
}
}200 Success - Market not found
{
"success": true,
"message": "Payload is valid and stored",
"products_count": 1,
"document_id": "apiTestDocumentId",
"market": null,
"expected_forward_cost": null,
"price_list": null
}400 Validation error
{
"success": false,
"message": "Invalid payload",
"errors": [
{
"path": "products.PRODUCT_ID.price_list_price",
"message": "Number must be greater than or equal to 0"
}
]
}401 Unauthorized
{
"error": "Unauthorized"
}405 Method not allowed
{
"error": "Method not allowed"
}409 Idempotency conflict
{
"error": "Idempotency key already used with a different payload"
}
or
{
"error": "Request is already being processed or requires review"
}500 API key is not configured
{
"error": "API key is not configured"
}Behavior / Side effects
Idempotency
- info.boq_list_id is used as the idempotency key.
- Same boq_list_id and same payload returns the previously stored successful response and does not create duplicate documents.
- Same boq_list_id and different payload returns 409.
- Existing processing or review request with the same boq_list_id returns 409.
Firestore writes
If the market does not exist, only the api_test document is created.
For every new valid request that is not a completed idempotent retry, creates:
api_test/{autoGeneratedId}If info.market_id matches an existing market, also creates:
markets/{market_id}/market_landed_cost/{expected_forward_cost_id}
markets/{market_id}/market_landed_cost/{expected_forward_cost_id}/market_landed_cost_products/{product_id}
markets/{market_id}/market_price_lists/{price_list_id}
markets/{market_id}/market_price_lists/{price_list_id}/market_price_lists_products/{product_id}Parent document status
- Parent documents are updated to status: "ready" only after all child product documents have been written successfully.
- If an error happens before the parent documents are marked as ready, the endpoint attempts to mark created parent documents as status: "failed".
- Consumers should only use documents with status: "ready".
The parent documents are created with:
{
"status": "processing"
}They are updated to:
{
"status": "ready"
}Failure fallback:
{
"status": "failed"
}Product ID rules
- Each key inside products is used as a Firestore document ID in the generated product subcollections.
- If a product ID is invalid, the endpoint returns 400 with a field-level validation error.
Invalid PRODUCT_ID values:
" product_id" starts with whitespace
"product_id " ends with whitespace
"products/123" contains "/"
"." reserved Firestore path segment
".." reserved Firestore path segment
"__product__" reserved Firestore ID patternProduct writes and batching
- Products are written in Firestore batches of up to 500 operations.
- If more than 500 products are received, the endpoint splits the writes into multiple sequential batches.
- Each product in products creates one document in each child collection.
- The document ID is the product ID from the payload.
Child product collections:
market_landed_cost_products/{product_id}
market_price_lists_products/{product_id}Date handling
- info.date must use MM/YYYY.
- For both expected_forward_cost.new_date and price_list.new_date, 06/2026 is stored as the object below.
- The month is 1-based.
{
"year": 2026,
"month": 6
}No rollback guarantee
- The endpoint does not perform rollback if a later Firestore operation fails.
- Instead, it uses parent document status fields to indicate whether generated lists are usable.
- If a request fails after creating some documents, internal consumers should ignore any parent documents that are not ready.