Skip to content
Developer preview: the API may change before general availability. Request access

Import a brand's menu

PUT
/brands/{brand_id}/menu
curl --request PUT \
--url https://sandbox.partners.loops.sa/v1/brands/brd_9Lk3Vb/menu \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Loops-Merchant-Id: mer_7Hq2Lw8Z' \
--data '{ "categories": [ { "external_ref": "CAT-BURGERS", "name": { "en": "Burgers", "ar": "برجر" }, "sort_order": 1 } ], "modifier_groups": [ { "external_ref": "MG-EXTRAS", "name": { "en": "Extras", "ar": "إضافات" }, "min_selections": 0, "max_selections": 3, "options": [ { "external_ref": "OPT-CHEESE", "name": { "en": "Extra cheese", "ar": "جبنة إضافية" }, "price": { "amount": 300, "currency": "SAR" } } ] } ], "products": [ { "external_ref": "PRD-CHICKEN-BURGER", "category_external_ref": "CAT-BURGERS", "name": { "en": "Chicken Burger", "ar": "برجر دجاج" }, "calories": 540, "variants": [ { "external_ref": "VAR-CB-REG", "name": { "en": "Regular", "ar": "عادي" }, "price": { "amount": 2500, "currency": "SAR" } }, { "external_ref": "VAR-CB-LRG", "name": { "en": "Large", "ar": "كبير" }, "price": { "amount": 3200, "currency": "SAR" } } ], "modifier_group_external_refs": [ "MG-EXTRAS" ] } ] }'

Replaces the brand’s whole menu: categories, products, variants and modifier groups, keyed by your own external_ref values. Anything not in the request is removed from the menu. Up to 5 MB of JSON.

Importing doesn’t change what delivery apps show. Publish the menu when you’re ready.

The import runs in the background: you get a job, and job.completed when it finishes.

brand_id
required
string

The brand ID (brd_…).

Loops-Merchant-Id
string

The merchant you are acting for (mer_…). Required for platform applications; optional for direct integrations, which have a single merchant.

Media typeapplication/json
object
categories
required
Array<object>
object
external_ref
required

Your own ID for this object, from your system. Unique within its type for the merchant (for menu items, within the brand). Letters, digits, -, _, . and :.

string
>= 1 characters <= 64 characters /^[A-Za-z0-9._:-]+$/
name
required

Text in English and Arabic. Delivery apps in Saudi Arabia show both, so both are required.

object
en
required
string
>= 1 characters <= 120 characters
ar
required
string
>= 1 characters <= 120 characters
description

Text in English and Arabic.

object
en
string | null
ar
string | null
sort_order
integer
0
modifier_groups
Array<object>

A set of choices, such as “Sauce” or “Extras”, that you can attach to several products.

object
external_ref
required

Your own ID for this object, from your system. Unique within its type for the merchant (for menu items, within the brand). Letters, digits, -, _, . and :.

string
>= 1 characters <= 64 characters /^[A-Za-z0-9._:-]+$/
name
required

Text in English and Arabic. Delivery apps in Saudi Arabia show both, so both are required.

object
en
required
string
>= 1 characters <= 120 characters
ar
required
string
>= 1 characters <= 120 characters
min_selections
required

0 makes the group optional; 1 or more makes it required.

integer
max_selections
required

Must be at least min_selections.

integer
>= 1
options
required
Array<object>
>= 1 items
object
external_ref
required

Your own ID for this object, from your system. Unique within its type for the merchant (for menu items, within the brand). Letters, digits, -, _, . and :.

string
>= 1 characters <= 64 characters /^[A-Za-z0-9._:-]+$/
name
required

Text in English and Arabic. Delivery apps in Saudi Arabia show both, so both are required.

object
en
required
string
>= 1 characters <= 120 characters
ar
required
string
>= 1 characters <= 120 characters
price
required

An amount in minor units (halalas for SAR), including VAT, as customers see it.

object
amount
required
integer
currency
required

ISO 4217 code. Only SAR for now.

string
calories
integer | null
available
boolean
default: true
sort_order
integer
0
products
required
Array<object>

Give either price, or at least one variant; not both.

object
external_ref
required

Your own ID for this object, from your system. Unique within its type for the merchant (for menu items, within the brand). Letters, digits, -, _, . and :.

string
>= 1 characters <= 64 characters /^[A-Za-z0-9._:-]+$/
category_external_ref
required
string
name
required

