Guidelines

This guide provides modelling, formatting and operational guidance for partners integrating with the Promotions API.

For endpoint details, authentication, request examples and the full upload workflow, see the Promotions API Integration Guide.


Promotion Model

Each promotion represents one partner-defined promotion with one mechanic, one condition model and one reward model.

At a high level:

  • promotion_type defines the promotion mechanic
  • condition defines what the customer basket must satisfy
  • reward defines what the customer receives

Example:

{
  "promotion_id": "GROCER-PCT-OFF-PASTA-2026Q2",
  "promotion_type": "PERCENTAGE_OFF_ON_ITEMS",
  "name": "20% off selected pasta",
  "user_target": "LOYALTY_CUSTOMER",
  "sites": [
    "grocer-gb-london-001"
  ],
  "fulfillment_method": "DELIVERY",
  "start_at": "2026-06-01T00:00:00Z",
  "end_at": "2026-06-30T23:59:59Z",
  "condition": {
    "items": [
      "5000169816246",
      "5000169816247"
    ]
  },
  "reward": {
    "percentage": 20
  }
}

Promotion Fields

promotion_id

Partner-defined identifier for the promotion.

The promotion_id is used for reconciliation, update and synchronisation operations. Partners must provide a promotion_id for every promotion.

Each promotion_id must be unique within a file. One promotion_id identifies exactly one promotion with one mechanic, one condition model and one reward model.

Recommendations:

  • Use stable identifiers for promotions you expect to reconcile or update over time
  • Keep IDs human-readable where possible
  • Use a format that helps identify the campaign, mechanic and time period
  • Do not reuse a promotion_id while a previous promotion with the same identifier is still processing or active
  • Use a new promotion_id for a different mechanic, reward value or non-contiguous promotion window
  • If some items need one reward value and other items need a different reward value, create separate promotions with separate promotion_id values
  • Ensure each promotion_id appears only once in a file

Example: GROCER-PCT-OFF-PASTA-2026Q2

Maximum length: 128 characters.

Allowed characters: letters, numbers, underscore (_), hyphen (-) and period (.).


promotion_type

Defines the promotion mechanic.

Supported promotion_type values:

ValueDescription
PERCENTAGE_OFF_ON_ITEMSPercentage discount on selected items
AMOUNT_OFF_ON_ITEMSFixed amount discount on selected items
PERCENTAGE_OFF_ON_SECOND_ITEMPercentage discount on the second qualifying item
PERCENTAGE_OFF_ON_SINGLE_ITEM_MULTIBUYPercentage discount when buying multiple units of the same item
FIXED_PRICE_ON_SINGLE_ITEM_MULTIBUYFixed price when buying multiple units of the same item
PERCENTAGE_OFF_ON_MULTIPLE_ITEM_MULTIBUYPercentage discount when buying a bundle of different items
FIXED_PRICE_ON_MULTIPLE_ITEM_MULTIBUYFixed price when buying a bundle of different items
PERCENTAGE_OFF_ON_BASKETPercentage discount on basket total
FREE_DELIVERYFree delivery promotion
BUY_X_FOR_YBuy X qualifying items and receive reward items
BUY_X_PLUS_SAVE_Y_PERCENTBuy X or more qualifying items and save Y%

Recommendations:

  • Use item-level promotion types when the promotion applies to specific catalogue items
  • Use basket-level promotion types when the promotion applies to the customer basket
  • Use FREE_DELIVERY only for delivery-fee promotions
  • Use multibuy promotion types when the customer must purchase a qualifying quantity
  • Use fixed-price multibuy promotion types when the customer pays a fixed final price for the qualifying items
  • Use future promotion types only once enabled for your integration

name

Human-readable promotion name.

Recommendations:

  • Use a clear name that describes the campaign or reward
  • Keep names useful for operational support and reconciliation
  • Do not rely on name as the unique identifier; use promotion_id for matching and updates

Example: 20% off selected pasta


sites

List of Deliveroo site identifiers where the promotion applies.

