Spree Commerce Promotions API
Promotions, promotion rules, promotion actions, and coupon codes
Promotions, promotion rules, promotion actions, and coupon codes
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/spree-promotions-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
openapi: 3.0.3
info:
title: Admin Account / Address Promotions API
contact:
name: Spree Commerce
url: https://spreecommerce.org
email: hello@spreecommerce.org
description: "Spree Admin API v3 - Administrative API for managing products, orders, and store settings.\n\n## Authentication\n\nThe Admin API requires a secret API key passed in the `x-spree-api-key` header.\nSecret API keys can be generated in the Spree admin dashboard.\n\n## Response Format\n\nAll responses are JSON. List endpoints return paginated responses with `data` and `meta` keys.\nSingle resource endpoints return a flat JSON object.\n\n## Resource IDs\n\nEvery resource is identified by an opaque string ID (e.g. `prod_86Rf07xd4z`,\n`variant_k5nR8xLq`, `or_UkLWZg9DAJ`). Use these IDs everywhere — URL paths,\nrequest bodies, and Ransack filters all accept them directly.\n\n## Error Handling\n\nErrors return a consistent format:\n```json\n{\n \"error\": {\n \"code\": \"validation_error\",\n \"message\": \"Validation failed\",\n \"details\": { \"name\": [\"can't be blank\"] }\n }\n}\n```\n"
version: v3
servers:
- url: http://{defaultHost}
variables:
defaultHost:
default: localhost:3000
tags:
- name: Promotions
description: Promotions, promotion rules, promotion actions, and coupon codes
paths:
/api/v3/admin/promotions/{promotion_id}/coupon_codes:
parameters:
- name: promotion_id
in: path
required: true
schema:
type: string
get:
summary: List coupon codes for a promotion
tags:
- Promotions
security:
- api_key: []
bearer_auth: []
description: 'Returns the auto-generated coupon codes for a multi-code promotion. Single-code promotions store the code on the promotion itself; this endpoint returns an empty list for them.
**Required scope:** `read_promotions` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nconst { data: coupons } = await client.promotions.couponCodes.list('promo_UkLWZg9DAJ')"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'200':
description: coupon codes found
content:
application/json:
example:
data:
- id: coupon_UkLWZg9DAJ
code: AAA111
state: unused
created_at: '2026-06-12T17:23:52.481Z'
updated_at: '2026-06-12T17:23:52.481Z'
promotion_id: promo_UkLWZg9DAJ
order_id: null
- id: coupon_gbHJdmfrXB
code: BBB222
state: unused
created_at: '2026-06-12T17:23:52.483Z'
updated_at: '2026-06-12T17:23:52.483Z'
promotion_id: promo_UkLWZg9DAJ
order_id: null
meta:
page: 1
limit: 25
count: 2
pages: 1
from: 1
to: 2
in: 2
previous: null
next: null
/api/v3/admin/promotions/{promotion_id}/promotion_actions:
parameters:
- name: promotion_id
in: path
required: true
schema:
type: string
get:
summary: List actions for a promotion
tags:
- Promotions
security:
- api_key: []
bearer_auth: []
description: '**Required scope:** `read_promotions` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nconst { data: actions } = await client.promotions.actions.list('promo_UkLWZg9DAJ')"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'200':
description: actions found
content:
application/json:
example:
data:
- id: pact_UkLWZg9DAJ
created_at: '2026-06-12T17:25:08.639Z'
updated_at: '2026-06-12T17:25:08.639Z'
type: free_shipping
promotion_id: promo_UkLWZg9DAJ
preferences: {}
preference_schema: []
label: Free shipping
calculator: null
line_items: null
meta:
page: 1
limit: 25
count: 1
pages: 1
from: 1
to: 1
in: 1
previous: null
next: null
post:
summary: Create an action on a promotion
tags:
- Promotions
security:
- api_key: []
bearer_auth: []
description: 'Adds a new action to a promotion. The `type` is the wire shorthand from `GET /promotion_actions/types` (e.g. `free_shipping`, `create_item_adjustments`). Fully-qualified Ruby class names are also accepted for backward compatibility.
**Required scope:** `write_promotions` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nconst action = await client.promotions.actions.create('promo_UkLWZg9DAJ', {\n type: 'free_shipping',\n})"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'201':
description: action created
content:
application/json:
example:
id: pact_UkLWZg9DAJ
created_at: '2026-06-12T17:25:09.232Z'
updated_at: '2026-06-12T17:25:09.232Z'
type: free_shipping
promotion_id: promo_UkLWZg9DAJ
preferences: {}
preference_schema: []
label: Free shipping
calculator: null
line_items: null
'422':
description: unknown action type
content:
application/json:
example:
error:
code: unknown_promotion_action_type
message: Unknown type
requestBody:
content:
application/json:
schema:
type: object
properties:
type:
type: string
example: free_shipping
preferences:
type: object
additionalProperties: true
required:
- type
/api/v3/admin/promotions/{promotion_id}/promotion_actions/{id}:
parameters:
- name: promotion_id
in: path
required: true
schema:
type: string
- name: id
in: path
required: true
schema:
type: string
delete:
summary: Delete an action from a promotion
tags:
- Promotions
security:
- api_key: []
bearer_auth: []
description: '**Required scope:** `write_promotions` (for API-key authentication).'
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'204':
description: action deleted
/api/v3/admin/promotions/{promotion_id}/promotion_rules:
parameters:
- name: promotion_id
in: path
required: true
schema:
type: string
get:
summary: List rules for a promotion
tags:
- Promotions
security:
- api_key: []
bearer_auth: []
description: '**Required scope:** `read_promotions` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nconst { data: rules } = await client.promotions.rules.list('promo_UkLWZg9DAJ')"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'200':
description: rules found
content:
application/json:
example:
data:
- id: prorule_UkLWZg9DAJ
created_at: '2026-06-12T17:25:09.944Z'
updated_at: '2026-06-12T17:25:09.944Z'
type: currency
promotion_id: promo_UkLWZg9DAJ
preferences:
currency: USD
preference_schema:
- key: currency
type: string
default: null
label: Currency
product_ids: null
category_ids: null
customer_ids: null
meta:
page: 1
limit: 25
count: 1
pages: 1
from: 1
to: 1
in: 1
previous: null
next: null
post:
summary: Create a rule on a promotion
tags:
- Promotions
security:
- api_key: []
bearer_auth: []
description: 'Adds a new rule to a promotion. The `type` is the wire shorthand from `GET /promotion_rules/types` (e.g. `currency`, `item_total`, `product`). Fully-qualified Ruby class names are also accepted for backward compatibility.
**Required scope:** `write_promotions` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nconst rule = await client.promotions.rules.create('promo_UkLWZg9DAJ', {\n type: 'currency',\n preferences: { currency: 'EUR' },\n})"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'201':
description: rule created with preferences
content:
application/json:
example:
id: prorule_UkLWZg9DAJ
created_at: '2026-06-12T17:25:10.546Z'
updated_at: '2026-06-12T17:25:10.546Z'
type: currency
promotion_id: promo_UkLWZg9DAJ
preferences:
currency: EUR
preference_schema:
- key: currency
type: string
default: null
label: Currency
product_ids: null
category_ids: null
customer_ids: null
'422':
description: unknown rule type
content:
application/json:
example:
error:
code: unknown_promotion_rule_type
message: Unknown type
requestBody:
content:
application/json:
schema:
type: object
properties:
type:
type: string
example: currency
preferences:
type: object
additionalProperties: true
required:
- type
/api/v3/admin/promotions/{promotion_id}/promotion_rules/{id}:
parameters:
- name: promotion_id
in: path
required: true
schema:
type: string
- name: id
in: path
required: true
schema:
type: string
patch:
summary: Update a rule's preferences
tags:
- Promotions
security:
- api_key: []
bearer_auth: []
description: '**Required scope:** `write_promotions` (for API-key authentication).'
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'200':
description: rule updated
content:
application/json:
example:
id: prorule_UkLWZg9DAJ
created_at: '2026-06-12T17:25:10.853Z'
updated_at: '2026-06-12T17:25:11.135Z'
type: currency
promotion_id: promo_UkLWZg9DAJ
preferences:
currency: GBP
preference_schema:
- key: currency
type: string
default: null
label: Currency
product_ids: null
category_ids: null
customer_ids: null
requestBody:
content:
application/json:
schema:
type: object
properties:
preferences:
type: object
additionalProperties: true
delete:
summary: Delete a rule from a promotion
tags:
- Promotions
security:
- api_key: []
bearer_auth: []
description: '**Required scope:** `write_promotions` (for API-key authentication).'
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'204':
description: rule deleted
/api/v3/admin/promotions:
get:
summary: List promotions
tags:
- Promotions
security:
- api_key: []
bearer_auth: []
description: 'Returns the store''s promotions, including manual coupon and automatic promotions.
**Required scope:** `read_promotions` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nconst { data: promotions } = await client.promotions.list()"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'200':
description: promotions found
content:
application/json:
example:
data:
- id: promo_UkLWZg9DAJ
name: Summer Sale
description: null
code: summer
starts_at: '2026-06-12T17:25:11.475Z'
expires_at: null
usage_limit: null
match_policy: all
path: null
kind: coupon_code
multi_codes: false
number_of_codes: null
code_prefix: null
promotion_category_id: null
metadata: {}
created_at: '2026-06-12T17:25:11.477Z'
updated_at: '2026-06-12T17:25:11.479Z'
action_ids: []
rule_ids: []
meta:
page: 1
limit: 25
count: 1
pages: 1
from: 1
to: 1
in: 1
previous: null
next: null
post:
summary: Create a promotion
tags:
- Promotions
security:
- api_key: []
bearer_auth: []
description: 'Creates a new promotion. `code` is required for single-code coupon promotions;
pass `multi_codes: true` with `number_of_codes` to auto-generate a batch.
Rules and actions can be created in the same request by passing arrays of
`{ type, preferences, ... }` rows. The server reconciles to the desired set:
new rows are built, existing rows (by `id`) are updated, omitted rows are removed.
**Required scope:** `write_promotions` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\n// Single coupon code, no rules/actions\nconst promotion = await client.promotions.create({\n name: 'Black Friday',\n code: 'BLACKFRIDAY',\n starts_at: '2026-11-29T00:00:00Z',\n expires_at: '2026-12-01T00:00:00Z',\n})\n\n// One-shot: promotion + rules + actions in a single request\nconst blackFriday = await client.promotions.create({\n name: 'Black Friday',\n code: 'BLACKFRIDAY',\n kind: 'coupon_code',\n starts_at: '2026-11-29T00:00:00Z',\n expires_at: '2026-12-01T00:00:00Z',\n match_policy: 'all',\n rules: [\n {\n type: 'currency',\n preferences: { currency: 'USD' },\n },\n {\n type: 'item_total',\n preferences: { amount_min: 100, operator_min: 'gte' },\n },\n {\n type: 'product',\n preferences: { match_policy: 'any' },\n product_ids: ['prod_abc123', 'prod_def456'],\n },\n ],\n actions: [\n {\n type: 'create_item_adjustments',\n calculator: {\n type: 'percent_on_line_item',\n preferences: { percent: 25 },\n },\n },\n { type: 'free_shipping' },\n ],\n})"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'201':
description: promotion created with rules and actions
content:
application/json:
example:
id: promo_gbHJdmfrXB
name: Black Friday
description: null
code: blackfriday-os
starts_at: '2026-06-12T17:25:12.469Z'
expires_at: null
usage_limit: null
match_policy: all
path: null
kind: coupon_code
multi_codes: false
number_of_codes: null
code_prefix: null
promotion_category_id: null
metadata: {}
created_at: '2026-06-12T17:25:12.471Z'
updated_at: '2026-06-12T17:25:12.495Z'
action_ids:
- pact_UkLWZg9DAJ
- pact_gbHJdmfrXB
rule_ids:
- prorule_UkLWZg9DAJ
- prorule_gbHJdmfrXB
- prorule_EfhxLZ9ck8
'422':
description: invalid params
content:
application/json:
example:
error:
code: validation_error
message: Name can't be blank and Code can't be blank
details:
name:
- can't be blank
code:
- can't be blank
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
type: string
code:
type: string
nullable: true
description:
type: string
nullable: true
starts_at:
type: string
format: date-time
nullable: true
expires_at:
type: string
format: date-time
nullable: true
usage_limit:
type: integer
nullable: true
match_policy:
type: string
enum:
- all
- any
kind:
type: string
enum:
- coupon_code
- automatic
multi_codes:
type: boolean
number_of_codes:
type: integer
nullable: true
code_prefix:
type: string
nullable: true
promotion_category_id:
type: string
nullable: true
rules:
type: array
items:
type: object
properties:
type:
type: string
example: currency
preferences:
type: object
additionalProperties: true
product_ids:
type: array
items:
type: string
category_ids:
type: array
items:
type: string
customer_ids:
type: array
items:
type: string
required:
- type
actions:
type: array
items:
type: object
properties:
type:
type: string
example: free_shipping
preferences:
type: object
additionalProperties: true
calculator:
type: object
properties:
type:
type: string
example: flat_percent_item_total
preferences:
type: object
additionalProperties: true
required:
- type
required:
- name
/api/v3/admin/promotions/{id}:
get:
summary: Show a promotion
tags:
- Promotions
security:
- api_key: []
bearer_auth: []
description: '**Required scope:** `read_promotions` (for API-key authentication).'
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: promotion found
content:
application/json:
example:
id: promo_UkLWZg9DAJ
name: Summer Sale
description: null
code: summer
starts_at: '2026-06-12T17:25:12.804Z'
expires_at: null
usage_limit: null
match_policy: all
path: null
kind: coupon_code
multi_codes: false
number_of_codes: null
code_prefix: null
promotion_category_id: null
metadata: {}
created_at: '2026-06-12T17:25:12.805Z'
updated_at: '2026-06-12T17:25:12.806Z'
action_ids: []
rule_ids: []
patch:
summary: Update a promotion
tags:
- Promotions
security:
- api_key: []
bearer_auth: []
description: 'Updates a promotion. The `rules` and `actions` arrays are treated as
a *desired set* — rows with `id` update in place, rows without `id` are
built fresh, and any existing row not present in the array is destroyed.
Pass `rules: []` or `actions: []` to clear them.
**Required scope:** `write_promotions` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\n// Basic field update\nawait client.promotions.update(promotionId, {\n description: 'Updated description',\n})\n\n// One-shot: rewrite rules + actions in a single request\nawait client.promotions.update(promotionId, {\n name: 'Holiday Sale',\n rules: [\n // Update an existing rule by id — `preferences` overwrite the prior set\n {\n id: 'prorule_existing',\n type: 'currency',\n preferences: { currency: 'EUR' },\n },\n // Add a new rule\n {\n type: 'item_total',\n preferences: { amount_min: 50, operator_min: 'gte' },\n },\n ],\n actions: [\n // Swap calculator type on an existing action\n {\n id: 'proaction_existing',\n type: 'create_item_adjustments',\n calculator: {\n type: 'percent_on_line_item',\n preferences: { percent: 15 },\n },\n },\n ],\n})\n\n// Remove all rules/actions\nawait client.promotions.update(promotionId, {\n rules: [],\n actions: [],\n})"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: rules and actions cleared with empty arrays
content:
application/json:
example:
id: promo_UkLWZg9DAJ
name: Summer Sale
description: null
code: summer
starts_at: '2026-06-12T17:25:13.717Z'
expires_at: null
usage_limit: null
match_policy: all
path: null
kind: coupon_code
multi_codes: false
number_of_codes: null
code_prefix: null
promotion_category_id: null
metadata: {}
created_at: '2026-06-12T17:25:13.718Z'
updated_at: '2026-06-12T17:25:14.003Z'
action_ids: []
rule_ids: []
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
type: string
description:
type: string
nullable: true
code:
type: string
nullable: true
starts_at:
type: string
format: date-time
nullable: true
expires_at:
type: string
format: date-time
nullable: true
usage_limit:
type: integer
nullable: true
match_policy:
type: string
enum:
- all
- any
kind:
type: string
enum:
- coupon_code
- automatic
promotion_category_id:
type: string
nullable: true
rules:
type: array
items:
type: object
properties:
id:
type: string
nullable: true
description: Pass to update an existing rule; omit to build a new one
type:
type: string
example: currency
preferences:
type: object
additionalProperties: true
product_ids:
type: array
items:
type: string
category_ids:
type: array
items:
type: string
customer_ids:
type: array
items:
type: string
required:
- type
actions:
type: array
items:
type: object
properties:
id:
type: string
nullable: true
type:
type: string
example: free_shipping
preferences:
type: object
additionalProperties: true
calculator:
type: object
properties:
type:
type: string
example: flat_percent_item_total
preferences:
type: object
additionalProperties: true
required:
- type
delete:
summary: Delete a promotion
tags:
- Promotions
security:
- api_key: []
bearer_auth: []
description: '**Required scope:** `write_promotions` (for API-key authentication).'
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
- name: id
in: path
required: true
schema:
type: string
responses:
'204':
description: promotion deleted
/api/v3/admin/promotion_actions/types:
get:
summary: List available promotion action types
tags:
- Promotions
security:
- api_key: []
bearer_auth: []
description: 'Returns the registered Spree::PromotionAction subclasses with their preference schemas. Used by admin UIs to populate the "Add action" picker and render generic preference forms.
**Required scope:** `read_promotions` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nconst { data: actionTypes } = await client.promotionActions.types()"
parameters:
- name: x-
# --- truncated at 32 KB (69 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/spree/refs/heads/main/openapi/spree-promotions-api-openapi.yml