Text in English and Arabic. Delivery apps in Saudi Arabia show both, so both are required.

object
en
required
string
>= 1 characters <= 120 characters
ar
required
string
>= 1 characters <= 120 characters
description

Text in English and Arabic.

object
en
string | null
ar
string | null
image_url

Public HTTPS URL of a JPG or PNG, at least 800 × 800 px. Loops downloads and stores a copy.

string | null format: uri
price
One of:

An amount in minor units (halalas for SAR), including VAT, as customers see it.

object
amount
required
integer
currency
required

ISO 4217 code. Only SAR for now.

string
sku
string | null
barcode
string | null
calories

Most delivery apps in Saudi Arabia require calories on menu items.

integer | null
variants
Array<object>

A version of a product with its own price, such as a size.

object
external_ref
required

Your own ID for this object, from your system. Unique within its type for the merchant (for menu items, within the brand). Letters, digits, -, _, . and :.

string
>= 1 characters <= 64 characters /^[A-Za-z0-9._:-]+$/
name
required

Text in English and Arabic. Delivery apps in Saudi Arabia show both, so both are required.

object
en
required
string
>= 1 characters <= 120 characters
ar
required
string
>= 1 characters <= 120 characters
price
required

An amount in minor units (halalas for SAR), including VAT, as customers see it.

object
amount
required
integer
currency
required

ISO 4217 code. Only SAR for now.

string
sku
string | null
barcode
string | null
calories
integer | null
available
boolean
default: true
modifier_group_external_refs

Modifier groups shown with this product, in this order.

Array<string>
available

false shows the product as unavailable on delivery apps.

boolean
default: true
sort_order
integer
0

Import started.

Media typeapplication/json
object
id
required
string
type
required
string
Allowed values: menu_import menu_publish
brand_id
string
status
required

pending and running while in progress. succeeded: everything applied. partially_failed: some results failed (publish only). failed: nothing applied.

string
Allowed values: pending running succeeded partially_failed failed
errors

For a failed import, every problem found. An import is all or nothing.

Array<object>
object
external_ref

The menu item the problem is about, if any.

string | null
field
string | null
code
required
string
message
required
string
results

For a publish, one result per location and delivery app.

Array<object>
object
location_id
required
string
channel_connection_id
required
string
channel
required
string
status
required
string
Allowed values: pending succeeded failed
errors

Items the delivery app rejected, and why.

Array<object>
object
external_ref

The menu item the problem is about, if any.

string | null
field
string | null
code
required
string
message
required
string
created_at
required
string format: date-time
completed_at
string | null format: date-time
Example
{
"id": "job_01J9R2K7TN",
"type": "menu_import",
"status": "pending",
"errors": [
{
"external_ref": "PRD-CHICKEN-BURGER",
"field": "image_url",
"code": "image_unreachable",
"message": "The image URL returned 404."
}
],
"results": [
{
"status": "pending",
"errors": [
{
"external_ref": "PRD-CHICKEN-BURGER",
"field": "image_url",
"code": "image_unreachable",
"message": "The image URL returned 404."
}
]
}
]
}

The request is invalid. errors lists each problem.

Media typeapplication/problem+json

RFC 9457 Problem Details with a stable code.

object
type
required
string format: uri
title
required
string
status
required
integer
code
required

Stable machine-readable error code. Switch on this, not on title.

string
Allowed values: invalid_request invalid_file merchant_required unauthorized insufficient_scope scope_not_granted connection_not_active application_suspended not_found merchant_not_found merchant_exists external_ref_conflict business_profile_locked idempotency_key_reused rate_limited upstream_unavailable
detail
string
request_id
required
string
existing_id

For external_ref_conflict, the ID of the object that already uses the reference.

string
errors

Field errors, for invalid_request.

Array<object>
object
field
string
message
string
Example
{
"type": "https://docs.partners.loops.sa/errors/external_ref_conflict",
"title": "External reference already used",
"status": 409,
"code": "invalid_request",
"detail": "A location with external_ref BR-01 already exists.",
"request_id": "req_01J9X7P2QK",
"existing_id": "loc_2Xf9K",
"errors": [
{
"field": "products[3].price",
"message": "Required unless the product has variants."
}
]
}

The access token is missing, expired or invalid.

Media typeapplication/problem+json

RFC 9457 Problem Details with a stable code.

object
type
required
string format: uri
title
required
string
status
required
integer
code
required