Recommendations:

  • Ensure all sites are already synced with Deliveroo
  • Remove inactive, closed or invalid sites before upload
  • Keep sites consistent across related promotions where the same campaign should apply to the same estate

Unknown or inactive sites may fail during processing with a SITE_NOT_FOUND error.


user_target

Defines the customer segment eligible for the promotion.

Supported user_target values:

ValueDescriptionNotes
ALL_CUSTOMERSAll customersDefault if user_target is omitted
NEW_CUSTOMERFirst-time orderers on the storeUse when the promotion should only apply to customers placing their first order with the store
LAPSED_CUSTOMERCustomers who have not ordered on the store for the past 365 daysUse for reactivation campaigns
LOYALTY_CUSTOMERLoyalty programme membersUse only for partners with a configured loyalty programme on Deliveroo. Please consult your Account Manager before integrating loyalty promotions.
PLUS_SUBSCRIBERDeliveroo Plus subscribersDo not use with FREE_DELIVERY, because Plus already includes delivery benefits
STUDENTVerified studentsUse for student-targeted campaigns
NEW_TO_BRANDCustomers who have never ordered from this brand beforeUse for brand acquisition campaigns

Use ALL_CUSTOMERS, an empty value or omit user_target for all-customer promotions.

Recommendations:

  • Keep customer targeting consistent across related promotions
  • Avoid creating overlapping targeted and non-targeted versions of the same promotion unless this has been agreed during onboarding
  • Use LOYALTY_CUSTOMER only for partners with a configured loyalty programme on Deliveroo
  • Do not use unsupported customer segments unless agreed during onboarding

fulfillment_method

Restricts the promotion to a specific fulfilment channel.

Supported values:

  • ANY
  • DELIVERY
  • COLLECTION

If omitted, fulfillment_method defaults to ANY.

Recommendations:

  • Use DELIVERY for delivery-only promotions
  • Use COLLECTION for collection-only promotions
  • Use ANY only where the promotion should apply across supported fulfilment channels
  • Keep fulfillment_method consistent across related promotions where the same campaign should apply to the same channel

start_at and end_at

Defines the promotion schedule.

Accepted format: ISO 8601 timestamp with UTC or an explicit offset.

Example: 2026-06-01T00:00:00Z

Both start_at and end_at are inclusive. The promotion is active at start_at and remains active until and including end_at.

Recommendations:

  • Use UTC timestamps or explicit offsets
  • Ensure end_at is greater than start_at for newly scheduled promotions
  • Do not create overlapping promotion windows for the same item and site
  • Use a different promotion_id for non-contiguous promotion windows
  • To end an active promotion early, update the promotion with a new end_at value
  • If the promotion should end immediately, set end_at to a timestamp in the past, then poll the returned job_id if confirmation is needed

Invalid schedules may fail with a SCHEDULE_INVALID error. Overlapping promotions may fail with a PROMOTION_OVERLAP error.


Condition Fields

The condition object defines what the customer basket must satisfy before the promotion can apply.

Use condition fields for qualifying items, required quantities, basket thresholds and alcohol eligibility.

Example:

"condition": {
  "items": [
    "5000169816246",
    "5000169816247"
  ],
  "quantity": 3
}

condition.items

Array of partner catalogue item identifiers.

Required for item-level promotion types.

Do not include condition.items for basket-level or delivery-fee promotions.

Recommendations:

  • Use the same IDs provided through catalogue synchronisation
  • Ensure items already exist in Deliveroo’s catalogue before upload
  • Remove inactive or deleted items before upload
  • Validate item coverage before submitting large uploads
  • Keep each promotion_id to a maximum of 2,000 items

Unknown items may fail during processing with an ITEM_NOT_FOUND error.


condition.quantity

Number of qualifying items the customer must purchase before the promotion applies.

Required for multibuy and buy-X promotion types.

Recommendations:

  • Use integer values
  • Use condition.quantity for the qualifying purchase quantity
  • For single-item multibuy promotions, use the quantity required for the same item
  • For multiple-item multibuy promotions, use the quantity required across the qualifying item set
  • Do not use condition.quantity for simple percentage-off or amount-off item promotions unless the promotion type requires it

