PF↗Pizza FashionPARTNER API
v1Restaurant website ↗

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.

REST / JSONHTTPS BasicEUR cents
API base URLhttps://api.pizzafashion.bg/v1

Language selection affects documentation only. JSON keys are unchanged; dish names and some restaurant messages remain Bulgarian.

Examples are illustrative: replace IDs, codes, revisions and amounts with actual catalogue and /quote values. This page sends no orders.

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.

  1. Obtain a separate API key/password from Pizza Fashion. The key is the Basic username; the password is the Basic password.
  2. Download every /catalogue page and read /availability.
  3. Build items + choice and call /quote. Show the final price to the customer.
  4. After confirmation, submit /orders with the same items/choice, quote_token and a permanent external_order_id.
  5. 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.

Authentication · keep values server-side
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'
Successful response envelope (abbreviated)
{
  "meta": {
    "data": {
      "currency": "EUR",
      "page": 1,
      "has_more": true
    }
  }
}
GET/catalogue?page=1

Products: 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.

Download the next page
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'
GET/availability

Zones, 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.

Zones and receipt options
curl --fail-with-body --user "$PF_API_KEY:$PF_API_PASSWORD" \
  --header 'Accept: application/vnd.api+json' \
  'https://api.pizzafashion.bg/v1/availability'
POST/quote

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

quote-request.json → /quote
{
  "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'
Pizza selection with options (illustrative IDs/codes)
{
  "type": "product",
  "product_id": 123,
  "size": "family",
  "removed": [
    17
  ],
  "crust": 1,
  "addons": [
    "garlic_sauce"
  ],
  "base": "tomato"
}
Combo selection (copy the catalogue default_selection)
{
  "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": []
    }
  ]
}
Half-and-half selection
{
  "type": "half",
  "halves": [
    {
      "product_id": 123,
      "size": "family",
      "removed": []
    },
    {
      "product_id": 125,
      "size": "family",
      "removed": [
        17
      ]
    }
  ],
  "base": "tomato"
}
Quote response (abbreviated)
{
  "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"
    }
  }
}
POST/orders

Orders: 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.

order-request.json → /orders
{
  "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'
Successfully recorded order
{
  "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
    }
  }
}
Node.js · backend functions, no automatic submission
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.
POST/orderstatus

