API Docs

Global settings

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-lists

Response

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 pattern

Product 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.