Example:

"condition": {
  "items": [
    "5000169816246"
  ],
  "quantity": 3
}

condition.min_order_value

Minimum basket subtotal required before the promotion applies, in minor currency units.

Example: 2000

For GBP, 2000 means £20.00.

Recommendations:

  • Use integer minor-unit values
  • Use only with promotion types that support basket thresholds, such as PERCENTAGE_OFF_ON_BASKET or FREE_DELIVERY
  • Ensure the value reflects the intended basket subtotal threshold before discounts

Applicable promotion types include:

  • PERCENTAGE_OFF_ON_BASKET
  • FREE_DELIVERY

condition.alcohol_allowed

Defines whether a basket or delivery promotion may apply to baskets containing alcoholic items.

If omitted, condition.alcohol_allowed defaults to false.

Recommendations:

  • Use only where relevant for basket-level or delivery-fee promotions
  • Set deliberately based on the partner’s policy and applicable market rules
  • Do not rely on this field for item catalogue classification

Applicable promotion types include:

  • PERCENTAGE_OFF_ON_BASKET
  • FREE_DELIVERY

Reward Fields

The reward object defines what the customer receives when the promotion condition is met.

Use reward fields for percentage discounts, amount-off discounts, fixed prices, reward quantities and discount caps.

Example:

"reward": {
  "percentage": 20
}

reward.percentage

Percentage discount value.

Example: 20

Recommendations:

  • Use integer percentage values
  • Use reward.percentage with percentage-based promotion types
  • Validate discount values before upload

Applicable promotion types include:

  • PERCENTAGE_OFF_ON_ITEMS
  • PERCENTAGE_OFF_ON_SECOND_ITEM
  • PERCENTAGE_OFF_ON_SINGLE_ITEM_MULTIBUY
  • PERCENTAGE_OFF_ON_MULTIPLE_ITEM_MULTIBUY
  • PERCENTAGE_OFF_ON_BASKET
  • BUY_X_PLUS_SAVE_Y_PERCENT

reward.amount_off

Fixed discount value in minor currency units.

Example: 100

For GBP, 100 means £1.00.

Currency is derived from the brand configuration. Each brand_id is associated with a single market during onboarding.

Recommendations:

  • Use integer minor-unit values
  • Use reward.amount_off with amount-off promotion types
  • Ensure the value represents the discount amount, not the final customer price

Applicable promotion types include:

  • AMOUNT_OFF_ON_ITEMS

reward.fixed_price

Fixed final price for a qualifying multibuy promotion, in minor currency units.

Example: 400

For GBP, 400 means £4.00.

Recommendations:

  • Use integer minor-unit values
  • Use reward.fixed_price only with fixed-price promotion types
  • Ensure the value represents the final customer price for the qualifying items, not the discount amount

Applicable promotion types include:

  • FIXED_PRICE_ON_SINGLE_ITEM_MULTIBUY
  • FIXED_PRICE_ON_MULTIPLE_ITEM_MULTIBUY

reward.quantity

Reward quantity, where applicable.

Use reward.quantity for the quantity the customer receives as the reward, not the qualifying purchase quantity.

For buy-X-get-Y mechanics:

  • condition.quantity defines X, the number of qualifying items the customer must buy
  • reward.quantity defines Y, the number of reward items the customer receives

Example:

"condition": {
  "items": [
    "5000169816301"
  ],
  "quantity": 3
},
"reward": {
  "quantity": 1
}

Applicable promotion types include:

  • BUY_X_FOR_Y

reward.max_discount

Maximum discount amount, in minor currency units.

Example: 500

For GBP, 500 means £5.00.

Recommendations:

  • Use integer minor-unit values
  • Use only where the promotion type supports a maximum discount cap
  • Use to limit the maximum customer discount for percentage-based basket promotions

Applicable promotion types include:

  • PERCENTAGE_OFF_ON_BASKET

Multibuy Promotions

The Promotions API supports single-item and multiple-item multibuy promotions.

