> ## Documentation Index
> Fetch the complete documentation index at: https://developers.xfaster.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Import the menu

> Send the menu built in your POS: sections, add-ons and items with option groups. Every line is matched by your `external_id`: new ones are created, changed ones updated, the rest counted as unchanged. One invalid line is reported in `errors` and does not stop the others. Deals are never changed. Up to 1000 lines per list per call; bigger menus go in parts with `mode=partial`.

Build the menu in your POS and send it to XFaster. You don't map items by hand: every section, add-on and item is matched by **your own `external_id`**.

## How lines are matched

| Line | Matched by | If not found |
| - | - | - |
| Section (`categories`) | `external_id`, else a section of the store with exactly the same name | Created |
| Add-on (`addons`) | `external_id` | Created (needs `price`) |
| Item (`items`) | `external_id`, else (markets) the item with the same `barcode` | Created (needs `name` and `price`) |

* A line that is already up to date counts as `unchanged` and changes nothing.
* A new item follows `available` (on when you leave it out).
* A plain text `name` is the Hebrew name. Send `{"he", "ar", "en"}` for more languages. Empty texts never erase a translation.
* `option_groups` and `addon_external_ids` **replace** the item's groups and add-ons. Option groups are for restaurants only.
* **Deals are never changed** from the POS. They follow the items inside them.

## Full or partial

* `mode: "partial"` (default) changes only the lines you send.
* `mode: "full"` also **switches off** your mapped items, add-ons and sections that are missing from the call. Nothing is deleted, and XFaster items you never mapped are left alone. A full menu with no items is refused.
* Up to **1000 lines per list** in one call. A bigger menu goes in parts with `mode: "partial"`.

<Tip>
  Send `"dry_run": true` first. You get the same counts and errors, and nothing changes.
</Tip>

## Errors

The answer always lists the lines that were skipped in `errors`, with a `code` and a `message`. The other lines were saved. Fix the line and send it again: re-sending the same menu is safe.

<Warning>
  If the store already has a menu on XFaster, link it first ([Get the menu](/restaurant/menu/get) and [Map your codes](/restaurant/menu/mapping)). Otherwise the import creates new items next to the old ones.
</Warning>


## OpenAPI

````yaml PUT /menu
openapi: 3.1.0
info:
  title: XFaster Partner API
  version: '1.0'
  description: >
    Receive XFaster orders in your POS, send their status back, and keep the
    menu, stock and prices in sync.

    One API key per store, or one integrator key for all your stores (it names
    the store in X-XFaster-Store). Money is in agorot (integers, VAT included).
    Times are ISO 8601 with the Israel offset.
servers:
  - url: https://staging2.xfaster.net/api/partner/v1
    description: Sandbox (test keys, xfp_test_…)
  - url: https://apps.xfaster.net/api/partner/v1
    description: Live (live keys, xfp_live_…)
security:
  - apiKey: []
  - apiKey: []
    storeHeader: []
tags:
  - name: Store
  - name: Orders
  - name: Menu
  - name: Market
  - name: Reports
  - name: Sandbox
paths:
  /menu:
    put:
      tags:
        - Menu
      summary: Import the menu
      description: >-
        Send the menu built in your POS: sections, add-ons and items with option
        groups. Every line is matched by your `external_id`: new ones are
        created, changed ones updated, the rest counted as unchanged. One
        invalid line is reported in `errors` and does not stop the others. Deals
        are never changed. Up to 1000 lines per list per call; bigger menus go
        in parts with `mode=partial`.
      operationId: importMenu
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MenuImport'
            example:
              mode: full
              categories:
                - external_id: CAT-PIZZA
                  name:
                    he: פיצות
                    ar: بيتزا
                    en: Pizzas
                  position: 1
                - external_id: CAT-DRINKS
                  name: שתייה
                  position: 2
              addons:
                - external_id: ADD-OLIVES
                  name:
                    he: זיתים
                    en: Olives
                  price: 400
                  available: true
              items:
                - external_id: PIZZA-MARG
                  category_external_id: CAT-PIZZA
                  name:
                    he: מרגריטה
                    ar: مارغريتا
                    en: Margherita
                  description:
                    he: רוטב עגבניות ומוצרלה
                  price: 5200
                  available: true
                  option_groups:
                    - name: גודל
                      type: single
                      required: true
                      min: 1
                      max: 1
                      options:
                        - name: אישית
                          price: 0
                        - name: משפחתית
                          price: 1500
                  addon_external_ids:
                    - ADD-OLIVES
                - external_id: COLA-330
                  category_external_id: CAT-DRINKS
                  name: קוקה קולה 330 מ"ל
                  price: 900
                  available: true
      responses:
        '200':
          description: What was done (or, with dry_run, what would be done)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MenuImportResult'
              example:
                dry_run: false
                created:
                  categories: 2
                  items: 1
                  addons: 1
                updated:
                  categories: 0
                  items: 1
                  addons: 0
                switched_off:
                  categories: 0
                  items: 3
                  addons: 0
                unchanged: 0
                errors:
                  - kind: item
                    external_id: WINGS-6
                    code: category_not_found
                    message: >-
                      No section with this category_external_id (send it in
                      categories).
        '422':
          description: The body itself was refused; nothing was changed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                too_large:
                  value:
                    error:
                      code: menu_too_large
                      message: >-
                        At most 1000 items in one call: send the menu in parts
                        with mode=partial.
                      details:
                        max: 1000
                invalid:
                  value:
                    error:
                      code: invalid_menu
                      message: A full menu needs its items.
                      details: null