Status 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.
status-request.json → /orderstatus
{
  "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.

Request headersHTTP headers3

Request to API · RequestHeaders

  • AuthorizationstringRequired

    HTTP Basic: API key is username, separate password is password. Send only to the HTTPS API host.

    Example value"Basic <base64(API_KEY:API_PASSWORD)>"
  • AcceptstringRequired

    Explicitly request the JSON API response format.

    Example value"application/vnd.api+json"
  • Content-TypestringConditional · post

    For 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–10000Optional

    Integer1–10000, default1. Example: /catalogue?page=2. Other query parameters are unsupported.

    Example value1
Catalogue responsemeta.data13

Response only · Catalogue

  • schemaintResponse only

    Payload schema version; currently 1.

    Example value1
  • currencyEURResponse only

    Currency of all prices.

    Example value"EUR"
  • timezonestringResponse only

    Timezone used for restaurant opening hours.

    Example value"Europe/Sofia"
  • categoriesCategory[]Response only

    Categories; resolve them using each product’s category_ids.

  • productsProduct[]Response only

    Up to 50 products per page, including option children.

  • pageintResponse only

    Current page number, starting at 1.

    Example value1
  • page_sizeintResponse only

    Page size: 50.

    Example value50
  • totalintResponse only

    Total product records, including unlisted option children.

    Example value144
  • has_moreboolResponse only

    When true, request the next page.

    Example valuetrue
  • combosCombo[]Response only

    Combo offers; merge repeated page metadata by code.

  • pizza_basesBase[]Response only

    Global pizza bases; use products[].bases for product eligibility.

  • halfHalfSettings objectResponse only

    Half-and-half configuration.

  • generated_atRFC3339 stringResponse only

    Generation timestamp, RFC3339.

    Example value"2026-10-18T12:00:00+00:00"
Categoriescategories[]4

Response only · Category

  • idintResponse only

    Stable category identifier.

    Example value7
  • namestringResponse only

    Public category name.

    Example value"Пици"
  • iconstringResponse only

    Icon code; presentation metadata.

    Example value"pizza"
  • countintResponse only

    Menu product count for the category.

    Example value24
Product attributesproducts[]18

Response only · Product

  • idintResponse only

    Stable ID; send it as selection.product_id.

    Example value123
  • namestringResponse only

    Display name; changing documentation language does not translate dish names.

    Example value"Пица Чикън"
  • descriptionstringResponse only

    Plain text description without HTML.

    Example value"Пилешко месо, моцарела"
  • category_idsinteger[]Response only

    Category IDs containing this product.

    Example value[7]
  • imageurl?Response only

    Absolute HTTPS product image URL, or null.

    Example value"https://pizzafashion.bg/images/example.webp"
  • thumbnailurl?Response only

    Smaller catalogue image URL, or null.

    Example value"https://pizzafashion.bg/images/example-thumb.webp"
  • image_illustrativeboolResponse only

    Whether the image is illustrative.

    Example valuefalse
  • listedboolResponse only

    False: flavour/package option child; retain it but do not display a separate card.

    Example valuetrue
  • minorminor?Response only

    Base displayed price in EUR cents. Use the selected variant price.

    Example value790
  • currencyEURResponse only

    Currency EUR.

    Example value"EUR"
  • availableboolResponse only

    When false, disable ordering this product.

    Example valuetrue
  • available_atstr?Response only

    Scheduled restock label/timestamp or null; does not override current available.

    Example valuenull
  • availability_noticestringResponse only

    Availability notice, when configured.

    Example value""
  • variantsVariant[]Response only

    Available sizes and their prices, ingredients and options.

  • addonsAddon[]Response only

    Allowed extras; selected-size prices are in variants[].addons.

  • basesBase[]Response only

    Allowed bases for this pizza; empty when inapplicable.

  • choicesChoiceGroup[]Response only

    Flavour/package choices referencing a different product_id.

  • half_eligibleboolResponse only

    Eligible for half-and-half when enabled and a family variant exists.

    Example valuetrue
Sizes / variantsproducts[].variants[]10

Response only · Variant

  • keystandard | medium | familyResponse only

    Key for selection.size; use only a returned variant.

    Example value"medium"
  • labelstringResponse only

    Human-readable size label.

    Example value"Средна"
  • minorminor?Response only

    Full price for this size, not a size surcharge.

    Example value790
  • weight_gint?Response only

    Weight in grams, or null when unspecified.

    Example value550
  • ingredientsIngredient[]Response only

    Ingredients; only entries with removable=1 may be removed.

  • allergensinteger[1–14][]Response only

    Numeric codes 1–14; ingredient removal never guarantees allergen-free food.

    Example value[1,7]
  • crust_selectableboolResponse only

    Whether crust selection is permitted.

    Example valuetrue
  • crust_includedboolResponse only

    Recipe’s included crust; use as the initial crust selection.

    Example valuefalse
  • crust_extra_minorminor?Response only

    Surcharge for adding crust; null does not mean free.

    Example value250
  • addonsAddon[]Response only

    Extras priced for this exact size.

Ingredientsvariants[].ingredients[]3

Response only · Ingredient

  • idintResponse only

    ID for selection.removed[] when removal is allowed.

    Example value17
  • namestringResponse only

    Ingredient name.

    Example value"Лук"
  • removableboolean | 0 | 1Response only

    1/true permits removal; 0/false prohibits it.

    Example value1
Sauces and extrasproducts[].addons[] / variants[].addons[]11

Response only · Addon

  • codestringResponse only

    Send this code in selection.addons[], without duplicates.

    Example value"garlic_sauce"
  • namestringResponse only

    Extra name for the selected size.

    Example value"Чеснов сос"
  • groupsauce | toppingResponse only

    Extra group: sauce or pizza topping.

    Example value"sauce"
  • enabledboolean | 0 | 1Response only

    Definition enabled flag; catalogue includes permitted extras.

    Example value1
  • minorinteger · EUR centsResponse only

    Price in EUR cents; variant-level value is size-specific.

    Example value100
  • allergensinteger[1–14][]Response only

    Allergen codes of this extra.

    Example value[7]
  • reviewedboolean | 0 | 1Response only

    Whether allergen data has been reviewed.

    Example value1
  • family_minorinteger · EUR centsResponse only

    Optional family-size price metadata.

    Example value200
  • weight_gintResponse only

    Extra weight in grams when known.

    Example value50
  • family_weight_gintResponse only

    Optional family-size weight.

    Example value100
  • family_namestringResponse only

    Optional family-size display label.

    Example value"Добавка — 100 г"
Pizza basesproducts[].bases[] / pizza_bases[]7

Response only · Base

  • codestringResponse only

    Code for selection.base; this is not a paid extra.

    Example value"tomato"
  • namestringResponse only

    Display label.

    Example value"Доматено пюре"
  • enabledboolean | 0 | 1Response only

    Definition flag; may be absent in normalized quote bases.

    Example value1
  • defaultboolean | 0 | 1Response only

    Default base, used when base is omitted.

    Example value1
  • minorinteger · EUR centsResponse only

    Bases are included; value is 0.

    Example value0
  • allergensinteger[1–14][]Response only

    Base allergen codes.

    Example value[]
  • reviewedboolean | 0 | 1Response only

    Whether allergen data is reviewed.

    Example value1
Flavour / package choicesproducts[].choices[]3

Response only · ChoiceGroup

  • labelstringResponse only

    Choice group heading.

    Example value"Избери вкус"
  • show_priceboolResponse only

    Whether to display the price beside the option.

    Example valuetrue
  • optionsChoiceOption[]Response only

    Alternatives, each using a separate product_id.

Choice optionchoices[].options[]4

Response only · ChoiceOption

  • product_idintResponse only

    After choosing, resolve this product and its own variants/extras.

    Example value124
  • labelstringResponse only

    Option label.

    Example value"Без захар"
  • minorinteger · EUR centsResponse only

    Standalone price of the target product.

    Example value200
  • currentboolResponse only

    Whether this option represents the current product.

    Example valuefalse
Combo offerscombos[]10

Response only · Combo

  • codestringResponse only

    Stable code for selection.combo_code.

    Example value"combo-example"
  • revisionstringResponse only

    Copy exactly into combo_revision; never invent a revision.

    Example value"<revision-from-catalogue>"
  • namestringResponse only

    Public name.

    Example value"Комбо предложение"
  • descriptionstringResponse only

    Short description.

    Example value"Пица и напитка"
  • imageurl?Response only

    HTTPS image URL or null.

    Example valuenull
  • minorinteger · EUR centsResponse only

    Default configuration price; options may add a surcharge.

    Example value1490
  • availableboolResponse only

    Current availability; server rechecks it on submission.

    Example valuetrue
  • schedulestringResponse only

    Human-readable schedule, not authoritative ordering permission.

    Example value"Всеки ден · през работното време"
  • slotsComboSlot[]Response only

    Ordered slots; preserve their count and order.

  • default_selectionSelection objectResponse only

    Ready-to-use request selection; change only permitted options.

Combo slotscombos[].slots[]2

Response only · ComboSlot

  • product_idsinteger[]Response only

    Allowed products for this slot; do not substitute other IDs.

    Example value[123,124]
  • sizestandard | medium | familyResponse only

    Fixed slot size; cannot be changed.

    Example value"medium"
Half-and-half settingshalf4

Response only · HalfSettings

  • enabledboolResponse only

    When false, do not offer the half-and-half builder.

    Example valuetrue
  • sizefamilyResponse only

    Half-and-half is available only in family size.

    Example value"family"
  • price_modehigher | average | fixed | unsetResponse only

    Pricing mode; always obtain the final amount from /quote.

    Example value"higher"
  • fixed_minorminor?Response only

    Fixed price for fixed mode; null does not mean zero.

    Example valuenull
Receipt and opening hoursmeta.data7

Response only · Availability

  • currencyEURResponse only

    Currency of monetary values.

    Example value"EUR"
  • timezonestringResponse only

    Local timezone used for schedules.

    Example value"Europe/Sofia"
  • payment_methodsstring[]Response only

    This version returns cash only.

    Example value["cash"]
  • ordering_enabledboolResponse only

    Global/partner submission switch; does not guarantee a zone is open now.

    Example valuetrue
  • zonesZone[]Response only

    Delivery zones. Numeric database fields may be numbers or numeric strings.

  • pickupPickup objectResponse only

    Restaurant collection information.

  • noticestringResponse only

    Reminder 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 only

    ID for choice.zone_id; send it as an integer.

    Example value1
  • namestringResponse only

    Delivery area name.

    Example value"Велинград"
  • enabledinteger | numeric stringResponse only

    1 means enabled; 0 means disabled.

    Example value1
  • days_maskinteger | numeric stringResponse only

    Weekday bitmask: Monday=1, Tuesday=2, … Sunday=64; 127=every day.

    Example value127
  • acceptance_opensstringResponse only

    Ordinary opening time, HH:MM, Europe/Sofia.

    Example value"09:00"
  • acceptance_cutoffstringResponse only

    Ordinary cutoff; late/holiday rules are enforced by /quote.

    Example value"22:00"
  • delivery_fee_minornumeric?Response only

    Ordinary delivery fee or null; use /quote for the exact fee.

    Example value0
  • minimum_order_minornumeric?Response only

    Ordinary minimum or null when not configured.

    Example value0
  • free_delivery_over_minornumeric?Response only

    Free-delivery threshold or null if none.

    Example valuenull
Restaurant collectionpickup6

Response only · Pickup

  • location_idmainResponse only

    Use main in choice.location_id.

    Example value"main"
  • namestringResponse only

    Restaurant name.

    Example value"Pizza Fashion"
  • addressstringResponse only

    Collection address.

    Example value"Велинград, адрес на обекта"
  • opensstringResponse only

    Ordinary opening time.

    Example value"09:00"
  • cutoffstringResponse only

    Ordinary cutoff time.

    Example value"22:00"
  • days(integer | numeric string)[]Response only

    ISO 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–80Required

    Your permanent unique ID: 1–80 ASCII letters/digits and _ . : -; starts alphanumeric. Reuse on retries.

    Example value"ONEBITE-2026-10001"
  • itemsLine[]Required

    1–40 lines; identical to the matching signed quote request.

  • choiceReceiptChoice objectRequired

    Receipt/payment choice; identical to the matching /quote request.

  • customerCustomer objectRequired

    Fulfilment contacts for this order; no Club account is created.

  • expected_total_minorinteger ≥ 1Required

    Exact quote.total_minor as a JSON integer; not a price override.

    Example value790
  • quote_tokenstringRequired

    Unchanged /quote token; first submission must be within 15 minutes.

    Example value"<quote_token-from-matching-quote>"
Authoritative quote requestPOST /quote2

Request to API · QuoteRequest

Order lineitems[]2

Request to API · Line

  • selectionSelection objectRequired

    Product, half-and-half or combo configuration.

  • quantityinteger 1–20Optional

    Quantity 1–20; defaults to 1.

    Example value1
Product configurationitems[].selection11

Request to API · Selection

  • typeproduct | half | comboOptional

    Selection kind; defaults to product. Set explicitly for half and combo.

    Example value"product"
  • product_idinteger ≥ 1Conditional · product

    Catalogue ID; for a normal product or nested half/combo slot.

    Example value123
  • sizestandard | medium | familyConditional · product

    Returned variants[].key. Preserve combo slot size; half-and-half uses family.

    Example value"medium"
  • removedinteger[]Optional

    Up to 100 distinct removable ingredient IDs; defaults to [].

    Example value[17]
  • crustboolean | 0 | 1Optional

    Crust selection: 0/1 or false/true. Initialize from crust_included; change only when selectable.

    Example value0
  • addonsstring[]Optional

    Up to 40 distinct codes allowed for the product/size; not on the outer half-and-half selection.

    Example value["garlic_sauce"]
  • basestringOptional

    One product.bases code; omission uses default. For half-and-half put it on the outer selection.

    Example value"tomato"
  • halvesSelection[]Conditional · half

    Exactly two halves with product_id, size=family and removed. Each half follows included crust; extra selection is unsupported here.

  • combo_codestringConditional · combo

    Exact combos[].code.

    Example value"combo-example"
  • combo_revisionstringConditional · combo

    Send the returned revision to detect a changed definition/price.

    Example value"<revision-from-catalogue>"
  • itemsSelection[]Conditional · combo

    Ordered default_selection.items, one per slot. No nested quantity field.

Delivery, collection and paymentchoice4

Request to API · ReceiptChoice

  • fulfilmentdelivery | pickupRequired

    Send delivery or pickup explicitly.

    Example value"delivery"
  • zone_idinteger ≥ 1Conditional · delivery

    Returned /availability.zones ID for delivery.

    Example value1
  • location_idmainConditional · pickup

    Use main for pickup; never change fulfilment to bypass delivery rules.

    Example value"main"
  • paymentcashRequired

    Cash on delivery/collection only; no online charge or payment-processing fee.

    Example value"cash"
Customer fulfilment contactscustomer5

Request to API · Customer

  • namestr100Required

    Real customer name, up to 100 characters, plain text.

    Example value"Име на клиента"
  • phonestring ≤ 80Required

    Complete 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 · email

    Valid email; required when configured by the restaurant. Send the real address when available.

    Example value"customer@example.com"
  • addressstr240Conditional · delivery

    For delivery: street, number and needed details, up to 240 characters. Unused for pickup.

    Example value"Улица Примерна 10, вход А"
  • notestr500Optional

    Note 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–80Required

    The 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 only

    Your order ID.

    Example value"ONEBITE-2026-10001"
  • order_numberstringResponse only

    Reference generated by Pizza Fashion; do not use it as your external ID.

    Example value"PF-A2000"
  • statusaccepted | preparing | sent | delivered | cancelled | payment_reviewResponse only

    Machine-readable state; meanings are listed separately.

    Example value"accepted"
  • status_labelstringResponse only

    Restaurant label; may remain Bulgarian regardless of documentation language.

    Example value"Приета"
  • currencyEURResponse only

    Order currency.

    Example value"EUR"
  • total_minorinteger · EUR centsResponse only

    Saved final amount including delivery.

    Example value790
  • paymentcashResponse only

    Payment method is cash.

    Example value"cash"
  • paidboolResponse only

    Recorded payment flag. False is normal for a newly created cash order.

    Example valuefalse
  • created_atRFC3339 stringResponse only

    UTC creation timestamp.

    Example value"2026-10-18T12:00:00Z"
  • updated_atRFC3339 stringResponse only

    UTC last-update timestamp.

    Example value"2026-10-18T12:05:00Z"
  • replayedboolResponse only

    Only on /orders: true means an identical replay, not another order.

    Example valuefalse
Quote responsemeta.data3

Response only · QuoteResponse

  • quoteQuote objectResponse only

    Authoritative pricing breakdown; do not POST this whole object as an order.

  • quote_tokenstringResponse only

    Retain and send with the original items/choice to /orders.

    Example value"<signed-token>"
  • expires_atRFC3339 stringResponse only

    First-submission deadline, approximately 15 minutes.

    Example value"2026-10-18T12:15:00+00:00"
Totals and receipt rulesquote25

Response only · Quote

  • schemaintResponse only

    Pricing payload schema version.

    Example value1
  • test_onlyboolResponse only

    True prevents operational partner submission.

    Example valuefalse
  • currencyEURResponse only

    All amounts are EUR cents.

    Example value"EUR"
  • choiceQuoteChoice objectResponse only

    Normalized display choice; keep the original request choice for /orders.

  • locationQuoteLocation objectResponse only

    Delivery zone or collection location.

  • itemsQuotedLine[]Response only

    Configured lines and pricing breakdowns.

  • countintResponse only

    Sum of quantities, not number of lines.

    Example value2
  • subtotal_minorinteger · EUR centsResponse only

    Product subtotal before order-level discounts and delivery.

    Example value1580
  • delivery_minorinteger · EUR centsResponse only

    Authoritative receipt/delivery fee.

    Example value0
  • total_minorinteger · EUR centsResponse only

    Final display amount; use as expected_total_minor.

    Example value1580
  • minimum_minorminor?Response only

    Applicable minimum-order amount.

    Example value0
  • minimum_exclusiveboolResponse only

    True means the amount must be strictly above the minimum.

    Example valuefalse
  • minimum_includes_deliveryboolResponse only

    Whether the minimum includes delivery.

    Example valuefalse
  • periodregular | late | pickupResponse only

    Applied receipt period.

    Example value"pickup"
  • schedule_readyboolResponse only

    Whether current rules permit operational submission.

    Example valuetrue
  • warningsstring[]Response only

    Warnings intended for display.

    Example value[]
  • discount_minorinteger · EUR centsResponse only

    Order promotion discount; partner API accepts no promo code, normally 0.

    Example value0
  • discount_codestringResponse only

    Promotion code; empty for this API.

    Example value""
  • online_discount_minorinteger · EUR centsResponse only

    Online payment discount; 0 for cash.

    Example value0
  • online_discount_basis_pointsintResponse only

    Online-discount basis points; 0 for cash.

    Example value0
  • delivery_net_minorinteger · EUR centsResponse only

    Delivery amount excluding included VAT.

    Example value0
  • delivery_tax_minorinteger · EUR centsResponse only

    Included VAT on delivery.

    Example value0
  • net_minorinteger · EUR centsResponse only

    Total net amount; do not add it again to total.

    Example value1317
  • tax_minorinteger · EUR centsResponse only

    Total included VAT, not an additional charge.

    Example value263
  • vat_percentintResponse only

    VAT percentage from restaurant configuration, not a fixed API policy.

    Example value20
Normalized quote choicequote.choice4

Response only · QuoteChoice

  • fulfilmentdelivery | pickupResponse only

    Validated receipt method.

    Example value"pickup"
  • zone_idintResponse only

    Delivery zone; 0 for pickup.

    Example value0
  • location_idstringResponse only

    Main for pickup; empty string for delivery.

    Example value"main"
  • paymentcashResponse only

    Validated cash payment method.

    Example value"cash"
Quoted receipt locationquote.location5

Response only · QuoteLocation

  • idinteger | "main"Response only

    Numeric zone ID or main for pickup.

    Example value"main"
  • namestringResponse only

    Location/zone name.

    Example value"Pizza Fashion"
  • addressstringResponse only

    Collection address for pickup; empty for a delivery zone.

    Example value"Велинград, адрес на обекта"
  • opensstringResponse only

    Ordinary opening time.

    Example value"09:00"
  • cutoffstringResponse only

    Ordinary cutoff.

    Example value"22:00"
Priced linequote.items[]10

Response only · QuotedLine

  • keystringResponse only

    Server line key; not a product_id and not a request field.

    Example value"<server-line-key>"
  • namestringResponse only

    Configured display name.

    Example value"Пица Чикън — Средна"
  • quantityintResponse only

    Quantity.

    Example value1
  • unit_minorinteger · EUR centsResponse only

    Price per configured unit.

    Example value790
  • subtotal_minorinteger · EUR centsResponse only

    Unit price × quantity before order-level discounts.

    Example value790
  • configurationConfiguration objectResponse only

    Full calculated configuration, read-only.

  • discount_minorinteger · EUR centsResponse only

    Allocated line discount.

    Example value0
  • gross_minorinteger · EUR centsResponse only

    Discounted line amount including VAT.

    Example value790
  • net_minorinteger · EUR centsResponse only

    Line amount excluding included VAT.

    Example value658
  • tax_minorinteger · EUR centsResponse only

    Included line VAT.

    Example value132
Calculated product / comboquote.items[].configuration15

Response only · Configuration

  • schemaintResponse only

    Configuration schema version.

    Example value1
  • typeproduct | half | comboResponse only

    Configured line kind.

    Example value"product"
  • namestringResponse only

    Display name.

    Example value"Пица Чикън — Средна"
  • unit_minorinteger · EUR centsResponse only

    Final configured unit price.

    Example value790
  • currencyEURResponse only

    Currency EUR.

    Example value"EUR"
  • weight_gint?Response only

    Total weight when known.

    Example value550
  • partsConfiguredPart[]Response only

    One product, two halves or combo parts.

  • allergensinteger[1–14][]Response only

    Combined allergen codes.

    Example value[1,7]
  • baseBase objectResponse only

    Normalized selected base when applicable.

  • combo_codestringResponse only

    Combo code.

    Example value"combo-example"
  • combo_revisionstringResponse only

    Applied combo revision.

    Example value"<revision-from-catalogue>"
  • regular_minorinteger · EUR centsResponse only

    Equivalent standalone total.

    Example value1800
  • combo_savings_minorinteger · EUR centsResponse only

    Savings from the combo price.

    Example value270
  • extras_minorinteger · EUR centsResponse only

    Selected option surcharges.

    Example value0
  • selectionSelection objectResponse only

    Normalized combo selection; retain the original request for token matching.

Calculated product partconfiguration.parts[]20

Response only · ConfiguredPart

  • product_idintResponse only

    Product ID.

    Example value123
  • namestringResponse only

    Product/part name.

    Example value"Пица Чикън"
  • sizestandard | medium | familyResponse only

    Selected size.

    Example value"medium"
  • recipe_revisionintResponse only

    Recipe revision, read-only.

    Example value3
  • weight_gint?Response only

    Weight in grams or null.

    Example value550
  • portionstringResponse only

    Configured serving label, when present.

    Example value""
  • ingredientsIngredient[]Response only

    Recipe ingredient list.

  • omittedOmittedIngredient[]Response only

    Removed ingredients.

  • allergensinteger[1–14][]Response only

    Allergen codes; removing an ingredient does not automatically remove them.

    Example value[1,7]
  • allergens_reviewedboolResponse only

    Whether allergen data is reviewed.

    Example valuetrue
  • composition_completeboolResponse only

    Whether recorded composition is complete.

    Example valuetrue
  • crustboolean | 0 | 1Response only

    Effective crust selection.

    Example value0
  • crust_includedboolResponse only

    Whether the recipe includes crust.

    Example valuefalse
  • crust_extra_minorinteger · EUR centsResponse only

    Actual applied crust surcharge.

    Example value0
  • base_minorinteger · EUR centsResponse only

    Base product/size price.

    Example value790
  • unit_minorinteger · EUR centsResponse only

    Part unit price. Do not independently sum half prices for a half-and-half order.

    Example value790
  • half_eligibleboolResponse only

    Half-and-half eligibility.

    Example valuetrue
  • addonsAddon[]Response only

    Selected product/combo-part extras; absent on half parts.

  • addons_extra_minorinteger · EUR centsResponse only

    Total extra surcharge.

    Example value0
  • size_extra_minorinteger · EUR centsResponse only

    Informational size surcharge, already included in the price.

    Example value0
Removed ingredientconfiguration.parts[].omitted[]2

Response only · OmittedIngredient

  • idintResponse only

    Removed ingredient ID.

    Example value17
  • namestringResponse only

    Removed ingredient name.

    Example value"Лук"
API controller errormeta.error2

Response only · ApiError

  • statusintResponse only

    HTTP error status.

    Example value422
  • messagestringResponse only

    Explanation; some business messages are Bulgarian. Branch on status, not exact text.

    Example value"Invalid request"
Allergen codesallergens[]14
  1. Gluten
  2. Crustaceans
  3. Eggs
  4. Fish
  5. Peanuts
  6. Soy
  7. Milk
  8. Nuts
  9. Celery
  10. Mustard
  11. Sesame
  12. Sulphur dioxide and sulphites
  13. Lupin
  14. 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.
Controller error envelope
{
  "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.