Multibuy promotions use:

  • condition.items to define qualifying items
  • condition.quantity to define the required purchase quantity
  • reward.percentage or reward.fixed_price to define the customer reward

Single-item multibuy

Triggered when customers purchase multiple units of the same item.

Example: buy 2 sandwiches for £2.

Use single-item multibuy promotion types when the same item must be purchased multiple times.

Applicable promotion types include:

  • PERCENTAGE_OFF_ON_SINGLE_ITEM_MULTIBUY
  • FIXED_PRICE_ON_SINGLE_ITEM_MULTIBUY

Example:

{
  "promotion_type": "FIXED_PRICE_ON_SINGLE_ITEM_MULTIBUY",
  "condition": {
    "items": [
      "5000169880001"
    ],
    "quantity": 2
  },
  "reward": {
    "fixed_price": 200
  }
}

Multiple-item multibuy

Triggered when customers purchase qualifying quantities across multiple items in condition.items.

Example: any 3 snacks, 25% off.

Use multiple-item multibuy promotion types when customers can build a bundle from a set of different items.

Applicable promotion types include:

  • PERCENTAGE_OFF_ON_MULTIPLE_ITEM_MULTIBUY
  • FIXED_PRICE_ON_MULTIPLE_ITEM_MULTIBUY

Example:

{
  "promotion_type": "PERCENTAGE_OFF_ON_MULTIPLE_ITEM_MULTIBUY",
  "condition": {
    "items": [
      "5000169816101",
      "5000169816102",
      "5000169816103"
    ],
    "quantity": 3
  },
  "reward": {
    "percentage": 25
  }
}

BUY_X_FOR_Y

Triggered when customers buy a required quantity of qualifying items and receive a reward quantity.

Example: buy 2, get 1 free.

Use:

  • condition.items for the qualifying items
  • condition.quantity for the number of items the customer must buy
  • reward.quantity for the number of reward items the customer receives

Example:

{
  "promotion_type": "BUY_X_FOR_Y",
  "condition": {
    "items": [
      "5000169816301"
    ],
    "quantity": 2
  },
  "reward": {
    "quantity": 1
  }
}

BUY_X_PLUS_SAVE_Y_PERCENT

Triggered when customers buy at least a required quantity of qualifying items and receive a percentage discount.

Example: buy 3 or more, save 15%.

Use:

  • condition.items for the qualifying items
  • condition.quantity for the minimum number of items the customer must buy
  • reward.percentage for the percentage discount

Example:

{
  "promotion_type": "BUY_X_PLUS_SAVE_Y_PERCENT",
  "condition": {
    "items": [
      "5000169816401",
      "5000169816402"
    ],
    "quantity": 3
  },
  "reward": {
    "percentage": 15
  }
}

Basket and Delivery Promotions

Basket promotions

Basket promotions apply to the customer basket rather than specific items.

Applicable promotion types include:

  • PERCENTAGE_OFF_ON_BASKET

Basket promotions do not require condition.items.

Common fields include:

  • condition.min_order_value
  • condition.alcohol_allowed
  • reward.percentage
  • reward.max_discount

Example:

{
  "promotion_type": "PERCENTAGE_OFF_ON_BASKET",
  "condition": {
    "min_order_value": 2000,
    "alcohol_allowed": false
  },
  "reward": {
    "percentage": 10,
    "max_discount": 500
  }
}

Free delivery promotions

Free delivery promotions apply to the delivery fee.

Applicable promotion types include:

  • FREE_DELIVERY

Free delivery promotions do not require condition.items.

Common fields include:

  • condition.min_order_value
  • condition.alcohol_allowed

Example:

{
  "promotion_type": "FREE_DELIVERY",
  "condition": {
    "min_order_value": 1500,
    "alcohol_allowed": false
  },
  "reward": {}
}

Promotion Synchronisation

Promotion uploads use a synchronisation model. Each successful upload represents the desired promotion state for the brand.

When a new file is processed:

  • new promotions are created
  • changed promotions are updated
  • unchanged promotions are left as they are
  • promotions missing from the file are removed