Stable machine-readable error code. Switch on this, not on title.

string
Allowed values: invalid_request invalid_file merchant_required unauthorized insufficient_scope scope_not_granted connection_not_active application_suspended not_found merchant_not_found merchant_exists external_ref_conflict business_profile_locked idempotency_key_reused rate_limited upstream_unavailable
detail
string
request_id
required
string
existing_id

For external_ref_conflict, the ID of the object that already uses the reference.

string
errors

Field errors, for invalid_request.

Array<object>
object
field
string
message
string
Example
{
"type": "https://docs.partners.loops.sa/errors/external_ref_conflict",
"title": "External reference already used",
"status": 409,
"code": "invalid_request",
"detail": "A location with external_ref BR-01 already exists.",
"request_id": "req_01J9X7P2QK",
"existing_id": "loc_2Xf9K",
"errors": [
{
"field": "products[3].price",
"message": "Required unless the product has variants."
}
]
}

Not allowed: the scope isn’t approved or granted, the connection is not active, or your application is suspended. See code.

Media typeapplication/problem+json

RFC 9457 Problem Details with a stable code.

object
type
required
string format: uri
title
required
string
status
required
integer
code
required

Stable machine-readable error code. Switch on this, not on title.

string
Allowed values: invalid_request invalid_file merchant_required unauthorized insufficient_scope scope_not_granted connection_not_active application_suspended not_found merchant_not_found merchant_exists external_ref_conflict business_profile_locked idempotency_key_reused rate_limited upstream_unavailable
detail
string
request_id
required
string
existing_id

For external_ref_conflict, the ID of the object that already uses the reference.

string
errors

Field errors, for invalid_request.

Array<object>
object
field
string
message
string
Example
{
"type": "https://docs.partners.loops.sa/errors/external_ref_conflict",
"title": "External reference already used",
"status": 409,
"code": "invalid_request",
"detail": "A location with external_ref BR-01 already exists.",
"request_id": "req_01J9X7P2QK",
"existing_id": "loc_2Xf9K",
"errors": [
{
"field": "products[3].price",
"message": "Required unless the product has variants."
}
]
}

Not found, or not accessible with this connection.

Media typeapplication/problem+json

RFC 9457 Problem Details with a stable code.

object
type
required
string format: uri
title
required
string
status
required
integer
code
required

Stable machine-readable error code. Switch on this, not on title.

string
Allowed values: invalid_request invalid_file merchant_required unauthorized insufficient_scope scope_not_granted connection_not_active application_suspended not_found merchant_not_found merchant_exists external_ref_conflict business_profile_locked idempotency_key_reused rate_limited upstream_unavailable
detail
string
request_id
required
string
existing_id

For external_ref_conflict, the ID of the object that already uses the reference.

string
errors

Field errors, for invalid_request.

Array<object>
object
field
string
message
string
Example
{
"type": "https://docs.partners.loops.sa/errors/external_ref_conflict",
"title": "External reference already used",
"status": 409,
"code": "invalid_request",
"detail": "A location with external_ref BR-01 already exists.",
"request_id": "req_01J9X7P2QK",
"existing_id": "loc_2Xf9K",
"errors": [
{
"field": "products[3].price",
"message": "Required unless the product has variants."
}
]
}

The request conflicts with the current state, for example an external_ref already in use, or a business profile that is in review. See code.

Media typeapplication/problem+json

RFC 9457 Problem Details with a stable code.

object
type
required
string format: uri
title
required
string
status
required
integer
code
required

Stable machine-readable error code. Switch on this, not on title.

string
Allowed values: invalid_request invalid_file merchant_required unauthorized insufficient_scope scope_not_granted connection_not_active application_suspended not_found merchant_not_found merchant_exists external_ref_conflict business_profile_locked idempotency_key_reused rate_limited upstream_unavailable
detail
string
request_id
required
string
existing_id

For external_ref_conflict, the ID of the object that already uses the reference.

string
errors

Field errors, for invalid_request.

Array<object>
object
field
string
message
string
Example
{
"type": "https://docs.partners.loops.sa/errors/external_ref_conflict",
"title": "External reference already used",
"status": 409,
"code": "invalid_request",
"detail": "A location with external_ref BR-01 already exists.",
"request_id": "req_01J9X7P2QK",
"existing_id": "loc_2Xf9K",
"errors": [
{
"field": "products[3].price",
"message": "Required unless the product has variants."
}
]
}