---
title: "Bulk-create promotional codes"
method: POST
path: "/api/v1/promotions/bulk"
tags: ["Promotions"]
---

# Bulk-create promotional codes

`POST /api/v1/promotions/bulk`

Creates many promotions in a single request, all sharing the configuration in `promotion`.
Supply the codes explicitly via `codes`, OR ask the server to generate them via `generate`
(exactly one of the two is required). Up to 1000 codes may be created per request.

The response reports partial success: every requested code appears either in `created`
or in `failed` with a reason (e.g. already taken, duplicate in request). A `201` is
returned when all codes were created, `207` when some failed.

## Request body

- object
  - `promotion` object, required — Shared configuration applied to every code
    - `account_id` integer — Target catalog (defaults to the key account; must be it or a subcatalog)
    - `listing_id` integer — Listing to scope the promotions to (omit for account-wide)
    - `name` string, required — Display name
    - `description` string — Optional description
    - `amount` number, required — Discount amount (0-100 when discount_type is percent)
    - `discount_type` 'flat' | 'percent', required
    - `usage_type` 'once-per-user' | 'unlimited' | 'one-time', required
    - `active` boolean
    - `start_date` string — ISO-8601 start (UTC)
    - `end_date` string — ISO-8601 end (UTC)
    - `max_per_user` integer
    - `max_total_use` integer
    - `enable_for_multi_seat_purchases` boolean
  - `codes` string[] — Explicit list of codes to create (mutually exclusive with generate)
  - `generate` object — Server-side generation spec (mutually exclusive with codes)
    - `count` integer, required — How many codes to generate (1-1000)
    - `prefix` string — Optional prefix prepended to every generated code
    - `length` integer — Length of the random portion (default 8)
    - `charset` 'alphanumeric' | 'alpha' | 'numeric' — Character set (default alphanumeric)

## Response `201`

all codes created

- object
  - `summary` object, required
    - `requested` integer, required — Total codes requested
    - `created` integer, required — Number of promotions created
    - `failed` integer, required — Number of codes that failed
  - `created` object[], required
    - `id` integer, required — Identifier for the promotion
    - `listing_id` integer, nullable — Listing the promotion is scoped to (null = account-wide)
    - `code` string, required — The promotional code (always upper-cased)
    - `name` string, required — Display name shared across the batch
    - `description` string, nullable — Optional description
    - `amount` string, required — Discount amount (percent 0-100, or a fixed currency amount)
    - `discount_type` 'flat' | 'percent', required — Whether the amount is a flat value or a percentage
    - `usage_type` 'once-per-user' | 'unlimited' | 'one-time', required — How often the code may be redeemed
    - `active` boolean, required — Whether the promotion is active
    - `start_date` string, nullable — When the promotion becomes valid (UTC)
    - `end_date` string, nullable — When the promotion expires (UTC)
    - `max_per_user` integer, nullable — Maximum redemptions per user
    - `max_total_use` integer, nullable — Maximum total redemptions
    - `status` string — Derived status (inactive, upcoming, active or expired)
    - `created_at` string — When the promotion was created (UTC)
  - `failed` object[], required
    - `code` string, nullable — The code that could not be created (null for generation failures)
    - `errors` string[], required — Why this code was not created

## Other responses

- `207` — some codes created, some failed
- `422` — shared configuration invalid, or code source missing/ambiguous

---

[API](https://skmtc.net/instructure/apis/canvas-catalog-api.md) · [All operations](https://skmtc.net/instructure/apis/canvas-catalog-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/instructure/canvas-catalog-api/revisions/78578cd3e37a/schema)