Partners should submit a complete file for the brand unless they intentionally want omitted promotions to be removed.

To remove all promotions for a brand, submit an empty promotions array if this behaviour has been enabled for your integration.

Example:

{
  "promotions": []
}

Overlapping Promotions

Overlapping promotions for the same items, sites and time windows may fail during processing with a PROMOTION_OVERLAP error.

Recommendations:

  • Avoid overlapping active promotions for the same item and site
  • Check existing campaign calendars before uploading new promotions
  • Use distinct promotion_id values for separate campaigns, mechanics, reward values or non-contiguous windows
  • Avoid creating overlapping targeted and non-targeted versions of the same promotion unless agreed during onboarding

Upload Size and Structure

Promotion uploads support files up to 50 MB.

Each promotion_id can contain up to 2,000 items.

Large promotions with many sites or many items are split internally during processing. Partners do not need to manually split promotions by site or item unless required operationally.

Recommendations:

  • Group related promotions into a single upload where practical
  • Avoid unnecessary duplication across promotion objects
  • Validate JSON locally before upload
  • Keep each upload focused on the intended promotion state for the relevant brand
  • Remove obsolete or invalid promotion objects before submitting the file
  • Ensure each promotion_id appears only once in the file
  • Keep each promotion_id within the 2,000 item limit

Files above the maximum size may fail with a FILE_TOO_LARGE error. Malformed JSON may fail with an INVALID_JSON error. Duplicate promotion identifiers may fail with a DUPLICATE_PROMOTION_ID error.


Async Processing and Polling

Promotion uploads and updates are processed asynchronously.

Each async operation returns a job_id. Partners can use this job_id to poll the job-status endpoint until the operation reaches a terminal state.

Recommended polling pattern:

  • Start with a short initial delay
  • Use exponential backoff
  • Cap polling intervals for long-running jobs
  • Stop polling once a terminal status is reached
  • Avoid continuous polling loops

Terminal statuses include:

  • completed: All promotions were processed successfully
  • partial_success: Some promotions succeeded and others failed. Partners should inspect counters and errors to reconcile the outcome
  • failed: The upload or update did not complete successfully. Inspect the returned errors before retrying

Updating or Ending Promotions

Use PUT /brands/{brand_id}/promotions/{promotion_id} to update an existing promotion.

The request body should contain the full updated promotion object. The promotion_id in the URL path must match the promotion_id in the request body.

Common use cases:

  • Correcting promotion details during the day
  • Updating price, discount, items, sites, timing or targeting
  • Ending an active promotion by updating its end time

Recommendations:

  • Send the full promotion object when updating a promotion
  • Use the Integration Guide for endpoint request and response examples
  • Poll the returned job_id if your integration needs confirmation
  • Avoid retrying update operations without first checking job state
  • If a bulk upload is processing the same promotion_id, retry after the bulk job reaches a terminal state
  • Re-upload promotions for broad campaign changes across many promotions

Errors

The Promotions API can return errors in two ways:

  • Synchronous API errors returned immediately by an endpoint
  • Asynchronous processing errors returned through job status after upload or update processing

Standard Error Response

Synchronous API errors use the following structure:

{
  "error_code": "INVALID_REQUEST",
  "error_message": "Field 'brand_id' is required.",
  "details": [
    {
      "field": "brand_id",
      "message": "must be provided in the URL path"
    }
  ]
}

The request correlation ID is returned in the X-Request-Id HTTP response header. Log this value alongside errors for operational support.


Synchronous API Errors

CodeMeaningRecommended action
INVALID_REQUESTRequest is malformed or missing required fieldsFix the request and retry
UNAUTHENTICATEDBearer token is missing or expiredRefresh the token and retry
FORBIDDENClient is not authorised for the brand or scopeContact Deliveroo onboarding
NOT_FOUNDUnknown job_id or promotion_idCheck the identifier
CONFLICTResource is in an incompatible stateRe-fetch state before retrying
RATE_LIMITEDRate limit exceededBack off before retrying
INTERNAL_ERRORTemporary platform errorRetry with exponential backoff