components:
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      description: >-
        Any unique string. Repeating a request with the same key within 24 hours
        returns the first answer instead of acting twice.
      schema:
        type: string
        example: 5f0c3d1e-8b6a-4e1f-9c2d-7a1b0e4f6d21
  schemas:
    MenuImport:
      type: object
      properties:
        mode:
          type: string
          enum:
            - partial
            - full
          default: partial
          description: >-
            partial: only the lines you send. full: also switches OFF your
            mapped items, add-ons and sections that are missing (never deletes;
            XFaster items you never mapped are left alone).
        dry_run:
          type: boolean
          default: false
          description: Check everything and return the counts, change nothing.
        categories:
          type: array
          maxItems: 1000
          items:
            type: object
            required:
              - external_id
              - name
            properties:
              external_id:
                type: string
                minLength: 1
                maxLength: 100
                description: Your own code. Lines are matched by it.
              name:
                oneOf:
                  - type: string
                    description: The Hebrew name
                  - type: object
                    properties:
                      he:
                        type: string
                      ar:
                        type: string
                      en:
                        type: string
              position:
                type: integer
                description: 1 = first section
        addons:
          type: array
          maxItems: 1000
          items:
            type: object
            required:
              - external_id
            properties:
              external_id:
                type: string
                minLength: 1
                maxLength: 100
                description: Your own code. Lines are matched by it.
              name:
                oneOf:
                  - type: string
                    description: The Hebrew name
                  - type: object
                    properties:
                      he:
                        type: string
                      ar:
                        type: string
                      en:
                        type: string
              price:
                type: integer
                minimum: 0
                maximum: 10000000
                description: Agorot. Required for a new add-on.
              available:
                type: boolean
        items:
          type: array
          maxItems: 1000
          items:
            type: object
            required:
              - external_id
            properties:
              external_id:
                type: string
                minLength: 1
                maxLength: 100
                description: Your own code. Lines are matched by it.
              category_external_id:
                type:
                  - string
                  - 'null'
                description: A section from categories (this call or an earlier one)
              name:
                oneOf:
                  - type: string
                    description: The Hebrew name
                  - type: object
                    properties:
                      he:
                        type: string
                      ar:
                        type: string
                      en:
                        type: string
              description:
                oneOf:
                  - type: string
                    description: The Hebrew name
                  - type: object
                    properties:
                      he:
                        type: string
                      ar:
                        type: string
                      en:
                        type: string
              price:
                type: integer
                minimum: 0
                maximum: 10000000
                description: Agorot, VAT included. Required (at least 1) for a new item.
              available:
                type: boolean
                description: A new item follows it (default true).
              barcode:
                type:
                  - string
                  - 'null'
                maxLength: 64
                description: >-
                  Markets: an item you never mapped is matched by its barcode
                  before a new one is created.
              option_groups:
                type: array
                description: >-
                  Restaurants only (a market line with option groups gets
                  options_food_only). Replaces the item's groups.
                items:
                  type: object
                  required:
                    - name
                    - options
                  properties:
                    name:
                      oneOf:
                        - type: string
                          description: The Hebrew name
                        - type: object
                          properties:
                            he:
                              type: string
                            ar:
                              type: string
                            en:
                              type: string
                    type:
                      type: string
                      enum:
                        - single
                        - multi
                    min:
                      type: integer
                    max:
                      type: integer
                    required:
                      type: boolean
                    options:
                      type: array
                      items:
                        type: object
                        required:
                          - name
                        properties:
                          name:
                            oneOf:
                              - type: string
                                description: The Hebrew name
                              - type: object
                                properties:
                                  he:
                                    type: string
                                  ar:
                                    type: string
                                  en:
                                    type: string
                          price:
                            type: integer
                            minimum: 0
                            description: Agorot
              addon_external_ids:
                type: array
                items:
                  type: string
                description: >-
                  Add-ons from addons (this call or an earlier one). Replaces
                  the item's add-ons.
    MenuImportResult:
      type: object
      properties:
        dry_run:
          type: boolean
        created:
          type: object
          properties:
            categories:
              type: integer
            items:
              type: integer
            addons:
              type: integer
        updated:
          type: object
          properties:
            categories:
              type: integer
            items:
              type: integer
            addons:
              type: integer
        switched_off:
          type: object
          properties:
            categories:
              type: integer
            items:
              type: integer
            addons:
              type: integer
        unchanged:
          type: integer
          description: Lines that were already up to date
        errors:
          type: array
          description: Lines that were skipped. The rest was saved.
          items:
            type: object
            properties:
              kind:
                type: string
                enum:
                  - category
                  - addon
                  - item
              external_id:
                type:
                  - string
                  - 'null'
              code:
                type: string
                enum:
                  - invalid_line
                  - invalid_external_id
                  - duplicate_external_id
                  - invalid_name
                  - invalid_value
                  - category_not_found
                  - addon_not_found
                  - invalid_options
                  - options_food_only
                  - barcode_taken
                  - deal_item
                  - not_saved
              message:
                type: string
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string
            details:
              type:
                - object
                - 'null'
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: >-
        The store's API key (xfp_test_… on the sandbox, xfp_live_… on live).
        XFaster creates it and shows it once. An integrator key (one key for a
        POS company) also sends X-XFaster-Store.
    storeHeader:
      type: apiKey
      in: header
      name: X-XFaster-Store
      description: >-
        Integrator keys only: the XFaster store id this request is for. It must
        be linked to your key (else 403 store_not_linked); without it 400
        store_header_required. Store keys don't send it.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.