Discounts API
Every Discounts action (12) and data model (10): endpoints, scopes, input, preconditions, events and examples.
Promotions and discount codes — sales, order deals and free gifts.
Module code discounts · 12 actions · 10 data models · 13 events. A key or token never does more here than the person behind it can; row-level security applies exactly as in the app. Scopes: discounts (everything), discounts:read (read only) or discounts:<capability>.
Resources
| Resource | Table | List and read | POST | PATCH | DELETE |
|---|---|---|---|---|---|
promotion_rule_channels | promotion_rule_channel | /api/v1/promotion_rule_channels | |||
promotion_rule_gifts | promotion_rule_gift | /api/v1/promotion_rule_gifts | |||
promotion_rule_targets | promotion_rule_target | /api/v1/promotion_rule_targets | |||
promotion_rules | promotion_rule | /api/v1/promotion_rules | discounts.save_promotion_rule | discounts.delete_promotion_rule | |
promotions | promotion | /api/v1/promotions | discounts.save_promotion | discounts.delete_promotion | |
voucher_catalogues | voucher_catalogue | /api/v1/voucher_catalogues | |||
voucher_channel_listings | voucher_channel_listing | /api/v1/voucher_channel_listings | |||
voucher_codes | voucher_code | /api/v1/voucher_codes | |||
voucher_customers | voucher_customer | /api/v1/voucher_customers | |||
vouchers | voucher | /api/v1/vouchers | discounts.save_voucher | discounts.delete_voucher |
Lists take limit (up to 200), cursor, order (e.g. -created_at), fields and q; every other query parameter is a column filter: ?status=paid, ?total[gte]=100, ?id[in]=a,b, ?x=null.
Actions
| Action | Needs | What it does |
|---|---|---|
discounts.add_voucher_codes | discounts:write | Add voucher codes |
discounts.announce_promotion | discounts:write | Announce a promotion starting or ending |
discounts.delete_promotion | discounts:delete | Delete a promotion |
discounts.delete_promotion_rule | discounts:write | Delete a promotion rule |
discounts.delete_voucher | discounts:delete | Delete a voucher |
discounts.delete_voucher_codes | discounts:delete | Remove voucher codes |
discounts.import_voucher_codes | discounts:write | Import voucher codes |
discounts.save_promotion | discounts:write | Save a promotion |
discounts.save_promotion_rule | discounts:write | Save a promotion rule |
discounts.save_voucher | discounts:write | Save a voucher |
discounts.set_voucher_catalogue | discounts:write | Limit a voucher to part of the catalogue |
discounts.set_voucher_channels | discounts:write | Set a voucher’s value per channel |
discounts.add_voucher_codes
Add voucher codes — Type codes, or generate up to fifty with an optional prefix.
Needs discounts:write · changes data
https://app.getera.io/api/v1/voucher_codes/actions/add_voucher_codes- Canonical address:
POST /api/v1/actions/discounts.add_voucher_codes
Input
| Field | Type | Required | Notes |
|---|---|---|---|
voucherId | uuid | yes | Voucher · id of voucher |
codes | object / array | Codes | |
quantity | number | How many to generate · min 0 · max 50 | |
prefix | string | Prefix · max 40 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The voucher must exist. |
no_codes | At least one code must be typed or generated. |
too_many_codes | At most fifty at a time. |
duplicate_codes | A code may be given once. |
code_taken | A code must be free in this workspace, ignoring case. |
Events it publishes: discounts.voucher_codes_added
What it writes: voucher_code (insert) — Writes the codes
curl -X POST https://app.getera.io/api/v1/voucher_codes/actions/add_voucher_codes \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"voucherId":"VOUCHER_ID"}'{
"ok": true,
"result": "…",
"events": [
"discounts.voucher_codes_added"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.voucherCodes.addVoucherCodes({
voucherId: "VOUCHER_ID"
})era voucher_codes add_voucher_codes \
--voucher-id "VOUCHER_ID"{
"name": "era__discounts__add_voucher_codes",
"arguments": {
"voucherId": "VOUCHER_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold discounts:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, no_codes, too_many_codes, duplicate_codes, code_taken · 429 slow down
discounts.announce_promotion
Announce a promotion starting or ending — Machine: the shop sweep announces a start or an end once, as Saleor’s scheduled task does.
Needs discounts:write · reaches outside (sends, publishes or charges)
https://app.getera.io/api/v1/promotions/{id}/actions/announce_promotion- Canonical address:
POST /api/v1/actions/discounts.announce_promotion
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Promotion · id of promotion |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The promotion must exist. |
nothing_to_announce | A start or an end must have passed since the last announcement. |
Events it publishes: discounts.promotion_started, discounts.promotion_ended
What it writes: promotion (update) — Stamps when it was announced
curl -X POST https://app.getera.io/api/v1/promotions/PROMOTION_ID/actions/announce_promotion \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"discounts.promotion_started",
"discounts.promotion_ended"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.promotions.announcePromotion("PROMOTION_ID")era promotions announce_promotion PROMOTION_ID{
"name": "era__discounts__announce_promotion",
"arguments": {
"id": "PROMOTION_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold discounts:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, nothing_to_announce · 429 slow down
discounts.delete_promotion
Delete a promotion — Its rules go with it. Orders already sold keep what they were charged.
Needs discounts:delete · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/promotions/{id}/actions/delete_promotion- REST alias:
DELETE /api/v1/promotions/{id} - Canonical address:
POST /api/v1/actions/discounts.delete_promotion
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Promotion · id of promotion |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The promotion must exist. |
Events it publishes: discounts.promotion_deleted
What it writes: promotion (delete) — Removes the promotion and its rules
curl -X POST https://app.getera.io/api/v1/promotions/PROMOTION_ID/actions/delete_promotion \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"discounts.promotion_deleted"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.promotions.deletePromotion("PROMOTION_ID")era promotions delete_promotion PROMOTION_ID{
"name": "era__discounts__delete_promotion",
"arguments": {
"id": "PROMOTION_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold discounts:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found · 429 slow down
discounts.delete_promotion_rule
Delete a promotion rule — The prices it painted go back to normal.
Needs discounts:write · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/promotion_rules/{id}/actions/delete_promotion_rule- REST alias:
DELETE /api/v1/promotion_rules/{id} - Canonical address:
POST /api/v1/actions/discounts.delete_promotion_rule
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Rule · id of promotion_rule |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The rule must exist. |
Events it publishes: discounts.rule_deleted
What it writes: promotion_rule (delete) — Removes the rule
curl -X POST https://app.getera.io/api/v1/promotion_rules/PROMOTION_RULE_ID/actions/delete_promotion_rule \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"discounts.rule_deleted"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.promotionRules.deletePromotionRule("PROMOTION_RULE_ID")era promotion_rules delete_promotion_rule PROMOTION_RULE_ID{
"name": "era__discounts__delete_promotion_rule",
"arguments": {
"id": "PROMOTION_RULE_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold discounts:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found · 429 slow down
discounts.delete_voucher
Delete a voucher — Its codes stop working. Orders keep the code text they were sold with.
Needs discounts:delete · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/vouchers/{id}/actions/delete_voucher- REST alias:
DELETE /api/v1/vouchers/{id} - Canonical address:
POST /api/v1/actions/discounts.delete_voucher
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Voucher · id of voucher |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The voucher must exist. |
Events it publishes: discounts.voucher_deleted
What it writes: voucher (delete) — Removes the voucher and its codes
curl -X POST https://app.getera.io/api/v1/vouchers/VOUCHER_ID/actions/delete_voucher \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"discounts.voucher_deleted"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.vouchers.deleteVoucher("VOUCHER_ID")era vouchers delete_voucher VOUCHER_ID{
"name": "era__discounts__delete_voucher",
"arguments": {
"id": "VOUCHER_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold discounts:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found · 429 slow down
discounts.delete_voucher_codes
Remove voucher codes — Used ones too, as in Saleor — the result says how many had been used.
Needs discounts:delete · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/voucher_codes/actions/delete_voucher_codes- Canonical address:
POST /api/v1/actions/discounts.delete_voucher_codes
Input
| Field | Type | Required | Notes |
|---|---|---|---|
voucherId | uuid | yes | Voucher · id of voucher |
codeIds | object / array | yes | Codes |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The voucher must exist. |
Events it publishes: discounts.voucher_codes_deleted
What it writes: voucher_code (delete) — Removes the codes
curl -X POST https://app.getera.io/api/v1/voucher_codes/actions/delete_voucher_codes \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"voucherId":"VOUCHER_ID","codeIds":{}}'{
"ok": true,
"result": "…",
"events": [
"discounts.voucher_codes_deleted"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.voucherCodes.deleteVoucherCodes({
voucherId: "VOUCHER_ID",
codeIds: {}
})era voucher_codes delete_voucher_codes --data '{"voucherId":"VOUCHER_ID","codeIds":{}}'{
"name": "era__discounts__delete_voucher_codes",
"arguments": {
"voucherId": "VOUCHER_ID",
"codeIds": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold discounts:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found · 429 slow down
discounts.import_voucher_codes
Import voucher codes — Codes from a CSV file — the first column, a header optional. All or none.
Needs discounts:write · changes data
https://app.getera.io/api/v1/voucher_codes/actions/import_voucher_codes- Canonical address:
POST /api/v1/actions/discounts.import_voucher_codes
Input
| Field | Type | Required | Notes |
|---|---|---|---|
voucherId | uuid | yes | Voucher · id of voucher |
codes | object / array | yes | Codes |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The voucher must exist. |
no_codes | The file has at least one code. |
import_too_large | At most five thousand codes per file. |
duplicate_codes | A code may be given once. |
code_taken | A code must be free in this workspace, ignoring case. |
Events it publishes: discounts.voucher_codes_added
What it writes: voucher_code (insert) — Writes the codes
curl -X POST https://app.getera.io/api/v1/voucher_codes/actions/import_voucher_codes \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"voucherId":"VOUCHER_ID","codes":{}}'{
"ok": true,
"result": "…",
"events": [
"discounts.voucher_codes_added"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.voucherCodes.importVoucherCodes({
voucherId: "VOUCHER_ID",
codes: {}
})era voucher_codes import_voucher_codes --data '{"voucherId":"VOUCHER_ID","codes":{}}'{
"name": "era__discounts__import_voucher_codes",
"arguments": {
"voucherId": "VOUCHER_ID",
"codes": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold discounts:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, no_codes, import_too_large, duplicate_codes, code_taken · 429 slow down
discounts.save_promotion
Save a promotion — A catalogue sale that paints prices, or an order deal on the basket total.
Needs discounts:write · changes data
https://app.getera.io/api/v1/promotions/{id}/actions/save_promotion- REST alias:
PATCH /api/v1/promotions/{id} - Canonical address:
POST /api/v1/actions/discounts.save_promotion
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | Filled from the path {id} · Promotion · id of promotion | |
name | string | yes | Name · max 255 |
type | string (enum) | Type · Allowed values: catalogue, order | |
description | string | Description · max 4000 | |
startDate | date-time | Starts | |
endDate | date-time | Ends |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
invalid_name | A promotion needs a name. |
not_found | An edited promotion must exist. |
end_before_start | It must end after it starts. |
Events it publishes: discounts.promotion_created, discounts.promotion_updated
What it writes: promotion (upsert) — Creates or edits the promotion
curl -X POST https://app.getera.io/api/v1/promotions/PROMOTION_ID/actions/save_promotion \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"name":"Name"}'{
"ok": true,
"result": "…",
"events": [
"discounts.promotion_created",
"discounts.promotion_updated"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.promotions.savePromotion("PROMOTION_ID", {
name: "Name"
})era promotions save_promotion PROMOTION_ID \
--name "Name"{
"name": "era__discounts__save_promotion",
"arguments": {
"id": "PROMOTION_ID",
"name": "Name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold discounts:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 invalid_name, not_found, end_before_start · 429 slow down
discounts.save_promotion_rule
Save a promotion rule — What it covers, in which channels, and what it gives.
Needs discounts:write · changes data
https://app.getera.io/api/v1/promotion_rules/{id}/actions/save_promotion_rule- REST alias:
PATCH /api/v1/promotion_rules/{id} - Canonical address:
POST /api/v1/actions/discounts.save_promotion_rule
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | Filled from the path {id} · Rule · id of promotion_rule | |
promotionId | uuid | Promotion · id of promotion | |
name | string | Name · max 255 | |
description | string | Description · max 2000 | |
channelIds | object / array | Channels | |
rewardValueType | string (enum) | Off by · Allowed values: percentage, fixed | |
rewardValue | number | Value · min 0 | |
rewardType | string (enum) | Reward · Allowed values: subtotal_discount, gift | |
targets | object / array | Covers | |
cataloguePredicate | object / array | Covers (AND / OR tree) | |
orderPredicate | object / array | Order condition | |
giftVariantIds | object / array | Gifts |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The promotion and an edited rule must exist. |
channel_not_found | Every channel must exist. |
predicate_required | A catalogue rule covers something; an order rule has a total condition. |
predicate_not_allowed | A rule carries only its own promotion type’s condition. |
invalid_range | An order total range (minor units) is positive and runs low to high. |
invalid_predicate | A condition tree is Saleor-shaped: AND / OR groups alone at their level, known leaves, ids and amounts. |
reward_type_not_allowed | A catalogue rule has no reward type. |
reward_type_required | An order rule says money off or gift. |
mixed_currencies | Amounts and order-total conditions need channels in one currency. |
gift_rule_invalid | A gift rule has gifts and no value. |
reward_value_required | A discount rule has a value above zero and its kind. |
channels_required | An amount off needs at least one channel. |
invalid_percentage | A percentage is at most 100. |
Events it publishes: discounts.rule_created, discounts.rule_updated
What it writes: promotion_rule (upsert) — Creates or edits the rule; promotion_rule_channel (insert) — Replaces its channels; promotion_rule_target (insert) — Replaces what it covers; promotion_rule_gift (insert) — Replaces its gifts
curl -X POST https://app.getera.io/api/v1/promotion_rules/PROMOTION_RULE_ID/actions/save_promotion_rule \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"promotionId":"PROMOTION_ID"}'{
"ok": true,
"result": "…",
"events": [
"discounts.rule_created",
"discounts.rule_updated"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.promotionRules.savePromotionRule("PROMOTION_RULE_ID", {
promotionId: "PROMOTION_ID"
})era promotion_rules save_promotion_rule PROMOTION_RULE_ID \
--promotion-id "PROMOTION_ID"{
"name": "era__discounts__save_promotion_rule",
"arguments": {
"id": "PROMOTION_RULE_ID",
"promotionId": "PROMOTION_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold discounts:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, channel_not_found, predicate_required, predicate_not_allowed, invalid_range, invalid_predicate, reward_type_not_allowed, reward_type_required, mixed_currencies, gift_rule_invalid, reward_value_required, channels_required, invalid_percentage · 429 slow down
discounts.save_voucher
Save a voucher — A code customers type — its reach, its limits, its dates.
Needs discounts:write · changes data
https://app.getera.io/api/v1/vouchers/{id}/actions/save_voucher- REST alias:
PATCH /api/v1/vouchers/{id} - Canonical address:
POST /api/v1/actions/discounts.save_voucher
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | Filled from the path {id} · Voucher · id of voucher | |
name | string | Name · max 255 | |
type | string (enum) | Applies to · Allowed values: entire_order, shipping, specific_product | |
discountValueType | string (enum) | Off by · Allowed values: fixed, percentage | |
usageLimit | number | Usage limit · min 1 | |
startDate | date-time | Starts | |
endDate | date-time | Ends | |
applyOncePerOrder | boolean | Once per order | |
applyOncePerCustomer | boolean | Once per customer | |
singleUse | boolean | Each code once | |
onlyForStaff | boolean | Staff only | |
minCheckoutItemsQuantity | number | Minimum items · min 0 | |
countries | object / array | Countries | |
codes | object / array | Codes |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | An edited voucher must exist. |
end_before_start | It must end after it starts. |
invalid_percentage | A switch to percent needs every channel value at most 100. |
duplicate_codes | A code may be given once. |
code_taken | A code must be free in this workspace, ignoring case. |
Events it publishes: discounts.voucher_created, discounts.voucher_updated
What it writes: voucher (upsert) — Creates or edits the voucher; voucher_code (insert) — Writes its first codes on create
curl -X POST https://app.getera.io/api/v1/vouchers/VOUCHER_ID/actions/save_voucher \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"name":"Name"}'{
"ok": true,
"result": "…",
"events": [
"discounts.voucher_created",
"discounts.voucher_updated"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.vouchers.saveVoucher("VOUCHER_ID", {
name: "Name"
})era vouchers save_voucher VOUCHER_ID \
--name "Name"{
"name": "era__discounts__save_voucher",
"arguments": {
"id": "VOUCHER_ID",
"name": "Name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold discounts:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, end_before_start, invalid_percentage, duplicate_codes, code_taken · 429 slow down
discounts.set_voucher_catalogue
Limit a voucher to part of the catalogue — The products, variants, categories and collections a specific-products voucher covers.
Needs discounts:write · changes data
https://app.getera.io/api/v1/voucher_catalogues/actions/set_voucher_catalogue- Canonical address:
POST /api/v1/actions/discounts.set_voucher_catalogue
Input
| Field | Type | Required | Notes |
|---|---|---|---|
voucherId | uuid | yes | Voucher · id of voucher |
itemIds | object / array | Products | |
variantIds | object / array | Variants | |
categoryIds | object / array | Categories | |
collectionIds | object / array | Collections |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The voucher must exist. |
catalogue_not_allowed | Only a specific-products voucher has a catalogue. |
Events it publishes: discounts.voucher_updated
What it writes: voucher_catalogue (insert) — Replaces what it covers
curl -X POST https://app.getera.io/api/v1/voucher_catalogues/actions/set_voucher_catalogue \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"voucherId":"VOUCHER_ID"}'{
"ok": true,
"result": "…",
"events": [
"discounts.voucher_updated"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.voucherCatalogues.setVoucherCatalogue({
voucherId: "VOUCHER_ID"
})era voucher_catalogues set_voucher_catalogue \
--voucher-id "VOUCHER_ID"{
"name": "era__discounts__set_voucher_catalogue",
"arguments": {
"voucherId": "VOUCHER_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold discounts:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, catalogue_not_allowed · 429 slow down
discounts.set_voucher_channels
Set a voucher’s value per channel — How much it takes off in each channel, and the minimum spend there.
Needs discounts:write · changes data
https://app.getera.io/api/v1/voucher_channel_listings/actions/set_voucher_channels- Canonical address:
POST /api/v1/actions/discounts.set_voucher_channels
Input
| Field | Type | Required | Notes |
|---|---|---|---|
voucherId | uuid | yes | Voucher · id of voucher |
listings | object / array | yes | Channels |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The voucher must exist. |
channel_not_found | Every channel must exist. |
value_required | Every channel needs a value above zero. |
invalid_percentage | A percentage is at most 100. |
Events it publishes: discounts.voucher_updated
What it writes: voucher_channel_listing (upsert) — Replaces the per-channel values
curl -X POST https://app.getera.io/api/v1/voucher_channel_listings/actions/set_voucher_channels \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"voucherId":"VOUCHER_ID","listings":{}}'{
"ok": true,
"result": "…",
"events": [
"discounts.voucher_updated"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.voucherChannelListings.setVoucherChannels({
voucherId: "VOUCHER_ID",
listings: {}
})era voucher_channel_listings set_voucher_channels --data '{"voucherId":"VOUCHER_ID","listings":{}}'{
"name": "era__discounts__set_voucher_channels",
"arguments": {
"voucherId": "VOUCHER_ID",
"listings": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold discounts:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, channel_not_found, value_required, invalid_percentage · 429 slow down
Data models
PromotionRuleChannel
Table promotion_rule_channel · Resource promotion_rule_channels
GET /api/v1/promotion_rule_channelsGET /api/v1/promotion_rule_channels/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
rule_id | string (uuid) · uuid | promotion_rule | ||
channel_id | string (uuid) · uuid | channel |
Primary key: id
Unique: rule_id, channel_id
PromotionRuleGift
Table promotion_rule_gift · Resource promotion_rule_gifts
GET /api/v1/promotion_rule_giftsGET /api/v1/promotion_rule_gifts/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
rule_id | string (uuid) · uuid | promotion_rule | ||
item_variant_id | string (uuid) · uuid | item_variant |
Primary key: id
Unique: rule_id, item_variant_id
PromotionRuleTarget
Table promotion_rule_target · Resource promotion_rule_targets
GET /api/v1/promotion_rule_targetsGET /api/v1/promotion_rule_targets/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
rule_id | string (uuid) · uuid | promotion_rule | ||
item_id | string (uuid) · uuid | yes | item | |
item_variant_id | string (uuid) · uuid | yes | item_variant | |
category_id | string (uuid) · uuid | yes | item_category | |
collection_id | string (uuid) · uuid | yes | item_collection |
Primary key: id
Checks:
CHECK ((num_nonnulls(item_id, item_variant_id, category_id, collection_id) = 1))
PromotionRule
Table promotion_rule · Resource promotion_rules
GET /api/v1/promotion_rulesGET /api/v1/promotion_rules/{id}PATCH /api/v1/promotion_rules/{id}→discounts.save_promotion_ruleDELETE /api/v1/promotion_rules/{id}→discounts.delete_promotion_rule
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
promotion_id | string (uuid) · uuid | promotion | ||
name | string · text | yes | ||
description | string · text | yes | ||
reward_value_type | string · text | yes | fixed, percentage | |
reward_value | string (decimal) · numeric | yes | ||
reward_type | string · text | yes | subtotal_discount, gift | |
order_predicate | object · jsonb | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz | |||
catalogue_predicate | object · jsonb |
Primary key: id
Checks:
CHECK (((reward_value_type <> 'percentage'::text) OR (reward_value IS NULL) OR (reward_value <= (100)::numeric)))CHECK (((reward_value IS NULL) OR (reward_value >= (0)::numeric)))
Promotion
Table promotion · Resource promotions
GET /api/v1/promotionsGET /api/v1/promotions/{id}PATCH /api/v1/promotions/{id}→discounts.save_promotionDELETE /api/v1/promotions/{id}→discounts.delete_promotion
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
name | string · text | |||
description | string · text | yes | ||
type | string · text | catalogue, order | ||
start_date | string (date-time) · timestamptz | |||
end_date | string (date-time) · timestamptz | yes | ||
metadata | object · jsonb | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz | |||
last_notification_scheduled_at | string (date-time) · timestamptz | yes |
Primary key: id
Checks:
CHECK (((end_date IS NULL) OR (end_date > start_date)))
VoucherCatalogue
Table voucher_catalogue · Resource voucher_catalogues
GET /api/v1/voucher_cataloguesGET /api/v1/voucher_catalogues/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
voucher_id | string (uuid) · uuid | voucher | ||
item_id | string (uuid) · uuid | yes | item | |
item_variant_id | string (uuid) · uuid | yes | item_variant | |
category_id | string (uuid) · uuid | yes | item_category | |
collection_id | string (uuid) · uuid | yes | item_collection |
Primary key: id
Checks:
CHECK ((num_nonnulls(item_id, item_variant_id, category_id, collection_id) = 1))
VoucherChannelListing
Table voucher_channel_listing · Resource voucher_channel_listings
GET /api/v1/voucher_channel_listingsGET /api/v1/voucher_channel_listings/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
voucher_id | string (uuid) · uuid | voucher | ||
channel_id | string (uuid) · uuid | channel | ||
discount_value | string (decimal) · numeric | |||
currency | string · bpchar | max 3 | ||
min_spent_amount | string (int64) · int8 | yes |
Primary key: id
Unique: voucher_id, channel_id
Checks:
CHECK ((currency ~ '^[A-Z]{3}$'::text))CHECK ((discount_value > (0)::numeric))CHECK (((min_spent_amount IS NULL) OR (min_spent_amount >= 0)))
VoucherCode
Table voucher_code · Resource voucher_codes
GET /api/v1/voucher_codesGET /api/v1/voucher_codes/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
voucher_id | string (uuid) · uuid | voucher | ||
code | string · text | |||
used | integer · int4 | |||
is_active | boolean · bool | |||
created_at | string (date-time) · timestamptz |
Primary key: id
Checks:
CHECK (((length(code) >= 1) AND (length(code) <= 255)))CHECK ((used >= 0))
VoucherCustomer
Table voucher_customer · Resource voucher_customers
GET /api/v1/voucher_customersGET /api/v1/voucher_customers/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
voucher_code_id | string (uuid) · uuid | voucher_code | ||
customer_email | string · text | yes | ||
order_id | string (uuid) · uuid | |||
created_at | string (date-time) · timestamptz |
Primary key: id
Unique: voucher_code_id, order_id
Voucher
Table voucher · Resource vouchers
GET /api/v1/vouchersGET /api/v1/vouchers/{id}PATCH /api/v1/vouchers/{id}→discounts.save_voucherDELETE /api/v1/vouchers/{id}→discounts.delete_voucher
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
name | string · text | yes | ||
type | string · text | entire_order, shipping, specific_product | ||
discount_value_type | string · text | fixed, percentage | ||
usage_limit | integer · int4 | yes | ||
start_date | string (date-time) · timestamptz | |||
end_date | string (date-time) · timestamptz | yes | ||
apply_once_per_order | boolean · bool | |||
apply_once_per_customer | boolean · bool | |||
single_use | boolean · bool | |||
only_for_staff | boolean · bool | |||
min_checkout_items_quantity | integer · int4 | yes | ||
countries | string[] · bpchar[] | |||
metadata | object · jsonb | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Checks:
CHECK (((end_date IS NULL) OR (end_date > start_date)))CHECK (((min_checkout_items_quantity IS NULL) OR (min_checkout_items_quantity >= 0)))CHECK (((usage_limit IS NULL) OR (usage_limit >= 1)))
Events
Workflows and webhooks react to these events.
| Event | What happens | Payload |
|---|---|---|
discounts.promotion_created | A promotion was created. | id, type |
discounts.promotion_deleted | A promotion was removed. | id, type |
discounts.promotion_ended | A promotion’s end date passed. | id, type, endDate |
discounts.promotion_started | A promotion’s start date passed. | id, type, startDate |
discounts.promotion_updated | A promotion was edited. | id, type |
discounts.rule_created | A promotion rule was added. | id, promotionId, type, channelIds |
discounts.rule_deleted | A promotion rule was removed. | id, promotionId, type |
discounts.rule_updated | A promotion rule was edited. | id, promotionId, type, channelIds |
discounts.voucher_codes_added | Codes were added to a voucher. | voucherId, count |
discounts.voucher_codes_deleted | Codes were removed from a voucher. | voucherId, count, used |
discounts.voucher_created | A voucher was created. | id, codes |
discounts.voucher_deleted | A voucher was removed. | id |
discounts.voucher_updated | A voucher was edited. | id |