PIZZA FASHION · DEVELOPER DOCUMENTATION
Catalogue and orders.
One integration.
Live menu data into your platform. Orders from your platform into Pizza Fashion. Every field, option and example in one place.
https://api.pizzafashion.bg/v1Language selection affects documentation only. JSON keys are unchanged; dish names and some restaurant messages remain Bulgarian.
Quick start
This is a server-to-server integration. You download the menu; you submit orders to us. There is no endpoint to modify our products.
- Obtain a separate API key/password from Pizza Fashion. The key is the Basic username; the password is the Basic password.
- Download every /catalogue page and read /availability.
- Build items + choice and call /quote. Show the final price to the customer.
- After confirmation, submit /orders with the same items/choice, quote_token and a permanent external_order_id.
- Save order_number. Poll only your own submitted orders through /orderstatus.
Use HTTPS with certificate verification, Accept: application/vnd.api+json, and Content-Type: application/json for POST. Maximum JSON body: 128 KiB. Do not automatically follow redirects with credentials.
Every operation, including the catalogue, requires authentication. Success is HTTP 200 with data under meta.data. Controller errors use meta.error; gateway/routing errors may use other JSON envelopes or text. Check HTTP status first.
export PF_API_KEY='<API_KEY>'
export PF_API_PASSWORD='<API_PASSWORD>'
curl --fail-with-body --user "$PF_API_KEY:$PF_API_PASSWORD" \
--header 'Accept: application/vnd.api+json' \
'https://api.pizzafashion.bg/v1/catalogue?page=1'{
"meta": {
"data": {
"currency": "EUR",
"page": 1,
"has_more": true
}
}
}/catalogue?page=1Products: download and synchronization
The only query parameter is page: integer1–10000, default1. Up to50products are returned. Continue until has_more=false. Category/combo metadata may repeat on every page: merge by id/code.
Display cards only for listed=true. Retain listed=false records because choices may reference them. Available=false prevents ordering; server rechecks availability and expired combos.
Sync approximately once per minute within your request limit. Replace local catalogue data only after a complete successful cycle. Hide products absent from a complete new catalogue. Keep the last successful copy on failed/partial sync.
A size’s price is variants[].minor, not a surcharge over base price. Use variants[].addons for size-specific extra prices. A flavour/package choice in choices selects a different product_id, with its own recipe and price.
curl --fail-with-body --user "$PF_API_KEY:$PF_API_PASSWORD" \
--header 'Accept: application/vnd.api+json' \
'https://api.pizzafashion.bg/v1/catalogue?page=2'/availabilityZones, receipt and payment
Response contains zones, collection location and ordering_enabled for the authenticated partner. This flag does not mean every zone is open now. Opening hours use Europe/Sofia; /quote enforces exact minimums, late periods, closures and fees.
Delivery: fulfilment=delivery + a returned zone_id. Collection: fulfilment=pickup + location_id=main. Never switch zone/receipt method to bypass minimums or opening hours.
This version accepts payment=cash only: cash on delivery/collection. No online payment or processing fee is taken through this API. POS on receipt is not a separate supported API method and requires separate agreement.
Promo codes, FASHION, personal vouchers, external prices and paid=true are not accepted. Combo discounts are already reflected in prices. No customer directory, Club accounts, points or other channels’ orders are exposed.
curl --fail-with-body --user "$PF_API_KEY:$PF_API_PASSWORD" \
--header 'Accept: application/vnd.api+json' \
'https://api.pizzafashion.bg/v1/availability'/quotePricing and product options
Send items and choice. Response contains quote.total_minor, quote_token and expires_at. A quote creates no order, reserves no stock and charges nothing. It expires after about 15 minutes; obtain and reconfirm a fresh quote when price/configuration/rules change.
Normal product: type=product, catalogue product_id and size. Removed is a list of removable ingredient IDs; addons is a list of allowed extra codes; base is one pizza-base code. Initialize crust from crust_included and change it only when crust_selectable=true.
Combo: copy combos[].default_selection including code/revision and ordered items. Change product_id only within slot.product_ids; preserve size and count. Do not add nested quantity. Selected modifications may increase the final amount.
Half-and-half: only when half.enabled=true and both pizzas are half_eligible with a family variant. Send exactly two halves with product_id, size=family and removed; put the shared base on the outer selection. Extras and crust changes per half are unsupported. Use /quote for the final price.
Retain the original items and choice. Never substitute quote.items[].configuration for request.selection: the response contains read-only fields rejected in requests.
{
"items": [
{
"selection": {
"type": "product",
"product_id": 123,
"size": "medium",
"removed": [],
"crust": 0,
"addons": []
},
"quantity": 1
}
],
"choice": {
"fulfilment": "pickup",
"location_id": "main",
"payment": "cash"
}
}
curl --fail-with-body --user "$PF_API_KEY:$PF_API_PASSWORD" \
--header 'Accept: application/vnd.api+json' \
--header 'Content-Type: application/json' \
--data-binary @quote-request.json \
'https://api.pizzafashion.bg/v1/quote'{
"type": "product",
"product_id": 123,
"size": "family",
"removed": [
17
],
"crust": 1,
"addons": [
"garlic_sauce"
],
"base": "tomato"
}{
"type": "combo",
"combo_code": "combo-example",
"combo_revision": "<revision-from-catalogue>",
"items": [
{
"product_id": 123,
"size": "medium",
"removed": [],
"crust": 0,
"addons": []
},
{
"product_id": 124,
"size": "standard",
"removed": [],
"crust": 0,
"addons": []
}
]
}{
"type": "half",
"halves": [
{
"product_id": 123,
"size": "family",
"removed": []
},
{
"product_id": 125,
"size": "family",
"removed": [
17
]
}
],
"base": "tomato"
}{
"meta": {
"data": {
"quote": {
"currency": "EUR",
"total_minor": 790,
"delivery_minor": 0,
"schedule_ready": true
},
"quote_token": "<signed-token>",
"expires_at": "2026-10-18T12:15:00Z"
}
}
}/ordersOrders: submit to the restaurant
This is the inbound flow: your platform creates an order in our administration. Send external_order_id, original items/choice, customer, expected_total_minor from the quote and quote_token. Never send price, source, user_id, status or card data.
External_order_id is your permanent idempotency key. Create and persist it before the first HTTP request. Identical content with the same ID returns the same PF reference and replayed=true. Changed content with the same ID returns409. Array order matters; persist the request body.
After a timeout, never generate a new ID. Query /orderstatus and retry the original request with the same ID/content if needed.404 does not prove an in-flight submission failed. An already saved identical request may resume even with an expired token; this does not authorize a new order from an expired quote.
Name and complete phone are required. Provide the customer’s actual email when available; restaurant configuration may require it. Delivery needs street, number and necessary details. Do not substitute partner contacts. This creates neither a registered account nor marketing consent.
Source is determined by authenticated partner identity. OneBite orders are automatically marked OneBite in administration and Telegram. HTTP 200 confirms persistence; it does not separately guarantee Telegram/email/SMS delivery.
{
"external_order_id": "ONEBITE-2026-10001",
"items": [
{
"selection": {
"type": "product",
"product_id": 123,
"size": "medium",
"removed": [],
"crust": 0,
"addons": []
},
"quantity": 1
}
],
"choice": {
"fulfilment": "pickup",
"location_id": "main",
"payment": "cash"
},
"customer": {
"name": "Customer name",
"phone": "0888123456",
"email": "customer@example.com",
"note": "Doorbell 3"
},
"expected_total_minor": 790,
"quote_token": "<quote_token-from-matching-quote>"
}
curl --fail-with-body --user "$PF_API_KEY:$PF_API_PASSWORD" \
--header 'Accept: application/vnd.api+json' \
--header 'Content-Type: application/json' \
--data-binary @order-request.json \
'https://api.pizzafashion.bg/v1/orders'{
"meta": {
"data": {
"external_order_id": "ONEBITE-2026-10001",
"order_number": "PF-A2000",
"status": "accepted",
"status_label": "Приета",
"currency": "EUR",
"total_minor": 790,
"payment": "cash",
"paid": false,
"created_at": "2026-10-18T12:00:00Z",
"updated_at": "2026-10-18T12:00:00Z",
"replayed": false
}
}
}const base = 'https://api.pizzafashion.bg/v1';
const auth = Buffer.from(
process.env.PF_API_KEY + ':' + process.env.PF_API_PASSWORD
).toString('base64');
async function pf(path, body) {
const response = await fetch(base + path, {
method: body === undefined ? 'GET' : 'POST',
redirect: 'error',
signal: AbortSignal.timeout(20_000),
headers: {
Authorization: 'Basic ' + auth,
Accept: 'application/vnd.api+json',
...(body === undefined ? {} : {'Content-Type':'application/json'})
},
...(body === undefined ? {} : {body:JSON.stringify(body)})
});
const raw = await response.text();
let payload;
try { payload = JSON.parse(raw); } catch { payload = null; }
if (!response.ok) {
// Do not log Authorization, raw contact data or the entire body.
const error = new Error(payload?.meta?.error?.message || 'API request failed');
error.status = response.status;
error.retryAfter = response.headers.get('retry-after');
throw error;
}
if (!payload?.meta?.data) throw new Error('Unexpected response envelope');
return payload.meta.data;
}
async function readCompleteCatalogue() {
const products = new Map(), combos = new Map();
for (let page = 1; ; page++) {
const data = await pf('/catalogue?page=' + page);
for (const product of data.products) products.set(product.id, product);
for (const combo of data.combos) combos.set(combo.code, combo);
if (!data.has_more) return {products:[...products.values()], combos:[...combos.values()]};
if (page >= 10000) throw new Error('Pagination limit');
}
}
async function priceSelection(items, choice) {
const result = await pf('/quote', {items, choice});
// Persist original items/choice; show result.quote.total_minor to customer.
return result;
}
async function sendConfirmedOrder(savedRequest) {
// Persist external_order_id and this exact payload BEFORE calling.
// Call only after the customer confirms the quoted total.
return pf('/orders', savedRequest);
}
async function checkOwnOrder(externalOrderId) {
return pf('/orderstatus', {external_order_id:externalOrderId});
}
// After an ambiguous timeout: checkOwnOrder(savedRequest.external_order_id).
// If needed retry sendConfirmedOrder(savedRequest), preserving ID and content.
// This sample defines functions only. It creates no order automatically./orderstatusStatus and retry handling
Send external_order_id only. Returns status only for this partner’s order, without customer contacts. V1 has no other-channel lookup, remote state change, cancellation or webhook endpoint. Contact the restaurant for changes.
Poll active orders every 30–60 seconds within the shared limit. On 429 respect Retry-After and add jitter; on 503 use bounded backoff with the same ID. Alert your operator after repeated failures.
acceptedRecorded in the restaurant system; staff continue processing.preparingBeing prepared.sentDispatched; for pickup this means ready for collection.deliveredDelivered / completed.cancelledCancelled.payment_reviewRequires manual review; contact the restaurant.
{
"external_order_id": "ONEBITE-2026-10001"
}
curl --fail-with-body --user "$PF_API_KEY:$PF_API_PASSWORD" \
--header 'Accept: application/vnd.api+json' \
--header 'Content-Type: application/json' \
--data-binary @status-request.json \
'https://api.pizzafashion.bg/v1/orderstatus'Complete attribute reference
Each list item describes one field. The path locates it; [] denotes an array element. Do not copy read-only response fields into an order request.
minor = integer EUR cents; 790 = EUR7.90. flag = boolean or 0/1. numeric = number or numeric string in responses; normalize before use. ? allows null. Product / half / combo marks a field used by that selection kind. Delivery / pickup depends on receipt method. Email depends on restaurant configuration.
No matching fields. Try another name.
Request headersHTTP headers3
Request to API · RequestHeaders
AuthorizationstringRequiredHTTP Basic: API key is username, separate password is password. Send only to the HTTPS API host.
Example value"Basic <base64(API_KEY:API_PASSWORD)>"AcceptstringRequiredExplicitly request the JSON API response format.
Example value"application/vnd.api+json"Content-TypestringConditional · postFor POST send a UTF-8 JSON object, not an HTML form or form-data.
Example value"application/json"
Catalogue paginationGET /catalogue query1
Request to API · CatalogueQuery
pageinteger 1–10000OptionalInteger1–10000, default1. Example: /catalogue?page=2. Other query parameters are unsupported.
Example value1
Catalogue responsemeta.data13
Response only · Catalogue
schemaintResponse onlyPayload schema version; currently 1.
Example value1currencyEURResponse onlyCurrency of all prices.
Example value"EUR"timezonestringResponse onlyTimezone used for restaurant opening hours.
Example value"Europe/Sofia"categoriesCategory[]Response onlyCategories; resolve them using each product’s category_ids.
Example valueNested fields: Category ↗productsProduct[]Response onlyUp to 50 products per page, including option children.
Example valueNested fields: Product ↗pageintResponse onlyCurrent page number, starting at 1.
Example value1page_sizeintResponse onlyPage size: 50.
Example value50totalintResponse onlyTotal product records, including unlisted option children.
Example value144has_moreboolResponse onlyWhen true, request the next page.
Example valuetruecombosCombo[]Response onlyCombo offers; merge repeated page metadata by code.
Example valueNested fields: Combo ↗pizza_basesBase[]Response onlyGlobal pizza bases; use products[].bases for product eligibility.
Example valueNested fields: Base ↗halfHalfSettings objectResponse onlyHalf-and-half configuration.
Example valueNested fields: HalfSettings ↗generated_atRFC3339 stringResponse onlyGeneration timestamp, RFC3339.
Example value"2026-10-18T12:00:00+00:00"
Categoriescategories[]4
Response only · Category
idintResponse onlyStable category identifier.
Example value7namestringResponse onlyPublic category name.
Example value"Пици"iconstringResponse onlyIcon code; presentation metadata.
Example value"pizza"countintResponse onlyMenu product count for the category.
Example value24
Product attributesproducts[]18
Response only · Product
idintResponse onlyStable ID; send it as selection.product_id.
Example value123namestringResponse onlyDisplay name; changing documentation language does not translate dish names.
Example value"Пица Чикън"descriptionstringResponse onlyPlain text description without HTML.
Example value"Пилешко месо, моцарела"category_idsinteger[]Response onlyCategory IDs containing this product.
Example value[7]imageurl?Response onlyAbsolute HTTPS product image URL, or null.
Example value"https://pizzafashion.bg/images/example.webp"thumbnailurl?Response onlySmaller catalogue image URL, or null.
Example value"https://pizzafashion.bg/images/example-thumb.webp"image_illustrativeboolResponse onlyWhether the image is illustrative.
Example valuefalselistedboolResponse onlyFalse: flavour/package option child; retain it but do not display a separate card.
Example valuetrueminorminor?Response onlyBase displayed price in EUR cents. Use the selected variant price.
Example value790currencyEURResponse onlyCurrency EUR.
Example value"EUR"availableboolResponse onlyWhen false, disable ordering this product.
Example valuetrueavailable_atstr?Response onlyScheduled restock label/timestamp or null; does not override current available.
Example valuenullavailability_noticestringResponse onlyAvailability notice, when configured.
Example value""variantsVariant[]Response onlyAvailable sizes and their prices, ingredients and options.
Example valueNested fields: Variant ↗addonsAddon[]Response onlyAllowed extras; selected-size prices are in variants[].addons.
Example valueNested fields: Addon ↗basesBase[]Response onlyAllowed bases for this pizza; empty when inapplicable.
Example valueNested fields: Base ↗choicesChoiceGroup[]Response onlyFlavour/package choices referencing a different product_id.
Example valueNested fields: ChoiceGroup ↗half_eligibleboolResponse onlyEligible for half-and-half when enabled and a family variant exists.
Example valuetrue
Sizes / variantsproducts[].variants[]10
Response only · Variant
keystandard | medium | familyResponse onlyKey for selection.size; use only a returned variant.
Example value"medium"labelstringResponse onlyHuman-readable size label.
Example value"Средна"minorminor?Response onlyFull price for this size, not a size surcharge.
Example value790weight_gint?Response onlyWeight in grams, or null when unspecified.
Example value550ingredientsIngredient[]Response onlyIngredients; only entries with removable=1 may be removed.
Example valueNested fields: Ingredient ↗allergensinteger[1–14][]Response onlyNumeric codes 1–14; ingredient removal never guarantees allergen-free food.
Example value[1,7]crust_selectableboolResponse onlyWhether crust selection is permitted.
Example valuetruecrust_includedboolResponse onlyRecipe’s included crust; use as the initial crust selection.
Example valuefalsecrust_extra_minorminor?Response onlySurcharge for adding crust; null does not mean free.
Example value250addonsAddon[]Response onlyExtras priced for this exact size.
Example valueNested fields: Addon ↗
Ingredientsvariants[].ingredients[]3
Response only · Ingredient
idintResponse onlyID for selection.removed[] when removal is allowed.
Example value17namestringResponse onlyIngredient name.
Example value"Лук"removableboolean | 0 | 1Response only1/true permits removal; 0/false prohibits it.
Example value1
Sauces and extrasproducts[].addons[] / variants[].addons[]11
Response only · Addon
codestringResponse onlySend this code in selection.addons[], without duplicates.
Example value"garlic_sauce"namestringResponse onlyExtra name for the selected size.
Example value"Чеснов сос"groupsauce | toppingResponse onlyExtra group: sauce or pizza topping.
Example value"sauce"enabledboolean | 0 | 1Response onlyDefinition enabled flag; catalogue includes permitted extras.
Example value1minorinteger · EUR centsResponse onlyPrice in EUR cents; variant-level value is size-specific.
Example value100allergensinteger[1–14][]Response onlyAllergen codes of this extra.
Example value[7]reviewedboolean | 0 | 1Response onlyWhether allergen data has been reviewed.
Example value1family_minorinteger · EUR centsResponse onlyOptional family-size price metadata.
Example value200weight_gintResponse onlyExtra weight in grams when known.
Example value50family_weight_gintResponse onlyOptional family-size weight.
Example value100family_namestringResponse onlyOptional family-size display label.
Example value"Добавка — 100 г"
Pizza basesproducts[].bases[] / pizza_bases[]7
Response only · Base
codestringResponse onlyCode for selection.base; this is not a paid extra.
Example value"tomato"namestringResponse onlyDisplay label.
Example value"Доматено пюре"enabledboolean | 0 | 1Response onlyDefinition flag; may be absent in normalized quote bases.
Example value1defaultboolean | 0 | 1Response onlyDefault base, used when base is omitted.
Example value1minorinteger · EUR centsResponse onlyBases are included; value is 0.
Example value0allergensinteger[1–14][]Response onlyBase allergen codes.
Example value[]reviewedboolean | 0 | 1Response onlyWhether allergen data is reviewed.
Example value1
Flavour / package choicesproducts[].choices[]3
Response only · ChoiceGroup
labelstringResponse onlyChoice group heading.
Example value"Избери вкус"show_priceboolResponse onlyWhether to display the price beside the option.
Example valuetrueoptionsChoiceOption[]Response onlyAlternatives, each using a separate product_id.
Example valueNested fields: ChoiceOption ↗
Choice optionchoices[].options[]4
Response only · ChoiceOption
product_idintResponse onlyAfter choosing, resolve this product and its own variants/extras.
Example value124labelstringResponse onlyOption label.
Example value"Без захар"minorinteger · EUR centsResponse onlyStandalone price of the target product.
Example value200currentboolResponse onlyWhether this option represents the current product.
Example valuefalse
Combo offerscombos[]10
Response only · Combo
codestringResponse onlyStable code for selection.combo_code.
Example value"combo-example"revisionstringResponse onlyCopy exactly into combo_revision; never invent a revision.
Example value"<revision-from-catalogue>"namestringResponse onlyPublic name.
Example value"Комбо предложение"descriptionstringResponse onlyShort description.
Example value"Пица и напитка"imageurl?Response onlyHTTPS image URL or null.
Example valuenullminorinteger · EUR centsResponse onlyDefault configuration price; options may add a surcharge.
Example value1490availableboolResponse onlyCurrent availability; server rechecks it on submission.
Example valuetrueschedulestringResponse onlyHuman-readable schedule, not authoritative ordering permission.
Example value"Всеки ден · през работното време"slotsComboSlot[]Response onlyOrdered slots; preserve their count and order.
Example valueNested fields: ComboSlot ↗default_selectionSelection objectResponse onlyReady-to-use request selection; change only permitted options.
Example valueNested fields: Selection ↗
Combo slotscombos[].slots[]2
Response only · ComboSlot
product_idsinteger[]Response onlyAllowed products for this slot; do not substitute other IDs.
Example value[123,124]sizestandard | medium | familyResponse onlyFixed slot size; cannot be changed.
Example value"medium"
Half-and-half settingshalf4
Response only · HalfSettings
enabledboolResponse onlyWhen false, do not offer the half-and-half builder.
Example valuetruesizefamilyResponse onlyHalf-and-half is available only in family size.
Example value"family"price_modehigher | average | fixed | unsetResponse onlyPricing mode; always obtain the final amount from /quote.
Example value"higher"fixed_minorminor?Response onlyFixed price for fixed mode; null does not mean zero.
Example valuenull
Receipt and opening hoursmeta.data7
Response only · Availability
currencyEURResponse onlyCurrency of monetary values.
Example value"EUR"timezonestringResponse onlyLocal timezone used for schedules.
Example value"Europe/Sofia"payment_methodsstring[]Response onlyThis version returns cash only.
Example value["cash"]ordering_enabledboolResponse onlyGlobal/partner submission switch; does not guarantee a zone is open now.
Example valuetruezonesZone[]Response onlyDelivery zones. Numeric database fields may be numbers or numeric strings.
Example valueNested fields: Zone ↗pickupPickup objectResponse onlyRestaurant collection information.
Example valueNested fields: Pickup ↗noticestringResponse onlyReminder to use /quote for authoritative rules.
Example value"Use POST /quote for authoritative current eligibility, fees and minimums."
Delivery zoneszones[]9
Response only · Zone
idinteger | numeric stringResponse onlyID for choice.zone_id; send it as an integer.
Example value1namestringResponse onlyDelivery area name.
Example value"Велинград"enabledinteger | numeric stringResponse only1 means enabled; 0 means disabled.
Example value1days_maskinteger | numeric stringResponse onlyWeekday bitmask: Monday=1, Tuesday=2, … Sunday=64; 127=every day.
Example value127acceptance_opensstringResponse onlyOrdinary opening time, HH:MM, Europe/Sofia.
Example value"09:00"acceptance_cutoffstringResponse onlyOrdinary cutoff; late/holiday rules are enforced by /quote.
Example value"22:00"delivery_fee_minornumeric?Response onlyOrdinary delivery fee or null; use /quote for the exact fee.
Example value0minimum_order_minornumeric?Response onlyOrdinary minimum or null when not configured.
Example value0free_delivery_over_minornumeric?Response onlyFree-delivery threshold or null if none.
Example valuenull
Restaurant collectionpickup6
Response only · Pickup
location_idmainResponse onlyUse main in choice.location_id.
Example value"main"namestringResponse onlyRestaurant name.
Example value"Pizza Fashion"addressstringResponse onlyCollection address.
Example value"Велинград, адрес на обекта"opensstringResponse onlyOrdinary opening time.
Example value"09:00"cutoffstringResponse onlyOrdinary cutoff time.
Example value"22:00"days(integer | numeric string)[]Response onlyISO weekdays: 1=Monday, 7=Sunday; values may be strings.
Example value[1,2,3,4,5,6,7]
Import an order into Pizza FashionPOST /orders6
Request to API · OrderRequest
external_order_idstring 1–80RequiredYour permanent unique ID: 1–80 ASCII letters/digits and _ . : -; starts alphanumeric. Reuse on retries.
Example value"ONEBITE-2026-10001"itemsLine[]Required1–40 lines; identical to the matching signed quote request.
Example valueNested fields: Line ↗choiceReceiptChoice objectRequiredReceipt/payment choice; identical to the matching /quote request.
Example valueNested fields: ReceiptChoice ↗customerCustomer objectRequiredFulfilment contacts for this order; no Club account is created.
Example valueNested fields: Customer ↗expected_total_minorinteger ≥ 1RequiredExact quote.total_minor as a JSON integer; not a price override.
Example value790quote_tokenstringRequiredUnchanged /quote token; first submission must be within 15 minutes.
Example value"<quote_token-from-matching-quote>"
Authoritative quote requestPOST /quote2
Request to API · QuoteRequest
itemsLine[]Required1–40 configured lines; never send item prices.
Example valueNested fields: Line ↗choiceReceiptChoice objectRequiredReceipt method and cash payment.
Example valueNested fields: ReceiptChoice ↗
Order lineitems[]2
Request to API · Line
selectionSelection objectRequiredProduct, half-and-half or combo configuration.
Example valueNested fields: Selection ↗quantityinteger 1–20OptionalQuantity 1–20; defaults to 1.
Example value1
Product configurationitems[].selection11
Request to API · Selection
typeproduct | half | comboOptionalSelection kind; defaults to product. Set explicitly for half and combo.
Example value"product"product_idinteger ≥ 1Conditional · productCatalogue ID; for a normal product or nested half/combo slot.
Example value123sizestandard | medium | familyConditional · productReturned variants[].key. Preserve combo slot size; half-and-half uses family.
Example value"medium"removedinteger[]OptionalUp to 100 distinct removable ingredient IDs; defaults to [].
Example value[17]crustboolean | 0 | 1OptionalCrust selection: 0/1 or false/true. Initialize from crust_included; change only when selectable.
Example value0addonsstring[]OptionalUp to 40 distinct codes allowed for the product/size; not on the outer half-and-half selection.
Example value["garlic_sauce"]basestringOptionalOne product.bases code; omission uses default. For half-and-half put it on the outer selection.
Example value"tomato"halvesSelection[]Conditional · halfExactly two halves with product_id, size=family and removed. Each half follows included crust; extra selection is unsupported here.
Example valueNested fields: Selection ↗combo_codestringConditional · comboExact combos[].code.
Example value"combo-example"combo_revisionstringConditional · comboSend the returned revision to detect a changed definition/price.
Example value"<revision-from-catalogue>"itemsSelection[]Conditional · comboOrdered default_selection.items, one per slot. No nested quantity field.
Example valueNested fields: Selection ↗
Delivery, collection and paymentchoice4
Request to API · ReceiptChoice
fulfilmentdelivery | pickupRequiredSend delivery or pickup explicitly.
Example value"delivery"zone_idinteger ≥ 1Conditional · deliveryReturned /availability.zones ID for delivery.
Example value1location_idmainConditional · pickupUse main for pickup; never change fulfilment to bypass delivery rules.
Example value"main"paymentcashRequiredCash on delivery/collection only; no online charge or payment-processing fee.
Example value"cash"
Customer fulfilment contactscustomer5
Request to API · Customer
namestr100RequiredReal customer name, up to 100 characters, plain text.
Example value"Име на клиента"phonestring ≤ 80RequiredComplete valid phone, preferably E.164. Bulgarian mobile: 10 domestic digits with leading0 or +359 and 9digits; incomplete numbers are rejected.
Example value"0888123456"emailemail string ≤ 190Conditional · emailValid email; required when configured by the restaurant. Send the real address when available.
Example value"customer@example.com"addressstr240Conditional · deliveryFor delivery: street, number and needed details, up to 240 characters. Unused for pickup.
Example value"Улица Примерна 10, вход А"notestr500OptionalNote up to 500 characters; restaurant can disable it. No HTML or line breaks.
Example value"Звънец 3"
Own-order status requestPOST /orderstatus1
Request to API · StatusRequest
external_order_idstring 1–80RequiredThe same ID this partner used when submitting the order.
Example value"ONEBITE-2026-10001"
Order / status responsemeta.data11
Response only · OrderStatus
external_order_idstring 1–80Response onlyYour order ID.
Example value"ONEBITE-2026-10001"order_numberstringResponse onlyReference generated by Pizza Fashion; do not use it as your external ID.
Example value"PF-A2000"statusaccepted | preparing | sent | delivered | cancelled | payment_reviewResponse onlyMachine-readable state; meanings are listed separately.
Example value"accepted"status_labelstringResponse onlyRestaurant label; may remain Bulgarian regardless of documentation language.
Example value"Приета"currencyEURResponse onlyOrder currency.
Example value"EUR"total_minorinteger · EUR centsResponse onlySaved final amount including delivery.
Example value790paymentcashResponse onlyPayment method is cash.
Example value"cash"paidboolResponse onlyRecorded payment flag. False is normal for a newly created cash order.
Example valuefalsecreated_atRFC3339 stringResponse onlyUTC creation timestamp.
Example value"2026-10-18T12:00:00Z"updated_atRFC3339 stringResponse onlyUTC last-update timestamp.
Example value"2026-10-18T12:05:00Z"replayedboolResponse onlyOnly on /orders: true means an identical replay, not another order.
Example valuefalse
Quote responsemeta.data3
Response only · QuoteResponse
quoteQuote objectResponse onlyAuthoritative pricing breakdown; do not POST this whole object as an order.
Example valueNested fields: Quote ↗quote_tokenstringResponse onlyRetain and send with the original items/choice to /orders.
Example value"<signed-token>"expires_atRFC3339 stringResponse onlyFirst-submission deadline, approximately 15 minutes.
Example value"2026-10-18T12:15:00+00:00"
Totals and receipt rulesquote25
Response only · Quote
schemaintResponse onlyPricing payload schema version.
Example value1test_onlyboolResponse onlyTrue prevents operational partner submission.
Example valuefalsecurrencyEURResponse onlyAll amounts are EUR cents.
Example value"EUR"choiceQuoteChoice objectResponse onlyNormalized display choice; keep the original request choice for /orders.
Example valueNested fields: QuoteChoice ↗locationQuoteLocation objectResponse onlyDelivery zone or collection location.
Example valueNested fields: QuoteLocation ↗itemsQuotedLine[]Response onlyConfigured lines and pricing breakdowns.
Example valueNested fields: QuotedLine ↗countintResponse onlySum of quantities, not number of lines.
Example value2subtotal_minorinteger · EUR centsResponse onlyProduct subtotal before order-level discounts and delivery.
Example value1580delivery_minorinteger · EUR centsResponse onlyAuthoritative receipt/delivery fee.
Example value0total_minorinteger · EUR centsResponse onlyFinal display amount; use as expected_total_minor.
Example value1580minimum_minorminor?Response onlyApplicable minimum-order amount.
Example value0minimum_exclusiveboolResponse onlyTrue means the amount must be strictly above the minimum.
Example valuefalseminimum_includes_deliveryboolResponse onlyWhether the minimum includes delivery.
Example valuefalseperiodregular | late | pickupResponse onlyApplied receipt period.
Example value"pickup"schedule_readyboolResponse onlyWhether current rules permit operational submission.
Example valuetruewarningsstring[]Response onlyWarnings intended for display.
Example value[]discount_minorinteger · EUR centsResponse onlyOrder promotion discount; partner API accepts no promo code, normally 0.
Example value0discount_codestringResponse onlyPromotion code; empty for this API.
Example value""online_discount_minorinteger · EUR centsResponse onlyOnline payment discount; 0 for cash.
Example value0online_discount_basis_pointsintResponse onlyOnline-discount basis points; 0 for cash.
Example value0delivery_net_minorinteger · EUR centsResponse onlyDelivery amount excluding included VAT.
Example value0delivery_tax_minorinteger · EUR centsResponse onlyIncluded VAT on delivery.
Example value0net_minorinteger · EUR centsResponse onlyTotal net amount; do not add it again to total.
Example value1317tax_minorinteger · EUR centsResponse onlyTotal included VAT, not an additional charge.
Example value263vat_percentintResponse onlyVAT percentage from restaurant configuration, not a fixed API policy.
Example value20
Normalized quote choicequote.choice4
Response only · QuoteChoice
fulfilmentdelivery | pickupResponse onlyValidated receipt method.
Example value"pickup"zone_idintResponse onlyDelivery zone; 0 for pickup.
Example value0location_idstringResponse onlyMain for pickup; empty string for delivery.
Example value"main"paymentcashResponse onlyValidated cash payment method.
Example value"cash"
Quoted receipt locationquote.location5
Response only · QuoteLocation
idinteger | "main"Response onlyNumeric zone ID or main for pickup.
Example value"main"namestringResponse onlyLocation/zone name.
Example value"Pizza Fashion"addressstringResponse onlyCollection address for pickup; empty for a delivery zone.
Example value"Велинград, адрес на обекта"opensstringResponse onlyOrdinary opening time.
Example value"09:00"cutoffstringResponse onlyOrdinary cutoff.
Example value"22:00"
Priced linequote.items[]10
Response only · QuotedLine
keystringResponse onlyServer line key; not a product_id and not a request field.
Example value"<server-line-key>"namestringResponse onlyConfigured display name.
Example value"Пица Чикън — Средна"quantityintResponse onlyQuantity.
Example value1unit_minorinteger · EUR centsResponse onlyPrice per configured unit.
Example value790subtotal_minorinteger · EUR centsResponse onlyUnit price × quantity before order-level discounts.
Example value790configurationConfiguration objectResponse onlyFull calculated configuration, read-only.
Example valueNested fields: Configuration ↗discount_minorinteger · EUR centsResponse onlyAllocated line discount.
Example value0gross_minorinteger · EUR centsResponse onlyDiscounted line amount including VAT.
Example value790net_minorinteger · EUR centsResponse onlyLine amount excluding included VAT.
Example value658tax_minorinteger · EUR centsResponse onlyIncluded line VAT.
Example value132
Calculated product / comboquote.items[].configuration15
Response only · Configuration
schemaintResponse onlyConfiguration schema version.
Example value1typeproduct | half | comboResponse onlyConfigured line kind.
Example value"product"namestringResponse onlyDisplay name.
Example value"Пица Чикън — Средна"unit_minorinteger · EUR centsResponse onlyFinal configured unit price.
Example value790currencyEURResponse onlyCurrency EUR.
Example value"EUR"weight_gint?Response onlyTotal weight when known.
Example value550partsConfiguredPart[]Response onlyOne product, two halves or combo parts.
Example valueNested fields: ConfiguredPart ↗allergensinteger[1–14][]Response onlyCombined allergen codes.
Example value[1,7]baseBase objectResponse onlyNormalized selected base when applicable.
Example valueNested fields: Base ↗combo_codestringResponse onlyCombo code.
Example value"combo-example"combo_revisionstringResponse onlyApplied combo revision.
Example value"<revision-from-catalogue>"regular_minorinteger · EUR centsResponse onlyEquivalent standalone total.
Example value1800combo_savings_minorinteger · EUR centsResponse onlySavings from the combo price.
Example value270extras_minorinteger · EUR centsResponse onlySelected option surcharges.
Example value0selectionSelection objectResponse onlyNormalized combo selection; retain the original request for token matching.
Example valueNested fields: Selection ↗
Calculated product partconfiguration.parts[]20
Response only · ConfiguredPart
product_idintResponse onlyProduct ID.
Example value123namestringResponse onlyProduct/part name.
Example value"Пица Чикън"sizestandard | medium | familyResponse onlySelected size.
Example value"medium"recipe_revisionintResponse onlyRecipe revision, read-only.
Example value3weight_gint?Response onlyWeight in grams or null.
Example value550portionstringResponse onlyConfigured serving label, when present.
Example value""ingredientsIngredient[]Response onlyRecipe ingredient list.
Example valueNested fields: Ingredient ↗omittedOmittedIngredient[]Response onlyRemoved ingredients.
Example valueNested fields: OmittedIngredient ↗allergensinteger[1–14][]Response onlyAllergen codes; removing an ingredient does not automatically remove them.
Example value[1,7]allergens_reviewedboolResponse onlyWhether allergen data is reviewed.
Example valuetruecomposition_completeboolResponse onlyWhether recorded composition is complete.
Example valuetruecrustboolean | 0 | 1Response onlyEffective crust selection.
Example value0crust_includedboolResponse onlyWhether the recipe includes crust.
Example valuefalsecrust_extra_minorinteger · EUR centsResponse onlyActual applied crust surcharge.
Example value0base_minorinteger · EUR centsResponse onlyBase product/size price.
Example value790unit_minorinteger · EUR centsResponse onlyPart unit price. Do not independently sum half prices for a half-and-half order.
Example value790half_eligibleboolResponse onlyHalf-and-half eligibility.
Example valuetrueaddonsAddon[]Response onlySelected product/combo-part extras; absent on half parts.
Example valueNested fields: Addon ↗addons_extra_minorinteger · EUR centsResponse onlyTotal extra surcharge.
Example value0size_extra_minorinteger · EUR centsResponse onlyInformational size surcharge, already included in the price.
Example value0
Removed ingredientconfiguration.parts[].omitted[]2
Response only · OmittedIngredient
idintResponse onlyRemoved ingredient ID.
Example value17namestringResponse onlyRemoved ingredient name.
Example value"Лук"
API controller errormeta.error2
Response only · ApiError
statusintResponse onlyHTTP error status.
Example value422messagestringResponse onlyExplanation; some business messages are Bulgarian. Branch on status, not exact text.
Example value"Invalid request"
Allergen codesallergens[]14
- Gluten
- Crustaceans
- Eggs
- Fish
- Peanuts
- Soy
- Milk
- Nuts
- Celery
- Mustard
- Sesame
- Sulphur dioxide and sulphites
- Lupin
- Molluscs
Response and error codes
Handle HTTP status before parsing JSON. Do not assume every infrastructure error contains meta.error. Do not endlessly retry invalid-data or authentication errors.
200Success, including an identical /orders replay.400Unsupported query parameters / gateway request.401Missing, invalid, paused or rotated credentials.403HTTPS required; HTTP API requests are not redirected with credentials.404No own order for this ID, or unknown route.409Changed/expired quote, conflicting external_order_id or busy request. Reconcile status.413 / 415Body exceeds 128 KiB / Content-Type: application/json required.422Invalid fields/options/contact/minimum, closed period or unavailable product. Correct the request.429Rate limit reached. Respect Retry-After.503Disabled submission/API/partner or temporary unavailability. Bounded retries with the same ID.
{
"meta": {
"error": {
"status": 409,
"message": "Total changed. Request a fresh quote."
}
}
}Access, limits and compatibility
Credentials are for your server only. Keep them in a secret store or environment variables. Never place them in URLs, frontend JavaScript, mobile apps, logs or a public API tester. Redact Authorization and customer data.
Default partner limit is 120 requests/minute; the restaurant controls individual/global caps. The lower cap applies, plus an independent 120/minute source-IP cap. Avoid aggressive concurrent full synchronizations.
New partners start paused, without issued credentials or order submission. Pizza Fashion generates separate credentials for each partner. Pausing blocks subsequent requests; rotation invalidates old credentials while preserving identity and replay protection.
API remains v1. The original https://pizzafashion.bg/api/index.php/v1/pizzafashion/partner base remains compatible. Ignore unknown additive response fields; do not send undocumented request fields.
Arrange a controlled integration rehearsal with Pizza Fashion before going live. These examples contain no working credentials and never send orders automatically.