Recommendations:

  • Log request correlation IDs for operational support
  • Retry 5xx responses using exponential backoff
  • Respect rate-limit wait headers on 429 responses
  • Check job state before retrying conflicting operations

S3 Upload Errors

StatusMeaningRecommended action
200 OKFile accepted by S3No action required
400 Bad RequestMalformed request to S3Check method, headers and payload
403 ForbiddenPresigned URL expired or signature mismatchRequest a new upload URL

Processing Errors

CodeMeaningRecommended action
ITEM_NOT_FOUNDItem identifier did not resolve to a catalogue item for this brandVerify the item exists in the Deliveroo catalogue and re-upload corrected items
SITE_NOT_FOUNDSite is not registered or active for the brandConfirm site synchronisation and active status
PROMOTION_OVERLAPAnother active promotion already exists for an overlapping item, site and time windowAdjust the promotion window or remove the duplicate promotion
HFSS_VIOLATIONItem is subject to HFSS promotion restrictionsConfirm affected items during onboarding
SCHEDULE_INVALIDPromotion timing is invalidFix timestamps and re-upload
FILE_TOO_LARGEUpload exceeds the maximum file sizeSplit into multiple uploads
INVALID_PROMOTION_TYPEpromotion_type is not supportedUse a supported promotion_type
INVALID_CONDITIONThe condition object is missing, malformed or incompatible with the selected promotion_typeFix the condition fields and re-upload
INVALID_REWARDThe reward object is missing, malformed or incompatible with the selected promotion_typeFix the reward fields and re-upload
INVALID_JSONFile could not be parsed as JSONValidate JSON locally before upload
DUPLICATE_FILEUploaded file matches the most recent successful upload for the brand, so promotions were not reprocessedTreat as a no-op unless the duplicate upload was unintended
DUPLICATE_PROMOTION_IDThe same promotion_id appears on more than one promotion object in the fileGive each promotion object a unique promotion_id

Rate Limits

Rate limits are configured per brand during onboarding.

Default rate limits:

EndpointDefault limit
POST /brands/{brand_id}/promotions/upload_url6 per brand per hour
PUT /brands/{brand_id}/promotions/{promotion_id}60 per brand per minute
GET /brands/{brand_id}/promotions/jobs/{job_id}/status60 per brand per minute per job

Rate-limited responses may include:

  • X-Deliveroo-RateLimit-Limit
  • X-Deliveroo-RateLimit-Remaining
  • X-Deliveroo-RateLimit-Wait-Time-Seconds

On a 429, wait at least X-Deliveroo-RateLimit-Wait-Time-Seconds before retrying.


Retry and Duplicate Upload Detection

The bulk upload flow detects duplicate file submissions by comparing each uploaded file with the most recent successful upload for the brand.

If the uploaded file matches the most recent successful upload, the job ends with DUPLICATE_FILE and promotions are not reprocessed.

Single-promotion update requests do not have built-in duplicate detection. Partners should maintain their own request tracking if they require at-most-once behaviour for updates.

Recommendations:

  • Retry upload URL requests where appropriate, especially after transient 429 or 5xx responses
  • Use exponential backoff for transient failures such as 429 and 5xx responses
  • Check the latest job status before retrying update operations
  • Maintain partner-side request tracking if you require at-most-once behaviour for updates

Avoid blindly retrying update requests, as the operation may already be processing asynchronously.


Reconciliation

Partners may choose to reconcile uploads after they reach a terminal status.

Use the job-status response to review:

  • Upload status
  • Promotion counters
  • Promotion item counters
  • Aggregated errors

A partial_success status means some promotions succeeded while others failed. Treat this as an actionable outcome and use the returned counters and errors to identify which promotions need correction or re-upload.

Retain job_id and request correlation IDs for operational support.

The errors array is grouped by promotion_id. Each promotion-level entry contains one or more error types and the related item identifiers, where applicable. Successful promotions do not appear in the errors list.