Shop API
Every Shop action (74) and data model (33): endpoints, scopes, input, preconditions, events and examples.
Sales channels, stock, shipping and orders — from basket to doorstep.
Module code retail · 74 actions · 33 data models · 56 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: retail (everything), retail:read (read only) or retail:<capability>.
Resources
| Resource | Table | List and read | POST | PATCH | DELETE |
|---|---|---|---|---|---|
allocations | allocation | /api/v1/allocations | |||
checkout_lines | checkout_line | /api/v1/checkout_lines | |||
checkouts | checkout | /api/v1/checkouts | retail.create_checkout | ||
collection_items | item_collection_item | /api/v1/collection_items | |||
collections | item_collection | /api/v1/collections | retail.save_collection | retail.delete_collection | |
fulfilment_lines | fulfilment_line | /api/v1/fulfilment_lines | |||
fulfilments | fulfilment | /api/v1/fulfilments | |||
item_collection_channel_listings | item_collection_channel_listing | /api/v1/item_collection_channel_listings | |||
order_discounts | sales_order_discount | /api/v1/order_discounts | |||
order_gift_cards | sales_order_gift_card | /api/v1/order_gift_cards | |||
order_granted_refund_lines | order_granted_refund_line | /api/v1/order_granted_refund_lines | |||
order_granted_refunds | order_granted_refund | /api/v1/order_granted_refunds | |||
order_lines | sales_order_line | /api/v1/order_lines | |||
order_return_lines | order_return_line | /api/v1/order_return_lines | |||
order_returns | order_return | /api/v1/order_returns | |||
orders | sales_order | /api/v1/orders | retail.create_draft_order | retail.update_order | retail.delete_draft_order |
preorder_allocations | preorder_allocation | /api/v1/preorder_allocations | |||
product_listings | item_channel_listing | /api/v1/product_listings | |||
sales_channel_warehouses | channel_warehouse | /api/v1/sales_channel_warehouses | |||
sales_channels | channel | /api/v1/sales_channels | retail.save_channel | retail.delete_channel | |
shipping_method_channel_listings | shipping_method_channel_listing | /api/v1/shipping_method_channel_listings | |||
shipping_method_excluded_items | shipping_method_excluded_item | /api/v1/shipping_method_excluded_items | |||
shipping_method_postal_code_rules | shipping_method_postal_code_rule | /api/v1/shipping_method_postal_code_rules | |||
shipping_methods | shipping_method | /api/v1/shipping_methods | retail.save_shipping_method | retail.delete_shipping_method | |
shipping_zone_channels | shipping_zone_channel | /api/v1/shipping_zone_channels | |||
shipping_zones | shipping_zone | /api/v1/shipping_zones | retail.save_shipping_zone | retail.delete_shipping_zone | |
stock_movements | stock_movement | /api/v1/stock_movements | |||
stock_reservations | stock_reservation | /api/v1/stock_reservations | |||
stocks | stock | /api/v1/stocks | |||
variant_listings | item_variant_channel_listing | /api/v1/variant_listings | |||
variant_preorders | variant_preorder | /api/v1/variant_preorders | |||
warehouse_shipping_zones | warehouse_shipping_zone | /api/v1/warehouse_shipping_zones | |||
warehouses | warehouse | /api/v1/warehouses | retail.create_warehouse | retail.update_warehouse | retail.delete_warehouse |
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 |
|---|---|---|
retail.adjust_stock | retail:write | Adjust stock |
retail.approve_fulfilment | retail:approve | Approve a despatch |
retail.attach_gift_card | retail:write | Pay with a gift card |
retail.bulk_update_prices | retail:write | Reprice in bulk |
retail.cancel_fulfilment | retail:handle | Cancel a despatch |
retail.cancel_order | retail:manage | Cancel an order |
retail.close_return | retail:handle | Close a return |
retail.complete_checkout | retail:write | Place the order |
retail.complete_draft_order | retail:write | Place a draft order |
retail.confirm_gift_card | retail:write | Confirm a gift card redemption |
retail.confirm_order | retail:approve | Confirm an order |
retail.create_checkout | retail:write | Open a basket |
retail.create_draft_order | retail:write | Start a draft order |
retail.create_warehouse | retail:manage | Open a warehouse |
retail.delete_channel | retail:delete | Delete a channel |
retail.delete_collection | retail:delete | Delete a collection |
retail.delete_draft_order | retail:delete | Discard a draft order |
retail.delete_draft_orders | retail:delete | Discard drafts |
retail.delete_shipping_method | retail:delete | Delete a delivery rate |
retail.delete_shipping_zone | retail:delete | Delete a shipping zone |
retail.delete_warehouse | retail:delete | Close a warehouse |
retail.detach_gift_card | retail:write | Take a gift card off |
retail.duplicate_order | retail:write | Order again |
retail.end_preorder | retail:manage | End a preorder |
retail.expire_order | retail:handle | Expire an unconfirmed order |
retail.expire_stock_reservations | retail:handle | Release expired checkout stock |
retail.fulfil_order | retail:handle | Despatch an order |
retail.grant_refund | retail:manage | Grant a refund |
retail.import_channel_order | retail:handle | Import a marketplace order |
retail.inspect_return | retail:handle | Inspect a return |
retail.open_shop | retail:manage | Open the shop |
retail.raise_abandoned_checkouts | retail:handle | Flag abandoned baskets |
retail.receive_return | retail:handle | Receive a return |
retail.refresh_collection | retail:write | Refresh a smart collection |
retail.reject_return | retail:handle | Refuse a return |
retail.remove_order_discount | retail:write | Remove the staff discount |
retail.renew_checkout_reservation | retail:write | Hold checkout stock |
retail.request_return | retail:handle | Open a return |
retail.return_products | retail:handle | Record a return |
retail.save_channel | retail:manage | Save a sales channel |
retail.save_collection | retail:write | Save a collection |
retail.save_shipping_method | retail:manage | Save a delivery rate |
retail.save_shipping_zone | retail:manage | Save a shipping zone |
retail.set_channel_active | retail:manage | Open or close a channel |
retail.set_channel_warehouses | retail:manage | Choose a channel’s warehouses |
retail.set_checkout_delivery | retail:write | Choose delivery |
retail.set_checkout_lines | retail:write | Change a basket |
retail.set_collection_items | retail:write | Choose what a collection holds |
retail.set_collection_rule | retail:write | Fill a collection from a rule |
retail.set_item_listing | retail:write | List something in a channel |
retail.set_order_discount | retail:write | Give a staff discount |
retail.set_order_lines | retail:write | Change an order’s lines |
retail.set_order_voucher | retail:write | Apply a coupon |
retail.set_postal_code_rules | retail:manage | Set postal-code rules |
retail.set_preorder | retail:manage | Sell on preorder |
retail.set_shipping_excluded_items | retail:manage | Exclude products from a delivery rate |
retail.set_shipping_price | retail:manage | Price a delivery rate in a channel |
retail.set_stock | retail:write | Count stock |
retail.set_stock_policy | retail:manage | Hold back safety stock |
retail.set_tracking | retail:handle | Set a tracking number |
retail.set_variant_price | retail:write | Price a variant in a channel |
retail.set_warehouse_zones | retail:manage | Choose which zones a warehouse serves |
retail.site_cart_add | retail:write | Add to basket (site) |
retail.site_cart_claim | retail:write | Keep my basket after signing in |
retail.site_cart_set | retail:write | Change a basket line (site) |
retail.site_checkout_delivery | retail:write | Choose delivery (site) |
retail.site_checkout_details | retail:write | Give checkout details (site) |
retail.site_checkout_voucher | retail:write | Use a coupon (site) |
retail.site_place_order | retail:write | Place an order (site) |
retail.sync_charge_status | retail:write | Update what an order has been paid |
retail.transfer_stock | retail:write | Move stock between warehouses |
retail.update_order | retail:write | Edit an order |
retail.update_order_metadata | retail:write | Edit order metadata |
retail.update_warehouse | retail:manage | Edit a warehouse |
retail.adjust_stock
Adjust stock — Goods in or out of one warehouse by an amount, with a reason for the ledger.
Needs retail:write · changes data
https://app.getera.io/api/v1/stocks/actions/adjust_stock- Canonical address:
POST /api/v1/actions/retail.adjust_stock
Input
| Field | Type | Required | Notes |
|---|---|---|---|
warehouseId | uuid | yes | Warehouse · id of warehouse |
itemVariantId | uuid | yes | Variant · id of item_variant |
delta | number | yes | Change · Positive in, negative out. |
reason | string (enum) | yes | Reason · Allowed values: count, received, damaged, correction |
note | string | Note · max 500 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
no_change | The change must be a whole number other than zero. |
warehouse_not_found | The warehouse must exist. |
variant_not_found | The variant must exist. |
below_allocated | The shelf may not drop below what orders are holding. |
Events it publishes: retail.stock_changed, retail.stock_low
What it writes: stock (upsert) — Moves the count by the amount, in the statement; stock_movement (insert) — Records the movement and its reason
curl -X POST https://app.getera.io/api/v1/stocks/actions/adjust_stock \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"warehouseId":"WAREHOUSE_ID","itemVariantId":"ITEM_VARIANT_ID","delta":1,"reason":"count"}'{
"ok": true,
"result": "…",
"events": [
"retail.stock_changed",
"retail.stock_low"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.stocks.adjustStock({
warehouseId: "WAREHOUSE_ID",
itemVariantId: "ITEM_VARIANT_ID",
delta: 1,
reason: "count"
})era stocks adjust_stock \
--warehouse-id "WAREHOUSE_ID" \
--item-variant-id "ITEM_VARIANT_ID" \
--delta 1 \
--reason "count"{
"name": "era__retail__adjust_stock",
"arguments": {
"warehouseId": "WAREHOUSE_ID",
"itemVariantId": "ITEM_VARIANT_ID",
"delta": 1,
"reason": "count"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 no_change, warehouse_not_found, variant_not_found, below_allocated · 429 slow down
retail.approve_fulfilment
Approve a despatch — Only needed when the shop does not approve despatches automatically.
Needs retail:approve · changes data
https://app.getera.io/api/v1/fulfilments/{id}/actions/approve_fulfilment- Canonical address:
POST /api/v1/actions/retail.approve_fulfilment
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Despatch · id of fulfilment |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The despatch must exist. |
fulfilment_not_awaiting | Only a despatch waiting for approval can be approved. |
Events it publishes: retail.fulfilment_approved
What it writes: fulfilment (update) — Marks it fulfilled
curl -X POST https://app.getera.io/api/v1/fulfilments/FULFILMENT_ID/actions/approve_fulfilment \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"retail.fulfilment_approved"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.fulfilments.approveFulfilment("FULFILMENT_ID")era fulfilments approve_fulfilment FULFILMENT_ID{
"name": "era__retail__approve_fulfilment",
"arguments": {
"id": "FULFILMENT_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:approve · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, fulfilment_not_awaiting · 429 slow down
retail.attach_gift_card
Pay with a gift card — One call: records the spend as pending and asks gift_cards to redeem it (a reaction); a refusal marks it failed and puts the amount back.
Needs retail:write · removes or cannot be undone
https://app.getera.io/api/v1/orders/{id}/actions/attach_gift_card- Canonical address:
POST /api/v1/actions/retail.attach_gift_card
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Order · id of sales_order |
code | string | Gift card code · max 64 | |
giftCardId | uuid | Gift card (already redeemed) | |
giftCardEventId | uuid | Redemption | |
codeTail | string | Last four · max 16 | |
amount | number (money) | yes | Amount · min 1 |
idempotencyKey | string | Idempotency key · max 512 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The order must exist. |
order_not_editable | Only a draft or unconfirmed order takes a gift card. |
gift_card_not_on_order | The card is named, by its code or as an already-redeemed card. |
gift_card_exceeds_due | A gift card never pays more than is still due. |
Events it publishes: retail.gift_card_requested, retail.gift_card_applied
What it writes: sales_order_gift_card (insert) — Records the spend (pending until redeemed); sales_order_gift_card (delete) — A failed try of the same card gives way to the new one; sales_order (update) — Lowers the amount due
curl -X POST https://app.getera.io/api/v1/orders/ORDER_ID/actions/attach_gift_card \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"amount":1}'{
"ok": true,
"result": "…",
"events": [
"retail.gift_card_requested",
"retail.gift_card_applied"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orders.attachGiftCard("ORDER_ID", {
amount: 1
})era orders attach_gift_card ORDER_ID \
--amount 1{
"name": "era__retail__attach_gift_card",
"arguments": {
"id": "ORDER_ID",
"amount": 1
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, order_not_editable, gift_card_not_on_order, gift_card_exceeds_due · 429 slow down
retail.bulk_update_prices
Reprice in bulk — One rule — set, or up or down by an amount or a percent — over many variants in a channel.
Needs retail:write · changes data
https://app.getera.io/api/v1/variant_listings/actions/bulk_update_prices- Canonical address:
POST /api/v1/actions/retail.bulk_update_prices
Input
| Field | Type | Required | Notes |
|---|---|---|---|
channelId | uuid | yes | Channel · id of channel |
itemVariantIds | object / array | yes | Variants |
mode | string (enum) | yes | Change · Allowed values: set, increase_percent, decrease_percent, increase_amount, decrease_amount |
value | number | yes | Value · min 0 |
field | string (enum) | Which figure · Allowed values: price, cost |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
channel_not_found | The channel must exist. |
no_variants | At least one variant must be chosen. |
variant_not_found | Every variant must exist. |
negative_price | No variant may end up below zero; one refusal stops the batch. |
Events it publishes: retail.channel_price_changed
What it writes: item_variant_channel_listing (upsert) — Writes each new channel price or cost
curl -X POST https://app.getera.io/api/v1/variant_listings/actions/bulk_update_prices \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"channelId":"CHANNEL_ID","itemVariantIds":{},"mode":"set","value":1}'{
"ok": true,
"result": "…",
"events": [
"retail.channel_price_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.variantListings.bulkUpdatePrices({
channelId: "CHANNEL_ID",
itemVariantIds: {},
mode: "set",
value: 1
})era variant_listings bulk_update_prices --data '{"channelId":"CHANNEL_ID","itemVariantIds":{},"mode":"set","value":1}'{
"name": "era__retail__bulk_update_prices",
"arguments": {
"channelId": "CHANNEL_ID",
"itemVariantIds": {},
"mode": "set",
"value": 1
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 channel_not_found, no_variants, variant_not_found, negative_price · 429 slow down
retail.cancel_fulfilment
Cancel a despatch — The goods go back to the shelf they left from.
Needs retail:handle · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/fulfilments/{id}/actions/cancel_fulfilment- Canonical address:
POST /api/v1/actions/retail.cancel_fulfilment
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Despatch · id of fulfilment |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The despatch must exist. |
fulfilment_not_cancellable | A refunded or cancelled despatch cannot be cancelled again. |
Events it publishes: retail.fulfilment_canceled
What it writes: fulfilment (update) — Marks it cancelled; stock (update) — Puts the goods back; sales_order_line (update) — Lowers quantity_fulfilled
curl -X POST https://app.getera.io/api/v1/fulfilments/FULFILMENT_ID/actions/cancel_fulfilment \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"retail.fulfilment_canceled"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.fulfilments.cancelFulfilment("FULFILMENT_ID")era fulfilments cancel_fulfilment FULFILMENT_ID{
"name": "era__retail__cancel_fulfilment",
"arguments": {
"id": "FULFILMENT_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, fulfilment_not_cancellable · 429 slow down
retail.cancel_order
Cancel an order — Releases everything it was holding. The goods never left the shelf.
Needs retail:manage · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/orders/{id}/actions/cancel_order- Canonical address:
POST /api/v1/actions/retail.cancel_order
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Order · id of sales_order |
reason | string | Reason · max 500 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The order must exist. |
order_not_cancellable | A finished, cancelled or expired order cannot be cancelled. |
order_has_fulfilments | Live despatches must be cancelled first. |
gift_card_pending | A gift card still being redeemed is answered before the order cancels. |
Events it publishes: retail.order_canceled, retail.gift_card_removed, retail.customer_notification_requested
What it writes: sales_order (update) — Marks it cancelled; allocation (delete) — Releases the stock it held; stock (update) — Lowers the allocated count; sales_order_gift_card (delete) — Gives every gift card back (gift_cards refunds it, a reaction)
curl -X POST https://app.getera.io/api/v1/orders/ORDER_ID/actions/cancel_order \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"reason":"Reason"}'{
"ok": true,
"result": "…",
"events": [
"retail.order_canceled",
"retail.gift_card_removed",
"retail.customer_notification_requested"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orders.cancelOrder("ORDER_ID", {
reason: "Reason"
})era orders cancel_order ORDER_ID \
--reason "Reason"{
"name": "era__retail__cancel_order",
"arguments": {
"id": "ORDER_ID",
"reason": "Reason"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, order_not_cancellable, order_has_fulfilments, gift_card_pending · 429 slow down
retail.close_return
Close a return — Nothing more will happen on it.
Needs retail:handle · changes data
https://app.getera.io/api/v1/order_returns/{id}/actions/close_return- Canonical address:
POST /api/v1/actions/retail.close_return
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Return · id of order_return |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
return_not_found | The return must exist. |
return_not_open | Only a received or inspected return can be closed. |
Events it publishes: retail.return_closed
What it writes: order_return (update) — Marks it closed
curl -X POST https://app.getera.io/api/v1/order_returns/ORDER_RETURN_ID/actions/close_return \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"retail.return_closed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orderReturns.closeReturn("ORDER_RETURN_ID")era order_returns close_return ORDER_RETURN_ID{
"name": "era__retail__close_return",
"arguments": {
"id": "ORDER_RETURN_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 return_not_found, return_not_open · 429 slow down
retail.complete_checkout
Place the order — Turns a basket into an order, allocates stock and closes the basket — once.
Needs retail:write · removes or cannot be undone
https://app.getera.io/api/v1/orders/actions/complete_checkout- Canonical address:
POST /api/v1/actions/retail.complete_checkout
Input
| Field | Type | Required | Notes |
|---|---|---|---|
token | uuid | yes | Basket |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The basket must exist. |
checkout_completing | The double-submit lock: one basket becomes one order. |
checkout_empty | An empty basket is not an order. |
prices_stale | A quote worked out too long ago must be refreshed. |
order_not_paid | Unless the channel allows unpaid orders, it must be paid for. |
shipping_address_missing | Something to be shipped needs somewhere to ship it. |
insufficient_stock | Every tracked line must be coverable, or none of them is taken. |
voucher_not_applicable | A coupon on the basket must still apply to the placed lines. |
voucher_usage_exceeded | A coupon on the basket must not be used up by the time the order is placed. |
Events it publishes: retail.order_placed, retail.customer_notification_requested, retail.stock_low, retail.order_discounts_applied
What it writes: sales_order (insert) — Writes the order; sales_order_line (insert) — Writes its lines, frozen; allocation (insert) — Promises stock to the order; stock (update) — Raises the allocated count; stock_reservation (update) — Commits the basket hold into the order allocation; checkout (delete) — Closes the basket
curl -X POST https://app.getera.io/api/v1/orders/actions/complete_checkout \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"token":"TOKEN_ID"}'{
"ok": true,
"result": "…",
"events": [
"retail.order_placed",
"retail.customer_notification_requested",
"retail.stock_low",
"retail.order_discounts_applied"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orders.completeCheckout({
token: "TOKEN_ID"
})era orders complete_checkout \
--token "TOKEN_ID"{
"name": "era__retail__complete_checkout",
"arguments": {
"token": "TOKEN_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, checkout_completing, checkout_empty, prices_stale, order_not_paid, shipping_address_missing, insufficient_stock, voucher_not_applicable, voucher_usage_exceeded · 429 slow down
retail.complete_draft_order
Place a draft order — Allocates stock and moves the draft into the order lifecycle.
Needs retail:write · changes data
https://app.getera.io/api/v1/orders/{id}/actions/complete_draft_order- Canonical address:
POST /api/v1/actions/retail.complete_draft_order
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Order · id of sales_order |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The order must exist. |
not_a_draft | Only a draft can be placed. |
order_empty | A draft with no lines is not an order. |
gift_card_pending | A gift card still being redeemed is answered before the draft is placed. |
shipping_address_missing | Something to be shipped needs somewhere to ship it. |
insufficient_stock | Every tracked line must be coverable, or none of them is taken. |
voucher_not_found | A coupon on the draft must still exist. |
voucher_not_applicable | A coupon on the draft must still apply. |
voucher_usage_exceeded | A coupon on the draft must not have been used up meanwhile. |
Events it publishes: retail.order_placed, retail.order_discounts_applied, retail.customer_notification_requested, retail.stock_low
What it writes: sales_order (update) — Moves it out of draft; sales_order_line (update) — Freezes the discount snapshot; sales_order_discount (insert) — Records what was applied; allocation (insert) — Promises stock to the order; preorder_allocation (insert) — Promises preorders; stock (update) — Raises the allocated count
curl -X POST https://app.getera.io/api/v1/orders/ORDER_ID/actions/complete_draft_order \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"retail.order_placed",
"retail.order_discounts_applied",
"retail.customer_notification_requested",
"retail.stock_low"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orders.completeDraftOrder("ORDER_ID")era orders complete_draft_order ORDER_ID{
"name": "era__retail__complete_draft_order",
"arguments": {
"id": "ORDER_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, not_a_draft, order_empty, gift_card_pending, shipping_address_missing, insufficient_stock, voucher_not_found, voucher_not_applicable, voucher_usage_exceeded · 429 slow down
retail.confirm_gift_card
Confirm a gift card redemption — Machine-only (finance-reactions.ts): gift_cards redeemed the card, so the pending row on the order is applied.
Needs retail:write · changes data
403 machine_only).https://app.getera.io/api/v1/order_gift_cards/{id}/actions/confirm_gift_card- Canonical address:
POST /api/v1/actions/retail.confirm_gift_card
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Gift card on the order · id of sales_order_gift_card |
giftCardId | uuid | yes | Gift card |
giftCardEventId | uuid | Redemption |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
gift_card_not_on_order | The gift card must be on the order. |
Events it publishes: retail.gift_card_applied
What it writes: sales_order_gift_card (update) — Applies the pending row
Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 gift_card_not_on_order · 429 slow down
retail.confirm_order
Confirm an order — The step that exists only when the channel does not confirm automatically.
Needs retail:approve · changes data
https://app.getera.io/api/v1/orders/{id}/actions/confirm_order- Canonical address:
POST /api/v1/actions/retail.confirm_order
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Order · id of sales_order |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The order must exist. |
order_not_unconfirmed | Only an unconfirmed order can be confirmed. |
Events it publishes: retail.order_confirmed
What it writes: sales_order (update) — Moves it to unfulfilled
curl -X POST https://app.getera.io/api/v1/orders/ORDER_ID/actions/confirm_order \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"retail.order_confirmed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orders.confirmOrder("ORDER_ID")era orders confirm_order ORDER_ID{
"name": "era__retail__confirm_order",
"arguments": {
"id": "ORDER_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:approve · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, order_not_unconfirmed · 429 slow down
retail.create_checkout
Open a basket — A basket in one channel, for a customer or a guest.
Needs retail:write · changes data
https://app.getera.io/api/v1/checkouts/actions/create_checkout- REST alias:
POST /api/v1/checkouts - Canonical address:
POST /api/v1/actions/retail.create_checkout
Input
| Field | Type | Required | Notes |
|---|---|---|---|
channelId | uuid | yes | Channel · id of channel |
customerId | uuid | Customer · id of customer | |
email | |||
lines | object / array | Lines |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
channel_not_found | The channel must exist. |
channel_closed | A closed channel takes no baskets. |
malformed_lines | Every line needs a variant and a quantity. |
variant_not_found | Every variant must exist. |
variant_not_priced | A variant with no price in this channel cannot be sold here. |
reservation_stock_unavailable | Every tracked line must be reservable for fifteen minutes. |
gift_line_locked | A promotion’s gift line cannot be edited by hand. |
Events it publishes: retail.checkout_created
What it writes: checkout (insert) — Opens the basket; checkout_line (upsert) — Writes its lines; stock_reservation (upsert) — Holds tracked stock for fifteen minutes
curl -X POST https://app.getera.io/api/v1/checkouts/actions/create_checkout \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"channelId":"CHANNEL_ID"}'{
"ok": true,
"result": "…",
"events": [
"retail.checkout_created"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.checkouts.create({
channelId: "CHANNEL_ID"
})era checkouts create \
--channel-id "CHANNEL_ID"{
"name": "era__retail__create_checkout",
"arguments": {
"channelId": "CHANNEL_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 channel_not_found, channel_closed, malformed_lines, variant_not_found, variant_not_priced, reservation_stock_unavailable, gift_line_locked · 429 slow down
retail.create_draft_order
Start a draft order — The shop assistant’s version of a basket: an order nobody has placed yet.
Needs retail:write · changes data
https://app.getera.io/api/v1/orders/actions/create_draft_order- REST alias:
POST /api/v1/orders - Canonical address:
POST /api/v1/actions/retail.create_draft_order
Input
| Field | Type | Required | Notes |
|---|---|---|---|
channelId | uuid | yes | Channel · id of channel |
customerId | uuid | Customer · id of customer | |
email | |||
customerNote | string | Note · max 2000 | |
billingAddressId | uuid | Billing address · id of address | |
shippingAddressId | uuid | Delivery address · id of address | |
fulfilmentMethod | string (enum) | Delivery · Allowed values: shipping, pickup |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
channel_not_found | The channel must exist. |
channel_closed | A closed channel takes no orders. |
Events it publishes: retail.draft_order_created
What it writes: sales_order (insert) — Writes the draft
curl -X POST https://app.getera.io/api/v1/orders/actions/create_draft_order \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"channelId":"CHANNEL_ID"}'{
"ok": true,
"result": "…",
"events": [
"retail.draft_order_created"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orders.create({
channelId: "CHANNEL_ID"
})era orders create \
--channel-id "CHANNEL_ID"{
"name": "era__retail__create_draft_order",
"arguments": {
"channelId": "CHANNEL_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 channel_not_found, channel_closed · 429 slow down
retail.create_warehouse
Open a warehouse — A place stock is held and orders are despatched from.
Needs retail:manage · changes data
https://app.getera.io/api/v1/warehouses/actions/create_warehouse- REST alias:
POST /api/v1/warehouses - Canonical address:
POST /api/v1/actions/retail.create_warehouse
Input
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | yes | Name · max 250 |
slug | string | Address · max 255 | |
addressId | uuid | Existing address · id of address | |
streetAddress1 | string | Street · max 250 | |
streetAddress2 | string | Street (second line) · max 250 | |
cityArea | string | District · max 120 | |
city | string | City · max 120 | |
countryArea | string | Region · max 120 | |
postalCode | string | Postal code · max 32 | |
country | string | Country · max 2 | |
phone | phone | Phone | |
email | |||
clickAndCollectOption | string (enum) | Collection · Allowed values: disabled, local, all | |
isPrivate | boolean | Hidden from customers |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
invalid_name | The name must produce a usable address. |
slug_taken | The address must be free in this workspace. |
address_not_found | A warehouse cannot exist without an address. |
Events it publishes: retail.warehouse_saved
What it writes: warehouse (insert) — Creates the warehouse
curl -X POST https://app.getera.io/api/v1/warehouses/actions/create_warehouse \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"name":"Name"}'{
"ok": true,
"result": "…",
"events": [
"retail.warehouse_saved"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.warehouses.create({
name: "Name"
})era warehouses create \
--name "Name"{
"name": "era__retail__create_warehouse",
"arguments": {
"name": "Name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 invalid_name, slug_taken, address_not_found · 429 slow down
retail.delete_channel
Delete a channel — Only a channel that never took an order or a basket; one that has sold is closed instead.
Needs retail:delete · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/sales_channels/{id}/actions/delete_channel- REST alias:
DELETE /api/v1/sales_channels/{id} - Canonical address:
POST /api/v1/actions/retail.delete_channel
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Channel · id of channel |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
channel_not_found | The channel must exist. |
channel_in_use | A channel with orders or baskets stays; close it instead. |
Events it publishes: retail.channel_deleted
What it writes: channel (delete) — Removes the channel and its listings
curl -X POST https://app.getera.io/api/v1/sales_channels/SALES_CHANNEL_ID/actions/delete_channel \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"retail.channel_deleted"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.salesChannels.deleteChannel("SALES_CHANNEL_ID")era sales_channels delete_channel SALES_CHANNEL_ID{
"name": "era__retail__delete_channel",
"arguments": {
"id": "SALES_CHANNEL_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 channel_not_found, channel_in_use · 429 slow down
retail.delete_collection
Delete a collection — The list goes; the items in it are the catalogue’s and stay.
Needs retail:delete · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/collections/{id}/actions/delete_collection- REST alias:
DELETE /api/v1/collections/{id} - Canonical address:
POST /api/v1/actions/retail.delete_collection
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Collection · id of item_collection |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The collection must exist. |
Events it publishes: retail.collection_deleted
What it writes: item_collection (delete) — Removes the collection
curl -X POST https://app.getera.io/api/v1/collections/COLLECTION_ID/actions/delete_collection \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"retail.collection_deleted"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.collections.deleteCollection("COLLECTION_ID")era collections delete_collection COLLECTION_ID{
"name": "era__retail__delete_collection",
"arguments": {
"id": "COLLECTION_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found · 429 slow down
retail.delete_draft_order
Discard a draft order — Removes an order shell only before it is placed.
Needs retail:delete · removes or cannot be undone
https://app.getera.io/api/v1/orders/{id}/actions/delete_draft_order- REST alias:
DELETE /api/v1/orders/{id} - Canonical address:
POST /api/v1/actions/retail.delete_draft_order
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Order · id of sales_order |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The order must exist. |
not_a_draft | Placed orders are cancelled, never deleted. |
draft_has_gift_cards | A gift card spent on the draft comes off first. |
Events it publishes: retail.draft_order_deleted
What it writes: sales_order (delete) — Removes the draft and its lines
curl -X POST https://app.getera.io/api/v1/orders/ORDER_ID/actions/delete_draft_order \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"retail.draft_order_deleted"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orders.deleteDraftOrder("ORDER_ID")era orders delete_draft_order ORDER_ID{
"name": "era__retail__delete_draft_order",
"arguments": {
"id": "ORDER_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, not_a_draft, draft_has_gift_cards · 429 slow down
retail.delete_draft_orders
Discard drafts — Several drafts at once, from the orders list. All or none.
Needs retail:delete · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/orders/actions/delete_draft_orders- Canonical address:
POST /api/v1/actions/retail.delete_draft_orders
Input
| Field | Type | Required | Notes |
|---|---|---|---|
ids | object / array | yes | Drafts |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
no_orders | At least one draft is chosen. |
not_a_draft | Every one must be a draft. |
draft_has_gift_cards | A draft a gift card paid towards keeps until the card is taken off. |
Events it publishes: retail.draft_order_deleted
What it writes: sales_order (delete) — Removes the drafts
curl -X POST https://app.getera.io/api/v1/orders/actions/delete_draft_orders \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"ids":{}}'{
"ok": true,
"result": "…",
"events": [
"retail.draft_order_deleted"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orders.deleteDraftOrders({
ids: {}
})era orders delete_draft_orders --data '{"ids":{}}'{
"name": "era__retail__delete_draft_orders",
"arguments": {
"ids": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 no_orders, not_a_draft, draft_has_gift_cards · 429 slow down
retail.delete_shipping_method
Delete a delivery rate — Past orders keep the name they were sold with.
Needs retail:delete · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/shipping_methods/{id}/actions/delete_shipping_method- REST alias:
DELETE /api/v1/shipping_methods/{id} - Canonical address:
POST /api/v1/actions/retail.delete_shipping_method
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Rate · id of shipping_method |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The rate must exist. |
Events it publishes: retail.shipping_method_deleted
What it writes: shipping_method (delete) — Removes the rate
curl -X POST https://app.getera.io/api/v1/shipping_methods/SHIPPING_METHOD_ID/actions/delete_shipping_method \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"retail.shipping_method_deleted"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.shippingMethods.deleteShippingMethod("SHIPPING_METHOD_ID")era shipping_methods delete_shipping_method SHIPPING_METHOD_ID{
"name": "era__retail__delete_shipping_method",
"arguments": {
"id": "SHIPPING_METHOD_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found · 429 slow down
retail.delete_shipping_zone
Delete a shipping zone — Only an empty zone can go; its rates would leave orders without one.
Needs retail:delete · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/shipping_zones/{id}/actions/delete_shipping_zone- REST alias:
DELETE /api/v1/shipping_zones/{id} - Canonical address:
POST /api/v1/actions/retail.delete_shipping_zone
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Zone · id of shipping_zone |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The zone must exist. |
zone_has_methods | A zone holding rates cannot be deleted. |
Events it publishes: retail.shipping_zone_deleted
What it writes: shipping_zone (delete) — Removes the zone
curl -X POST https://app.getera.io/api/v1/shipping_zones/SHIPPING_ZONE_ID/actions/delete_shipping_zone \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"retail.shipping_zone_deleted"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.shippingZones.deleteShippingZone("SHIPPING_ZONE_ID")era shipping_zones delete_shipping_zone SHIPPING_ZONE_ID{
"name": "era__retail__delete_shipping_zone",
"arguments": {
"id": "SHIPPING_ZONE_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, zone_has_methods · 429 slow down
retail.delete_warehouse
Close a warehouse — The warehouse and its address go together, as they do in the source.
Needs retail:delete · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/warehouses/{id}/actions/delete_warehouse- REST alias:
DELETE /api/v1/warehouses/{id} - Canonical address:
POST /api/v1/actions/retail.delete_warehouse
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Warehouse · id of warehouse |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The warehouse must exist. |
warehouse_has_allocations | Stock promised to open orders must be settled first. |
Events it publishes: retail.warehouse_deleted
What it writes: warehouse (delete) — Removes the warehouse and its stock rows
curl -X POST https://app.getera.io/api/v1/warehouses/WAREHOUSE_ID/actions/delete_warehouse \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"retail.warehouse_deleted"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.warehouses.deleteWarehouse("WAREHOUSE_ID")era warehouses delete_warehouse WAREHOUSE_ID{
"name": "era__retail__delete_warehouse",
"arguments": {
"id": "WAREHOUSE_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, warehouse_has_allocations · 429 slow down
retail.detach_gift_card
Take a gift card off — Removes a gift card from an order; gift_cards puts the money back onto the card (a reaction). With failure set, it is the compensation of a refused redemption.
Needs retail:write · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/order_gift_cards/{id}/actions/detach_gift_card- Canonical address:
POST /api/v1/actions/retail.detach_gift_card
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Gift card on the order · id of sales_order_gift_card |
failure | string | Why the card was refused (compensation) · max 500 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
gift_card_not_on_order | The gift card must be on the order. |
gift_card_pending | A card still being redeemed waits for its answer. |
order_not_editable | A placed order keeps its gift cards. |
Events it publishes: retail.gift_card_removed
What it writes: sales_order_gift_card (delete) — Removes the spend; sales_order_gift_card (update) — Marks a refused redemption failed; sales_order (update) — Raises the amount due again
curl -X POST https://app.getera.io/api/v1/order_gift_cards/ORDER_GIFT_CARD_ID/actions/detach_gift_card \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"failure":"Why the card was refused (compensation)"}'{
"ok": true,
"result": "…",
"events": [
"retail.gift_card_removed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orderGiftCards.detachGiftCard("ORDER_GIFT_CARD_ID", {
failure: "Why the card was refused (compensation)"
})era order_gift_cards detach_gift_card ORDER_GIFT_CARD_ID \
--failure "Why the card was refused (compensation)"{
"name": "era__retail__detach_gift_card",
"arguments": {
"id": "ORDER_GIFT_CARD_ID",
"failure": "Why the card was refused (compensation)"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 gift_card_not_on_order, gift_card_pending, order_not_editable · 429 slow down
retail.duplicate_order
Order again — A new draft with the same customer, addresses and lines, at today’s channel prices.
Needs retail:write · changes data
https://app.getera.io/api/v1/orders/{id}/actions/duplicate_order- Canonical address:
POST /api/v1/actions/retail.duplicate_order
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Order · id of sales_order |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The order must exist. |
channel_closed | Its channel must still be open. |
Events it publishes: retail.draft_order_created, retail.order_lines_changed, retail.order_discounts_applied
What it writes: sales_order (insert) — Writes the new draft; sales_order_line (insert) — Copies the lines at today’s prices; sales_order_discount (insert) — Records what applies to the new draft
curl -X POST https://app.getera.io/api/v1/orders/ORDER_ID/actions/duplicate_order \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"retail.draft_order_created",
"retail.order_lines_changed",
"retail.order_discounts_applied"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orders.duplicateOrder("ORDER_ID")era orders duplicate_order ORDER_ID{
"name": "era__retail__duplicate_order",
"arguments": {
"id": "ORDER_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, channel_closed · 429 slow down
retail.end_preorder
End a preorder — The goods arrived: every preorder becomes a real allocation on a shelf — all of them, or none.
Needs retail:manage · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/allocations/actions/end_preorder- Canonical address:
POST /api/v1/actions/retail.end_preorder
Input
| Field | Type | Required | Notes |
|---|---|---|---|
itemVariantId | uuid | yes | Variant · id of item_variant |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
variant_not_found | The variant must exist. |
not_preorder | The variant must be on preorder. |
insufficient_stock | The shelves must hold what the preorders promised. |
Events it publishes: retail.preorder_ended, retail.stock_low
What it writes: preorder_allocation (delete) — Releases the preorders; variant_preorder (delete) — Ends the preorder; allocation (insert) — Promises the shelf instead; stock (update) — Raises the allocated count
curl -X POST https://app.getera.io/api/v1/allocations/actions/end_preorder \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"itemVariantId":"ITEM_VARIANT_ID"}'{
"ok": true,
"result": "…",
"events": [
"retail.preorder_ended",
"retail.stock_low"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.allocations.endPreorder({
itemVariantId: "ITEM_VARIANT_ID"
})era allocations end_preorder \
--item-variant-id "ITEM_VARIANT_ID"{
"name": "era__retail__end_preorder",
"arguments": {
"itemVariantId": "ITEM_VARIANT_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 variant_not_found, not_preorder, insufficient_stock · 429 slow down
retail.expire_order
Expire an unconfirmed order — Machine: the shop sweep lets an order nobody confirmed in time go, with its stock and its coupon.
Needs retail:handle · removes or cannot be undone
https://app.getera.io/api/v1/orders/{id}/actions/expire_order- Canonical address:
POST /api/v1/actions/retail.expire_order
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Order · id of sales_order |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The order must exist. |
order_not_expirable | Only an unconfirmed order past the expiry setting expires. |
Events it publishes: retail.order_expired
What it writes: sales_order (update) — Marks it expired; allocation (delete) — Releases the stock it held; stock (update) — Lowers the allocated count
curl -X POST https://app.getera.io/api/v1/orders/ORDER_ID/actions/expire_order \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"retail.order_expired"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orders.expireOrder("ORDER_ID")era orders expire_order ORDER_ID{
"name": "era__retail__expire_order",
"arguments": {
"id": "ORDER_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, order_not_expirable · 429 slow down
retail.expire_stock_reservations
Release expired checkout stock — Machine action: releases checkout claims older than fifteen minutes.
Needs retail:handle · changes data
https://app.getera.io/api/v1/stock_reservations/actions/expire_stock_reservations- Canonical address:
POST /api/v1/actions/retail.expire_stock_reservations
Input
This action takes no input.
Events it publishes: retail.stock_reservations_expired
What it writes: stock_reservation (update) — Marks expired claims released
curl -X POST https://app.getera.io/api/v1/stock_reservations/actions/expire_stock_reservations \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"retail.stock_reservations_expired"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.stockReservations.expireStockReservations()era stock_reservations expire_stock_reservations{
"name": "era__retail__expire_stock_reservations",
"arguments": {}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 429 slow down
retail.fulfil_order
Despatch an order — Goods leave a named shelf. This is where the count actually drops.
Needs retail:handle · changes data
https://app.getera.io/api/v1/orders/{id}/actions/fulfil_order- Canonical address:
POST /api/v1/actions/retail.fulfil_order
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Order · id of sales_order |
lines | object / array | yes | Lines |
trackingNumber | string | Tracking number · max 255 | |
carrier | string | Carrier · max 120 | |
trackingUrl | string | Tracking link · max 1000 | |
notifyCustomer | boolean | Tell the customer |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The order must exist. |
order_not_fulfillable | Only an open order can be despatched from. |
preorder_not_fulfillable | A line on preorder waits for the preorder to end. |
malformed_lines | Every despatch line needs an order line and a quantity. |
line_not_found | Every line must be on this order. |
over_fulfilment | More cannot go out than was ordered. |
insufficient_stock | The named warehouse must actually hold the goods. |
Events it publishes: retail.fulfilment_created, retail.customer_notification_requested, retail.stock_low
What it writes: fulfilment (insert) — Records the despatch; fulfilment_line (insert) — Records which shelf each line left from; stock (update) — Lowers the count and the allocation; sales_order_line (update) — Raises quantity_fulfilled; sales_order (update) — Settles the order status
curl -X POST https://app.getera.io/api/v1/orders/ORDER_ID/actions/fulfil_order \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"lines":{}}'{
"ok": true,
"result": "…",
"events": [
"retail.fulfilment_created",
"retail.customer_notification_requested",
"retail.stock_low"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orders.fulfilOrder("ORDER_ID", {
lines: {}
})era orders fulfil_order ORDER_ID --data '{"lines":{}}'{
"name": "era__retail__fulfil_order",
"arguments": {
"id": "ORDER_ID",
"lines": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, order_not_fulfillable, preorder_not_fulfillable, malformed_lines, line_not_found, over_fulfilment, insufficient_stock · 429 slow down
retail.grant_refund
Grant a refund — The shop agrees to give an amount back — Saleor’s granted refund. The money moves in Finance.
Needs retail:manage · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/order_granted_refunds/actions/grant_refund- Canonical address:
POST /api/v1/actions/retail.grant_refund
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Order · id of sales_order |
amount | number (money) | Amount · Leave empty to work it out from the lines. · min 0 | |
lines | object / array | Lines | |
includeShipping | boolean | Include delivery | |
reason | string | Reason · max 500 | |
returnId | uuid | Return · id of order_return |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The order must exist. |
line_not_found | Every line must be on this order. |
no_refund_amount | A grant has an amount. |
refund_exceeds_order | Grants never exceed what the order was sold for. |
return_not_found | A named return must exist. |
Events it publishes: retail.refund_granted, retail.customer_notification_requested
What it writes: order_granted_refund (insert) — Records the grant; order_granted_refund_line (insert) — Records the lines it is for
curl -X POST https://app.getera.io/api/v1/order_granted_refunds/actions/grant_refund \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"id":"SALES_ORDER_ID"}'{
"ok": true,
"result": "…",
"events": [
"retail.refund_granted",
"retail.customer_notification_requested"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orderGrantedRefunds.grantRefund({
id: "SALES_ORDER_ID"
})era order_granted_refunds grant_refund \
--id "SALES_ORDER_ID"{
"name": "era__retail__grant_refund",
"arguments": {
"id": "SALES_ORDER_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, line_not_found, no_refund_amount, refund_exceeds_order, return_not_found · 429 slow down
retail.import_channel_order
Import a marketplace order — Writes (or moves) one order that came from a marketplace or another shop: lines, frozen addresses, the stock it holds.
Needs retail:handle · changes data
https://app.getera.io/api/v1/orders/actions/import_channel_order- Canonical address:
POST /api/v1/actions/retail.import_channel_order
Input
| Field | Type | Required | Notes |
|---|---|---|---|
channelId | uuid | yes | Channel · id of channel |
accountId | uuid | Integration account | |
provider | string | yes | Provider · max 41 |
warehouseId | uuid | Warehouse · id of warehouse | |
customerId | uuid | Customer · id of customer | |
order | object / array | yes | Order |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
malformed_order | The order needs an id, a known status, a currency and lines. |
channel_not_found | The channel must exist in this workspace. |
unknown_order | A status-only update cannot create an order Era does not have. |
Events it publishes: retail.channel_order_imported, retail.channel_order_updated, retail.stock_low
What it writes: sales_order (upsert) — The order, keyed on (channel, external reference); sales_order_line (insert) — Its lines, each tied to a variant where one matched; allocation (upsert) — Holds the stock (may oversell; returned as a shortage); stock (update) — Allocated, released or taken off the shelf by a despatch; stock_movement (insert) — The despatch in the stock ledger; fulfilment (insert) — The despatch when the marketplace shipped it; fulfilment_line (insert) — What left, from which shelf
curl -X POST https://app.getera.io/api/v1/orders/actions/import_channel_order \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"channelId":"CHANNEL_ID","provider":"Provider","order":{}}'{
"ok": true,
"result": "…",
"events": [
"retail.channel_order_imported",
"retail.channel_order_updated",
"retail.stock_low"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orders.importChannelOrder({
channelId: "CHANNEL_ID",
provider: "Provider",
order: {}
})era orders import_channel_order --data '{"channelId":"CHANNEL_ID","provider":"Provider","order":{}}'{
"name": "era__retail__import_channel_order",
"arguments": {
"channelId": "CHANNEL_ID",
"provider": "Provider",
"order": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 malformed_order, channel_not_found, unknown_order · 429 slow down
retail.inspect_return
Inspect a return — What can be sold again goes back on a named shelf, with a ledger row; what is damaged does not.
Needs retail:handle · changes data
https://app.getera.io/api/v1/order_returns/{id}/actions/inspect_return- Canonical address:
POST /api/v1/actions/retail.inspect_return
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Return · id of order_return |
lines | object / array | yes | Lines |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
return_not_found | The return must exist. |
return_not_open | Only a received return can be inspected. |
line_not_found | Every line must be on this return. |
over_restock | More cannot go back on the shelf than came back. |
warehouse_not_found | A restock names the shelf it goes back on. |
Events it publishes: retail.return_inspected, retail.stock_changed, retail.stock_low
What it writes: order_return_line (update) — Records the condition and the restock; order_return (update) — Marks it inspected; stock (upsert) — Puts resellable goods back; stock_movement (insert) — Explains the restock
curl -X POST https://app.getera.io/api/v1/order_returns/ORDER_RETURN_ID/actions/inspect_return \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"lines":{}}'{
"ok": true,
"result": "…",
"events": [
"retail.return_inspected",
"retail.stock_changed",
"retail.stock_low"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orderReturns.inspectReturn("ORDER_RETURN_ID", {
lines: {}
})era order_returns inspect_return ORDER_RETURN_ID --data '{"lines":{}}'{
"name": "era__retail__inspect_return",
"arguments": {
"id": "ORDER_RETURN_ID",
"lines": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 return_not_found, return_not_open, line_not_found, over_restock, warehouse_not_found · 429 slow down
retail.open_shop
Open the shop — One call: an open sales channel named after the workspace, in TRY and Türkiye unless told otherwise. Answers with the existing shop when there is one. Era calls it after "Mağazan yok — açayım mı?" is answered yes.
Needs retail:manage · changes data
https://app.getera.io/api/v1/sales_channels/actions/open_shop- Canonical address:
POST /api/v1/actions/retail.open_shop
Input
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | Name · Empty = the workspace name. · max 250 | |
currencyCode | string | Currency · ISO-4217. Empty = TRY. · max 3 | |
defaultCountry | string | Default country · Empty = TR. · max 2 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
invalid_currency | The currency must be an ISO-4217 code. |
Events it publishes: retail.channel_saved
What it writes: channel (upsert) — Creates (or opens) the shop channel
curl -X POST https://app.getera.io/api/v1/sales_channels/actions/open_shop \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"name":"Name"}'{
"ok": true,
"result": "…",
"events": [
"retail.channel_saved"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.salesChannels.openShop({
name: "Name"
})era sales_channels open_shop \
--name "Name"{
"name": "era__retail__open_shop",
"arguments": {
"name": "Name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 invalid_currency · 429 slow down
retail.raise_abandoned_checkouts
Flag abandoned baskets — Machine-only (the shop sweep): every basket with an address on it that went quiet for the window is told about once.
Needs retail:handle · changes data
403 machine_only).https://app.getera.io/api/v1/checkouts/actions/raise_abandoned_checkouts- Canonical address:
POST /api/v1/actions/retail.raise_abandoned_checkouts
Input
This action takes no input.
Events it publishes: retail.checkout_abandoned
What it writes: checkout (update) — Records that the basket was flagged as abandoned
Errors: 401 no or expired credential · 403 the credential does not hold retail:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 429 slow down
retail.receive_return
Receive a return — The parcel arrived. The order moves to returned or partially returned; the shelf waits for the inspection.
Needs retail:handle · changes data
https://app.getera.io/api/v1/order_returns/{id}/actions/receive_return- Canonical address:
POST /api/v1/actions/retail.receive_return
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Return · id of order_return |
lines | object / array | Lines |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
return_not_found | The return must exist. |
return_not_open | Only a requested return can be received. |
over_receipt | More cannot arrive than was asked back. |
Events it publishes: retail.return_received, retail.order_returned
What it writes: order_return_line (update) — Records what arrived; order_return (update) — Marks it received; sales_order (update) — Moves it to returned or partially returned
curl -X POST https://app.getera.io/api/v1/order_returns/ORDER_RETURN_ID/actions/receive_return \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"lines":{}}'{
"ok": true,
"result": "…",
"events": [
"retail.return_received",
"retail.order_returned"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orderReturns.receiveReturn("ORDER_RETURN_ID", {
lines: {}
})era order_returns receive_return ORDER_RETURN_ID --data '{"lines":{}}'{
"name": "era__retail__receive_return",
"arguments": {
"id": "ORDER_RETURN_ID",
"lines": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 return_not_found, return_not_open, over_receipt · 429 slow down
retail.refresh_collection
Refresh a smart collection — Runs the rule again, for items added or changed since.
Needs retail:write · changes data
https://app.getera.io/api/v1/collection_items/actions/refresh_collection- Canonical address:
POST /api/v1/actions/retail.refresh_collection
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Collection · id of item_collection |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The collection must exist. |
collection_not_smart | Only a rule collection can be refreshed. |
Events it publishes: retail.collection_items_changed
What it writes: item_collection_item (upsert) — Replaces the membership
curl -X POST https://app.getera.io/api/v1/collection_items/actions/refresh_collection \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"id":"ITEM_COLLECTION_ID"}'{
"ok": true,
"result": "…",
"events": [
"retail.collection_items_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.collectionItems.refreshCollection({
id: "ITEM_COLLECTION_ID"
})era collection_items refresh_collection \
--id "ITEM_COLLECTION_ID"{
"name": "era__retail__refresh_collection",
"arguments": {
"id": "ITEM_COLLECTION_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, collection_not_smart · 429 slow down
retail.reject_return
Refuse a return — The return is not accepted — outside the window, or not what was sold.
Needs retail:handle · reaches outside (sends, publishes or charges) · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/order_returns/{id}/actions/reject_return- Canonical address:
POST /api/v1/actions/retail.reject_return
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Return · id of order_return |
reason | string | Reason · max 500 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
return_not_found | The return must exist. |
return_not_open | Only a requested return can be refused. |
Events it publishes: retail.return_rejected
What it writes: order_return (update) — Marks it rejected
curl -X POST https://app.getera.io/api/v1/order_returns/ORDER_RETURN_ID/actions/reject_return \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"reason":"Reason"}'{
"ok": true,
"result": "…",
"events": [
"retail.return_rejected"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orderReturns.rejectReturn("ORDER_RETURN_ID", {
reason: "Reason"
})era order_returns reject_return ORDER_RETURN_ID \
--reason "Reason"{
"name": "era__retail__reject_return",
"arguments": {
"id": "ORDER_RETURN_ID",
"reason": "Reason"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 return_not_found, return_not_open · 429 slow down
retail.remove_order_discount
Remove the staff discount — Saleor’s orderDiscountDelete. The coupon and promotions apply again.
Needs retail:write · removes or cannot be undone
https://app.getera.io/api/v1/orders/{id}/actions/remove_order_discount- Canonical address:
POST /api/v1/actions/retail.remove_order_discount
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Order · id of sales_order |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The order must exist. |
order_not_editable | Only a draft or unconfirmed order changes its discount. |
Events it publishes: retail.order_discounts_applied
What it writes: sales_order (update) — Clears the discount and recomputes the totals; sales_order_line (update) — Rewrites the per-line snapshot; sales_order_discount (insert) — Records what applies now
curl -X POST https://app.getera.io/api/v1/orders/ORDER_ID/actions/remove_order_discount \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"retail.order_discounts_applied"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orders.removeOrderDiscount("ORDER_ID")era orders remove_order_discount ORDER_ID{
"name": "era__retail__remove_order_discount",
"arguments": {
"id": "ORDER_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, order_not_editable · 429 slow down
retail.renew_checkout_reservation
Hold checkout stock — Renews the basket stock claim for fifteen minutes.
Needs retail:write · changes data
https://app.getera.io/api/v1/stock_reservations/actions/renew_checkout_reservation- Canonical address:
POST /api/v1/actions/retail.renew_checkout_reservation
Input
| Field | Type | Required | Notes |
|---|---|---|---|
token | uuid | yes | Basket |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The basket must exist. |
checkout_completing | A basket being completed cannot renew its hold. |
reservation_stock_unavailable | Every tracked line must be reservable. |
Events it publishes: retail.stock_reserved
What it writes: stock_reservation (upsert) — Replaces the fifteen-minute stock hold
curl -X POST https://app.getera.io/api/v1/stock_reservations/actions/renew_checkout_reservation \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"token":"TOKEN_ID"}'{
"ok": true,
"result": "…",
"events": [
"retail.stock_reserved"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.stockReservations.renewCheckoutReservation({
token: "TOKEN_ID"
})era stock_reservations renew_checkout_reservation \
--token "TOKEN_ID"{
"name": "era__retail__renew_checkout_reservation",
"arguments": {
"token": "TOKEN_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, checkout_completing, reservation_stock_unavailable · 429 slow down
retail.request_return
Open a return — The customer is sending something back. Nothing moves until it arrives.
Needs retail:handle · changes data
https://app.getera.io/api/v1/order_returns/actions/request_return- Canonical address:
POST /api/v1/actions/retail.request_return
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Order · id of sales_order |
lines | object / array | yes | Lines |
reason | string | Reason · max 500 | |
note | string | Note · max 2000 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The order must exist. |
order_not_returnable | Nothing can come back that never went out. |
malformed_lines | Say which lines are coming back, and how many. |
line_not_found | Every line must be on this order. |
over_return | More cannot come back than went out and is not already on its way. |
Events it publishes: retail.return_requested
What it writes: order_return (insert) — Opens the return; order_return_line (insert) — Lists what is coming back
curl -X POST https://app.getera.io/api/v1/order_returns/actions/request_return \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"id":"SALES_ORDER_ID","lines":{}}'{
"ok": true,
"result": "…",
"events": [
"retail.return_requested"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orderReturns.requestReturn({
id: "SALES_ORDER_ID",
lines: {}
})era order_returns request_return --data '{"id":"SALES_ORDER_ID","lines":{}}'{
"name": "era__retail__request_return",
"arguments": {
"id": "SALES_ORDER_ID",
"lines": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, order_not_returnable, malformed_lines, line_not_found, over_return · 429 slow down
retail.return_products
Record a return — The status moves; the shelf does not. A returned item may be damaged.
Needs retail:handle · reaches outside (sends, publishes or charges) · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/orders/{id}/actions/return_products- Canonical address:
POST /api/v1/actions/retail.return_products
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Order · id of sales_order |
lines | object / array | yes | Lines |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The order must exist. |
order_not_returnable | Nothing can come back that never went out. |
malformed_lines | Say which lines are coming back, and how many. |
line_not_found | Every line must be on this order. |
over_return | More cannot come back than went out. |
Events it publishes: retail.order_returned
What it writes: sales_order (update) — Moves it to returned or partially returned
curl -X POST https://app.getera.io/api/v1/orders/ORDER_ID/actions/return_products \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"lines":{}}'{
"ok": true,
"result": "…",
"events": [
"retail.order_returned"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orders.returnProducts("ORDER_ID", {
lines: {}
})era orders return_products ORDER_ID --data '{"lines":{}}'{
"name": "era__retail__return_products",
"arguments": {
"id": "ORDER_ID",
"lines": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, order_not_returnable, malformed_lines, line_not_found, over_return · 429 slow down
retail.save_channel
Save a sales channel — A place things are sold: its name, address, currency and country.
Needs retail:manage · changes data
https://app.getera.io/api/v1/sales_channels/{id}/actions/save_channel- REST alias:
PATCH /api/v1/sales_channels/{id} - Canonical address:
POST /api/v1/actions/retail.save_channel
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | Filled from the path {id} · Channel · id of channel | |
name | string | yes | Name · max 250 |
slug | string | Address · max 255 | |
currencyCode | string | Currency · ISO-4217, three letters. · max 3 | |
defaultCountry | string | Default country · max 2 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
invalid_name | The name must produce a usable address. |
slug_taken | The address must be free in this workspace. |
invalid_currency | The currency must be an ISO-4217 code. |
channel_not_found | The channel must exist. |
channel_currency_locked | A channel with prices in it cannot change currency. |
Events it publishes: retail.channel_saved
What it writes: channel (upsert) — Creates or edits the channel
curl -X POST https://app.getera.io/api/v1/sales_channels/SALES_CHANNEL_ID/actions/save_channel \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"name":"Name"}'{
"ok": true,
"result": "…",
"events": [
"retail.channel_saved"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.salesChannels.saveChannel("SALES_CHANNEL_ID", {
name: "Name"
})era sales_channels save_channel SALES_CHANNEL_ID \
--name "Name"{
"name": "era__retail__save_channel",
"arguments": {
"id": "SALES_CHANNEL_ID",
"name": "Name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 invalid_name, slug_taken, invalid_currency, channel_not_found, channel_currency_locked · 429 slow down
retail.save_collection
Save a collection — A hand-ordered merchandising list — “Summer”, “Staff picks”.
Needs retail:write · changes data
https://app.getera.io/api/v1/collections/{id}/actions/save_collection- REST alias:
PATCH /api/v1/collections/{id} - Canonical address:
POST /api/v1/actions/retail.save_collection
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | Filled from the path {id} · Collection · id of item_collection | |
name | string | yes | Name · max 250 |
slug | string | Address · max 255 | |
description | string | Description · max 4000 | |
seoTitle | string | SEO title · max 200 | |
seoDescription | string | SEO description · max 500 | |
backgroundFileId | uuid | Banner image · id of file | |
backgroundAlt | string | Banner description · max 250 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
invalid_name | The name must produce a usable address. |
slug_taken | The address must be free in this workspace. |
not_found | The collection must exist when one is named. |
Events it publishes: retail.collection_saved
What it writes: item_collection (upsert) — Creates or edits the collection
curl -X POST https://app.getera.io/api/v1/collections/COLLECTION_ID/actions/save_collection \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"name":"Name"}'{
"ok": true,
"result": "…",
"events": [
"retail.collection_saved"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.collections.saveCollection("COLLECTION_ID", {
name: "Name"
})era collections save_collection COLLECTION_ID \
--name "Name"{
"name": "era__retail__save_collection",
"arguments": {
"id": "COLLECTION_ID",
"name": "Name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 invalid_name, slug_taken, not_found · 429 slow down
retail.save_shipping_method
Save a delivery rate — A way of delivering, priced by order value or by weight.
Needs retail:manage · changes data
https://app.getera.io/api/v1/shipping_methods/{id}/actions/save_shipping_method- REST alias:
PATCH /api/v1/shipping_methods/{id} - Canonical address:
POST /api/v1/actions/retail.save_shipping_method
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | Filled from the path {id} · Rate · id of shipping_method | |
shippingZoneId | uuid | yes | Zone · id of shipping_zone |
name | string | yes | Name · max 100 |
type | string (enum) | Priced by · Allowed values: price, weight | |
description | string | Description · max 2000 | |
minimumOrderWeightGrams | number | From (grams) · min 0 | |
maximumOrderWeightGrams | number | To (grams) · min 0 | |
minimumDeliveryDays | number | Fastest (days) · min 0 | |
maximumDeliveryDays | number | Slowest (days) · min 0 | |
taxClassId | uuid | Tax class · Empty: the country’s default rate. · id of tax_class |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
zone_not_found | The zone must exist. |
tax_class_not_found | The tax class must exist and not be archived. |
not_found | The rate must exist when one is named. |
weight_on_price_method | A price-based rate carries no weight band. |
range_inverted | The smaller end of a range must not exceed the larger. |
Events it publishes: retail.shipping_method_saved
What it writes: shipping_method (upsert) — Creates or edits the rate
curl -X POST https://app.getera.io/api/v1/shipping_methods/SHIPPING_METHOD_ID/actions/save_shipping_method \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"shippingZoneId":"SHIPPING_ZONE_ID","name":"Name"}'{
"ok": true,
"result": "…",
"events": [
"retail.shipping_method_saved"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.shippingMethods.saveShippingMethod("SHIPPING_METHOD_ID", {
shippingZoneId: "SHIPPING_ZONE_ID",
name: "Name"
})era shipping_methods save_shipping_method SHIPPING_METHOD_ID \
--shipping-zone-id "SHIPPING_ZONE_ID" \
--name "Name"{
"name": "era__retail__save_shipping_method",
"arguments": {
"id": "SHIPPING_METHOD_ID",
"shippingZoneId": "SHIPPING_ZONE_ID",
"name": "Name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 zone_not_found, tax_class_not_found, not_found, weight_on_price_method, range_inverted · 429 slow down
retail.save_shipping_zone
Save a shipping zone — A group of countries that share delivery rates.
Needs retail:manage · changes data
https://app.getera.io/api/v1/shipping_zones/{id}/actions/save_shipping_zone- REST alias:
PATCH /api/v1/shipping_zones/{id} - Canonical address:
POST /api/v1/actions/retail.save_shipping_zone
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | Filled from the path {id} · Zone · id of shipping_zone | |
name | string | yes | Name · max 100 |
description | string | Description · max 2000 | |
countries | object / array | Countries | |
isDefault | boolean | Everywhere else | |
channelIds | object / array | Channels |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
invalid_country | Every country must be a two-letter code. |
not_found | The zone must exist when one is named. |
channel_not_found | Every channel must exist. |
Events it publishes: retail.shipping_zone_saved
What it writes: shipping_zone (upsert) — Creates or edits the zone
curl -X POST https://app.getera.io/api/v1/shipping_zones/SHIPPING_ZONE_ID/actions/save_shipping_zone \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"name":"Name"}'{
"ok": true,
"result": "…",
"events": [
"retail.shipping_zone_saved"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.shippingZones.saveShippingZone("SHIPPING_ZONE_ID", {
name: "Name"
})era shipping_zones save_shipping_zone SHIPPING_ZONE_ID \
--name "Name"{
"name": "era__retail__save_shipping_zone",
"arguments": {
"id": "SHIPPING_ZONE_ID",
"name": "Name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 invalid_country, not_found, channel_not_found · 429 slow down
retail.set_channel_active
Open or close a channel — A closed channel takes no baskets and no orders.
Needs retail:manage · changes data
https://app.getera.io/api/v1/sales_channels/{id}/actions/set_channel_active- Canonical address:
POST /api/v1/actions/retail.set_channel_active
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Channel · id of channel |
isActive | boolean | yes | Open |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
channel_not_found | The channel must exist. |
channel_has_open_work | A channel with open baskets or orders cannot be closed. |
Events it publishes: retail.channel_saved
What it writes: channel (update) — Opens or closes the channel
curl -X POST https://app.getera.io/api/v1/sales_channels/SALES_CHANNEL_ID/actions/set_channel_active \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"isActive":true}'{
"ok": true,
"result": "…",
"events": [
"retail.channel_saved"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.salesChannels.setChannelActive("SALES_CHANNEL_ID", {
isActive: true
})era sales_channels set_channel_active SALES_CHANNEL_ID \
--is-active true{
"name": "era__retail__set_channel_active",
"arguments": {
"id": "SALES_CHANNEL_ID",
"isActive": true
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 channel_not_found, channel_has_open_work · 429 slow down
retail.set_channel_warehouses
Choose a channel’s warehouses — Which warehouses a channel draws stock from, in the order stock is taken.
Needs retail:manage · changes data
https://app.getera.io/api/v1/sales_channel_warehouses/actions/set_channel_warehouses- Canonical address:
POST /api/v1/actions/retail.set_channel_warehouses
Input
| Field | Type | Required | Notes |
|---|---|---|---|
channelId | uuid | yes | Channel · id of channel |
warehouseIds | object / array | yes | Warehouses |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
channel_not_found | The channel must exist. |
warehouse_not_found | Every warehouse must exist. |
Events it publishes: retail.channel_warehouses_changed
What it writes: channel_warehouse (upsert) — Replaces the channel’s warehouse order
curl -X POST https://app.getera.io/api/v1/sales_channel_warehouses/actions/set_channel_warehouses \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"channelId":"CHANNEL_ID","warehouseIds":{}}'{
"ok": true,
"result": "…",
"events": [
"retail.channel_warehouses_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.salesChannelWarehouses.setChannelWarehouses({
channelId: "CHANNEL_ID",
warehouseIds: {}
})era sales_channel_warehouses set_channel_warehouses --data '{"channelId":"CHANNEL_ID","warehouseIds":{}}'{
"name": "era__retail__set_channel_warehouses",
"arguments": {
"channelId": "CHANNEL_ID",
"warehouseIds": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 channel_not_found, warehouse_not_found · 429 slow down
retail.set_checkout_delivery
Choose delivery — The address, the rate and, where offered, the collection point.
Needs retail:write · changes data
https://app.getera.io/api/v1/checkouts/actions/set_checkout_delivery- Canonical address:
POST /api/v1/actions/retail.set_checkout_delivery
Input
| Field | Type | Required | Notes |
|---|---|---|---|
token | uuid | yes | Basket |
shippingAddressId | uuid | Delivery address · id of address | |
billingAddressId | uuid | Billing address · id of address | |
shippingMethodId | uuid | Delivery rate · id of shipping_method | |
collectionPointId | uuid | Collect from · id of warehouse |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The basket must exist. |
shipping_method_not_available | The rate must serve this address at this order value. |
collection_point_unavailable | That location must offer collection. |
Events it publishes: retail.checkout_updated
What it writes: checkout (update) — Writes the delivery choice
curl -X POST https://app.getera.io/api/v1/checkouts/actions/set_checkout_delivery \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"token":"TOKEN_ID"}'{
"ok": true,
"result": "…",
"events": [
"retail.checkout_updated"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.checkouts.setCheckoutDelivery({
token: "TOKEN_ID"
})era checkouts set_checkout_delivery \
--token "TOKEN_ID"{
"name": "era__retail__set_checkout_delivery",
"arguments": {
"token": "TOKEN_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, shipping_method_not_available, collection_point_unavailable · 429 slow down
retail.set_checkout_lines
Change a basket — Add, change or remove lines. A quantity of zero removes the line.
Needs retail:write · changes data
https://app.getera.io/api/v1/checkout_lines/actions/set_checkout_lines- Canonical address:
POST /api/v1/actions/retail.set_checkout_lines
Input
| Field | Type | Required | Notes |
|---|---|---|---|
token | uuid | yes | Basket |
lines | object / array | yes | Lines |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The basket must exist. |
checkout_completing | A basket being turned into an order cannot be edited. |
malformed_lines | Every line needs a variant and a quantity. |
variant_not_priced | A variant with no price in this channel cannot be sold here. |
variant_not_found | Every variant must exist. |
reservation_stock_unavailable | Every tracked line must be reservable for fifteen minutes. |
gift_line_locked | A promotion’s gift line cannot be edited by hand. |
Events it publishes: retail.checkout_updated
What it writes: checkout_line (upsert) — Writes the basket lines; stock_reservation (upsert) — Replaces the basket stock hold
curl -X POST https://app.getera.io/api/v1/checkout_lines/actions/set_checkout_lines \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"token":"TOKEN_ID","lines":{}}'{
"ok": true,
"result": "…",
"events": [
"retail.checkout_updated"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.checkoutLines.setCheckoutLines({
token: "TOKEN_ID",
lines: {}
})era checkout_lines set_checkout_lines --data '{"token":"TOKEN_ID","lines":{}}'{
"name": "era__retail__set_checkout_lines",
"arguments": {
"token": "TOKEN_ID",
"lines": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, checkout_completing, malformed_lines, variant_not_priced, variant_not_found, reservation_stock_unavailable, gift_line_locked · 429 slow down
retail.set_collection_items
Choose what a collection holds — The whole membership, in the order the storefront draws it.
Needs retail:write · changes data
https://app.getera.io/api/v1/collection_items/actions/set_collection_items- Canonical address:
POST /api/v1/actions/retail.set_collection_items
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Collection · id of item_collection |
itemIds | object / array | yes | Items |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The collection must exist. |
item_not_found | Every catalogue entry must exist. |
collection_is_smart | A rule collection is not picked by hand. |
Events it publishes: retail.collection_items_changed
What it writes: item_collection_item (upsert) — Replaces the membership
curl -X POST https://app.getera.io/api/v1/collection_items/actions/set_collection_items \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"id":"ITEM_COLLECTION_ID","itemIds":{}}'{
"ok": true,
"result": "…",
"events": [
"retail.collection_items_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.collectionItems.setCollectionItems({
id: "ITEM_COLLECTION_ID",
itemIds: {}
})era collection_items set_collection_items --data '{"id":"ITEM_COLLECTION_ID","itemIds":{}}'{
"name": "era__retail__set_collection_items",
"arguments": {
"id": "ITEM_COLLECTION_ID",
"itemIds": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, item_not_found, collection_is_smart · 429 slow down
retail.set_collection_rule
Fill a collection from a rule — A smart collection: its members are whatever matches — a category, a brand, a price range, a name.
Needs retail:write · changes data
https://app.getera.io/api/v1/collections/{id}/actions/set_collection_rule- Canonical address:
POST /api/v1/actions/retail.set_collection_rule
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Collection · id of item_collection |
rule | object / array | Rule |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The collection must exist. |
invalid_rule | A rule has at least one condition and a sane price range. |
Events it publishes: retail.collection_items_changed
What it writes: item_collection (update) — Writes the rule; item_collection_item (upsert) — Replaces the membership
curl -X POST https://app.getera.io/api/v1/collections/COLLECTION_ID/actions/set_collection_rule \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"rule":{}}'{
"ok": true,
"result": "…",
"events": [
"retail.collection_items_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.collections.setCollectionRule("COLLECTION_ID", {
rule: {}
})era collections set_collection_rule COLLECTION_ID --data '{"rule":{}}'{
"name": "era__retail__set_collection_rule",
"arguments": {
"id": "COLLECTION_ID",
"rule": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, invalid_rule · 429 slow down
retail.set_item_listing
List something in a channel — Whether a catalogue entry is published, visible and on sale in one channel.
Needs retail:write · changes data
https://app.getera.io/api/v1/product_listings/actions/set_item_listing- Canonical address:
POST /api/v1/actions/retail.set_item_listing
Input
| Field | Type | Required | Notes |
|---|---|---|---|
itemId | uuid | yes | Item · id of item |
channelId | uuid | Channel · Empty = the shop. · id of channel | |
isPublished | boolean | Published | |
visibleInListings | boolean | Show in listings | |
availableForPurchaseAt | date-time | On sale from |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
channel_not_found | The channel must exist. |
item_not_found | The catalogue entry must exist. |
store_missing | With no channel given, the workspace must have an open shop (retail.open_shop). |
Events it publishes: retail.listing_changed
What it writes: item_channel_listing (upsert) — Writes the channel listing
curl -X POST https://app.getera.io/api/v1/product_listings/actions/set_item_listing \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"itemId":"ITEM_ID"}'{
"ok": true,
"result": "…",
"events": [
"retail.listing_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.productListings.setItemListing({
itemId: "ITEM_ID"
})era product_listings set_item_listing \
--item-id "ITEM_ID"{
"name": "era__retail__set_item_listing",
"arguments": {
"itemId": "ITEM_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 channel_not_found, item_not_found, store_missing · 429 slow down
retail.set_order_discount
Give a staff discount — Saleor’s orderDiscountAdd/Update: one percentage or amount off a draft or unconfirmed order, with a reason. The order-level coupon and order promotions step aside while it is on.
Needs retail:write · changes data
https://app.getera.io/api/v1/orders/{id}/actions/set_order_discount- Canonical address:
POST /api/v1/actions/retail.set_order_discount
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Order · id of sales_order |
valueType | string (enum) | yes | Off by · Allowed values: percentage, fixed |
value | number | yes | Value (percent, or minor units) · min 0 |
reason | string | Reason · max 500 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The order must exist. |
order_not_editable | Only a draft or unconfirmed order is discounted. |
invalid_discount_value | Above zero; a percentage at most 100, an amount at most the undiscounted total. |
Events it publishes: retail.order_discounts_applied
What it writes: sales_order (update) — Writes the discount and the new totals; sales_order_line (update) — Spreads the discount over the lines; sales_order_discount (insert) — Records what was applied
curl -X POST https://app.getera.io/api/v1/orders/ORDER_ID/actions/set_order_discount \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"valueType":"percentage","value":1}'{
"ok": true,
"result": "…",
"events": [
"retail.order_discounts_applied"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orders.setOrderDiscount("ORDER_ID", {
valueType: "percentage",
value: 1
})era orders set_order_discount ORDER_ID \
--value-type "percentage" \
--value 1{
"name": "era__retail__set_order_discount",
"arguments": {
"id": "ORDER_ID",
"valueType": "percentage",
"value": 1
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, order_not_editable, invalid_discount_value · 429 slow down
retail.set_order_lines
Change an order’s lines — Only while it is a draft or unconfirmed — the source’s ORDER_EDITABLE_STATUS.
Needs retail:write · removes or cannot be undone
https://app.getera.io/api/v1/orders/{id}/actions/set_order_lines- Canonical address:
POST /api/v1/actions/retail.set_order_lines
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Order · id of sales_order |
lines | object / array | yes | Lines |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The order must exist. |
order_not_editable | Only drafts and unconfirmed orders may change lines. |
malformed_lines | Every line needs a variant and a quantity. |
variant_not_found | Every variant must exist. |
variant_not_priced | A variant with no price needs one typed. |
Events it publishes: retail.order_lines_changed, retail.order_discounts_applied
What it writes: sales_order_line (upsert) — Replaces the lines, with promotions and the coupon applied; allocation (delete) — Releases what the old lines held; sales_order (update) — Recomputes the totals; sales_order_discount (insert) — Records what was applied
curl -X POST https://app.getera.io/api/v1/orders/ORDER_ID/actions/set_order_lines \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"lines":{}}'{
"ok": true,
"result": "…",
"events": [
"retail.order_lines_changed",
"retail.order_discounts_applied"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orders.setOrderLines("ORDER_ID", {
lines: {}
})era orders set_order_lines ORDER_ID --data '{"lines":{}}'{
"name": "era__retail__set_order_lines",
"arguments": {
"id": "ORDER_ID",
"lines": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, order_not_editable, malformed_lines, variant_not_found, variant_not_priced · 429 slow down
retail.set_order_voucher
Apply a coupon — Puts a voucher code on a draft or unconfirmed order, or takes it off. Order promotions step aside for a coupon.
Needs retail:write · changes data
https://app.getera.io/api/v1/orders/{id}/actions/set_order_voucher- Canonical address:
POST /api/v1/actions/retail.set_order_voucher
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Order · id of sales_order |
code | string | Coupon code · max 255 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The order must exist. |
order_not_editable | Only a draft or unconfirmed order changes its coupon. |
voucher_not_found | The code must name a voucher. |
voucher_not_applicable | The voucher must be running, priced in the channel and its conditions met. |
voucher_usage_exceeded | The code must not be used up. |
Events it publishes: retail.order_discounts_applied
What it writes: sales_order (update) — Writes the code and the new totals; sales_order_line (update) — Writes the per-line discount snapshot; sales_order_discount (insert) — Records what was applied
curl -X POST https://app.getera.io/api/v1/orders/ORDER_ID/actions/set_order_voucher \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"code":"Coupon code"}'{
"ok": true,
"result": "…",
"events": [
"retail.order_discounts_applied"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orders.setOrderVoucher("ORDER_ID", {
code: "Coupon code"
})era orders set_order_voucher ORDER_ID \
--code "Coupon code"{
"name": "era__retail__set_order_voucher",
"arguments": {
"id": "ORDER_ID",
"code": "Coupon code"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, order_not_editable, voucher_not_found, voucher_not_applicable, voucher_usage_exceeded · 429 slow down
retail.set_postal_code_rules
Set postal-code rules — Absence is permission; adding a rule restricts where a rate applies.
Needs retail:manage · changes data
https://app.getera.io/api/v1/shipping_method_postal_code_rules/actions/set_postal_code_rules- Canonical address:
POST /api/v1/actions/retail.set_postal_code_rules
Input
| Field | Type | Required | Notes |
|---|---|---|---|
shippingMethodId | uuid | yes | Rate · id of shipping_method |
rules | object / array | yes | Rules |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The rate must exist. |
malformed_rules | Every rule needs a starting code. |
duplicate_rule | The same range may not appear twice. |
Events it publishes: retail.shipping_rules_changed
What it writes: shipping_method_postal_code_rule (upsert) — Replaces the rate’s postal-code rules
curl -X POST https://app.getera.io/api/v1/shipping_method_postal_code_rules/actions/set_postal_code_rules \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"shippingMethodId":"SHIPPING_METHOD_ID","rules":{}}'{
"ok": true,
"result": "…",
"events": [
"retail.shipping_rules_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.shippingMethodPostalCodeRules.setPostalCodeRules({
shippingMethodId: "SHIPPING_METHOD_ID",
rules: {}
})era shipping_method_postal_code_rules set_postal_code_rules --data '{"shippingMethodId":"SHIPPING_METHOD_ID","rules":{}}'{
"name": "era__retail__set_postal_code_rules",
"arguments": {
"shippingMethodId": "SHIPPING_METHOD_ID",
"rules": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, malformed_rules, duplicate_rule · 429 slow down
retail.set_preorder
Sell on preorder — Take orders before the goods are on a shelf, up to a limit per channel and overall.
Needs retail:manage · changes data
https://app.getera.io/api/v1/variant_preorders/actions/set_preorder- Canonical address:
POST /api/v1/actions/retail.set_preorder
Input
| Field | Type | Required | Notes |
|---|---|---|---|
itemVariantId | uuid | yes | Variant · id of item_variant |
enabled | boolean | yes | On preorder |
endDate | date-time | Ends | |
globalThreshold | number | Limit, all channels · min 0 | |
channelThresholds | object / array | Limits per channel |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
variant_not_found | The variant must exist. |
invalid_preorder | A preorder ends in the future. |
preorder_has_orders | Orders holding preorders are moved by ending it, not by switching it off. |
channel_not_found | Every channel must exist. |
Events it publishes: retail.preorder_changed
What it writes: variant_preorder (upsert) — Writes the preorder; item_variant_channel_listing (upsert) — Writes the per-channel limit
curl -X POST https://app.getera.io/api/v1/variant_preorders/actions/set_preorder \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"itemVariantId":"ITEM_VARIANT_ID","enabled":true}'{
"ok": true,
"result": "…",
"events": [
"retail.preorder_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.variantPreorders.setPreorder({
itemVariantId: "ITEM_VARIANT_ID",
enabled: true
})era variant_preorders set_preorder \
--item-variant-id "ITEM_VARIANT_ID" \
--enabled true{
"name": "era__retail__set_preorder",
"arguments": {
"itemVariantId": "ITEM_VARIANT_ID",
"enabled": true
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 variant_not_found, invalid_preorder, preorder_has_orders, channel_not_found · 429 slow down
retail.set_shipping_excluded_items
Exclude products from a delivery rate — Saleor’s excluded products: an order holding any of them is not offered the rate.
Needs retail:manage · changes data
https://app.getera.io/api/v1/shipping_method_excluded_items/actions/set_shipping_excluded_items- Canonical address:
POST /api/v1/actions/retail.set_shipping_excluded_items
Input
| Field | Type | Required | Notes |
|---|---|---|---|
shippingMethodId | uuid | yes | Rate · id of shipping_method |
itemIds | object / array | yes | Products |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The rate must exist. |
item_not_found | Every product must exist. |
Events it publishes: retail.shipping_exclusions_changed
What it writes: shipping_method_excluded_item (upsert) — Replaces the rate’s exclusions
curl -X POST https://app.getera.io/api/v1/shipping_method_excluded_items/actions/set_shipping_excluded_items \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"shippingMethodId":"SHIPPING_METHOD_ID","itemIds":{}}'{
"ok": true,
"result": "…",
"events": [
"retail.shipping_exclusions_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.shippingMethodExcludedItems.setShippingExcludedItems({
shippingMethodId: "SHIPPING_METHOD_ID",
itemIds: {}
})era shipping_method_excluded_items set_shipping_excluded_items --data '{"shippingMethodId":"SHIPPING_METHOD_ID","itemIds":{}}'{
"name": "era__retail__set_shipping_excluded_items",
"arguments": {
"shippingMethodId": "SHIPPING_METHOD_ID",
"itemIds": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, item_not_found · 429 slow down
retail.set_shipping_price
Price a delivery rate in a channel — What delivery costs, and between which order values it applies.
Needs retail:manage · changes data
https://app.getera.io/api/v1/shipping_method_channel_listings/actions/set_shipping_price- Canonical address:
POST /api/v1/actions/retail.set_shipping_price
Input
| Field | Type | Required | Notes |
|---|---|---|---|
shippingMethodId | uuid | yes | Rate · id of shipping_method |
channelId | uuid | yes | Channel · id of channel |
priceAmount | number (money) | yes | Price (minor units) · min 0 |
minimumOrderPriceAmount | number (money) | From order value · min 0 | |
maximumOrderPriceAmount | number (money) | To order value · min 0 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
channel_not_found | The channel must exist. |
not_found | The rate must exist. |
range_inverted | The smallest order value must not exceed the largest. |
Events it publishes: retail.shipping_price_changed
What it writes: shipping_method_channel_listing (upsert) — Writes the channel price
curl -X POST https://app.getera.io/api/v1/shipping_method_channel_listings/actions/set_shipping_price \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"shippingMethodId":"SHIPPING_METHOD_ID","channelId":"CHANNEL_ID","priceAmount":1}'{
"ok": true,
"result": "…",
"events": [
"retail.shipping_price_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.shippingMethodChannelListings.setShippingPrice({
shippingMethodId: "SHIPPING_METHOD_ID",
channelId: "CHANNEL_ID",
priceAmount: 1
})era shipping_method_channel_listings set_shipping_price \
--shipping-method-id "SHIPPING_METHOD_ID" \
--channel-id "CHANNEL_ID" \
--price-amount 1{
"name": "era__retail__set_shipping_price",
"arguments": {
"shippingMethodId": "SHIPPING_METHOD_ID",
"channelId": "CHANNEL_ID",
"priceAmount": 1
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 channel_not_found, not_found, range_inverted · 429 slow down
retail.set_stock
Count stock — The number on a shelf, in one warehouse, for one variant.
Needs retail:write · changes data
https://app.getera.io/api/v1/stocks/actions/set_stock- Canonical address:
POST /api/v1/actions/retail.set_stock
Input
| Field | Type | Required | Notes |
|---|---|---|---|
warehouseId | uuid | yes | Warehouse · id of warehouse |
itemVariantId | uuid | yes | Variant · id of item_variant |
quantity | number | yes | Quantity |
note | string | Note · max 500 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
warehouse_not_found | The warehouse must exist. |
variant_not_found | The variant must exist. |
below_allocated | The count may not drop below what is already promised. |
Events it publishes: retail.stock_changed, retail.stock_low
What it writes: stock (upsert) — Writes the count; stock_movement (insert) — Records the stocktake difference in the ledger
curl -X POST https://app.getera.io/api/v1/stocks/actions/set_stock \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"warehouseId":"WAREHOUSE_ID","itemVariantId":"ITEM_VARIANT_ID","quantity":1}'{
"ok": true,
"result": "…",
"events": [
"retail.stock_changed",
"retail.stock_low"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.stocks.setStock({
warehouseId: "WAREHOUSE_ID",
itemVariantId: "ITEM_VARIANT_ID",
quantity: 1
})era stocks set_stock \
--warehouse-id "WAREHOUSE_ID" \
--item-variant-id "ITEM_VARIANT_ID" \
--quantity 1{
"name": "era__retail__set_stock",
"arguments": {
"warehouseId": "WAREHOUSE_ID",
"itemVariantId": "ITEM_VARIANT_ID",
"quantity": 1
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 warehouse_not_found, variant_not_found, below_allocated · 429 slow down
retail.set_stock_policy
Hold back safety stock — How many on this shelf are never promised to an order.
Needs retail:manage · changes data
https://app.getera.io/api/v1/stocks/actions/set_stock_policy- Canonical address:
POST /api/v1/actions/retail.set_stock_policy
Input
| Field | Type | Required | Notes |
|---|---|---|---|
warehouseId | uuid | yes | Warehouse · id of warehouse |
itemVariantId | uuid | yes | Variant · id of item_variant |
safetyStock | number | yes | Safety stock · min 0 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
stock_not_found | The warehouse must keep the variant. |
Events it publishes: retail.stock_policy_changed, retail.stock_low
What it writes: stock (update) — Writes the safety stock
curl -X POST https://app.getera.io/api/v1/stocks/actions/set_stock_policy \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"warehouseId":"WAREHOUSE_ID","itemVariantId":"ITEM_VARIANT_ID","safetyStock":1}'{
"ok": true,
"result": "…",
"events": [
"retail.stock_policy_changed",
"retail.stock_low"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.stocks.setStockPolicy({
warehouseId: "WAREHOUSE_ID",
itemVariantId: "ITEM_VARIANT_ID",
safetyStock: 1
})era stocks set_stock_policy \
--warehouse-id "WAREHOUSE_ID" \
--item-variant-id "ITEM_VARIANT_ID" \
--safety-stock 1{
"name": "era__retail__set_stock_policy",
"arguments": {
"warehouseId": "WAREHOUSE_ID",
"itemVariantId": "ITEM_VARIANT_ID",
"safetyStock": 1
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 stock_not_found · 429 slow down
retail.set_tracking
Set a tracking number — What the customer follows the parcel with.
Needs retail:handle · changes data
https://app.getera.io/api/v1/fulfilments/{id}/actions/set_tracking- Canonical address:
POST /api/v1/actions/retail.set_tracking
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Despatch · id of fulfilment |
trackingNumber | string | yes | Tracking number · max 255 |
carrier | string | Carrier · max 120 | |
trackingUrl | string | Tracking link · max 1000 | |
notifyCustomer | boolean | Tell the customer |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The despatch must exist. |
Events it publishes: retail.fulfilment_tracking_updated, retail.customer_notification_requested
What it writes: fulfilment (update) — Writes the carrier, tracking number and link
curl -X POST https://app.getera.io/api/v1/fulfilments/FULFILMENT_ID/actions/set_tracking \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"trackingNumber":"Tracking number"}'{
"ok": true,
"result": "…",
"events": [
"retail.fulfilment_tracking_updated",
"retail.customer_notification_requested"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.fulfilments.setTracking("FULFILMENT_ID", {
trackingNumber: "Tracking number"
})era fulfilments set_tracking FULFILMENT_ID \
--tracking-number "Tracking number"{
"name": "era__retail__set_tracking",
"arguments": {
"id": "FULFILMENT_ID",
"trackingNumber": "Tracking number"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found · 429 slow down
retail.set_variant_price
Price a variant in a channel — The channel override. Without one, the catalogue list price applies.
Needs retail:write · changes data
https://app.getera.io/api/v1/variant_listings/actions/set_variant_price- Canonical address:
POST /api/v1/actions/retail.set_variant_price
Input
| Field | Type | Required | Notes |
|---|---|---|---|
itemVariantId | uuid | yes | Variant · id of item_variant |
channelId | uuid | yes | Channel · id of channel |
priceAmount | number (money) | Price (minor units) · min 0 | |
costPriceAmount | number (money) | Cost (minor units) · min 0 | |
priorPriceAmount | number (money) | Was (minor units) · min 0 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
channel_not_found | The channel must exist. |
variant_not_found | The variant must exist. |
Events it publishes: retail.channel_price_changed
What it writes: item_variant_channel_listing (upsert) — Writes the price override
curl -X POST https://app.getera.io/api/v1/variant_listings/actions/set_variant_price \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"itemVariantId":"ITEM_VARIANT_ID","channelId":"CHANNEL_ID"}'{
"ok": true,
"result": "…",
"events": [
"retail.channel_price_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.variantListings.setVariantPrice({
itemVariantId: "ITEM_VARIANT_ID",
channelId: "CHANNEL_ID"
})era variant_listings set_variant_price \
--item-variant-id "ITEM_VARIANT_ID" \
--channel-id "CHANNEL_ID"{
"name": "era__retail__set_variant_price",
"arguments": {
"itemVariantId": "ITEM_VARIANT_ID",
"channelId": "CHANNEL_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 channel_not_found, variant_not_found · 429 slow down
retail.set_warehouse_zones
Choose which zones a warehouse serves — A warehouse only ships to the countries its zones cover.
Needs retail:manage · changes data
https://app.getera.io/api/v1/warehouse_shipping_zones/actions/set_warehouse_zones- Canonical address:
POST /api/v1/actions/retail.set_warehouse_zones
Input
| Field | Type | Required | Notes |
|---|---|---|---|
warehouseId | uuid | yes | Warehouse · id of warehouse |
shippingZoneIds | object / array | yes | Shipping zones |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The warehouse must exist. |
zone_not_found | Every shipping zone must exist. |
Events it publishes: retail.warehouse_zones_changed
What it writes: warehouse_shipping_zone (upsert) — Replaces the zone assignment
curl -X POST https://app.getera.io/api/v1/warehouse_shipping_zones/actions/set_warehouse_zones \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"warehouseId":"WAREHOUSE_ID","shippingZoneIds":{}}'{
"ok": true,
"result": "…",
"events": [
"retail.warehouse_zones_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.warehouseShippingZones.setWarehouseZones({
warehouseId: "WAREHOUSE_ID",
shippingZoneIds: {}
})era warehouse_shipping_zones set_warehouse_zones --data '{"warehouseId":"WAREHOUSE_ID","shippingZoneIds":{}}'{
"name": "era__retail__set_warehouse_zones",
"arguments": {
"warehouseId": "WAREHOUSE_ID",
"shippingZoneIds": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, zone_not_found · 429 slow down
retail.site_cart_add
Add to basket (site) — A visitor puts a product in their basket; the basket opens in the site’s channel if they have none.
Needs retail:write · changes data · Also callable from a published site (visitor)
/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).https://app.getera.io/api/v1/checkouts/actions/site_cart_add- Canonical address:
POST /api/v1/actions/retail.site_cart_add
Input
| Field | Type | Required | Notes |
|---|---|---|---|
token | uuid | Basket | |
itemVariantId | uuid | yes | Variant · id of item_variant |
quantity | number | Quantity |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
shop_closed | The site’s workspace has an open sales channel. |
not_for_sale | Only a public product listed in the site’s channel can be bought. |
bad_quantity | A quantity is a whole number from 0 to 99. |
Events it publishes: retail.checkout_created, retail.checkout_updated
What it writes: checkout (insert) — Opens the basket when there is none; checkout_line (upsert) — Adds the quantity to the line; stock_reservation (upsert) — Holds tracked stock for fifteen minutes
Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 shop_closed, not_for_sale, bad_quantity · 429 slow down
retail.site_cart_claim
Keep my basket after signing in — The guest basket becomes the signed-in customer’s, with the lines of any basket they left before.
Needs retail:write · removes or cannot be undone · Also callable from a published site (customer)
/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).https://app.getera.io/api/v1/checkout_lines/actions/site_cart_claim- Canonical address:
POST /api/v1/actions/retail.site_cart_claim
Input
| Field | Type | Required | Notes |
|---|---|---|---|
token | uuid | yes | Basket |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
basket_not_found | The basket must exist and not be someone else’s. |
Events it publishes: retail.checkout_updated
What it writes: checkout (update) — Puts the customer on the basket; checkout_line (upsert) — Folds in the older basket’s lines; checkout (delete) — Removes the older basket
Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 basket_not_found · 429 slow down
retail.site_cart_set
Change a basket line (site) — A visitor changes the quantity of a line in their basket; 0 removes it.
Needs retail:write · changes data · Also callable from a published site (visitor)
/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).https://app.getera.io/api/v1/checkout_lines/actions/site_cart_set- Canonical address:
POST /api/v1/actions/retail.site_cart_set
Input
| Field | Type | Required | Notes |
|---|---|---|---|
token | uuid | yes | Basket |
itemVariantId | uuid | yes | Variant · id of item_variant |
quantity | number | yes | Quantity |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
basket_not_found | The basket must exist and be the caller’s. |
checkout_completing | A basket being turned into an order does not change. |
bad_quantity | A quantity is a whole number from 0 to 99. |
not_for_sale | Only a public product listed in the site’s channel can be bought. |
Events it publishes: retail.checkout_updated
What it writes: checkout_line (upsert) — Sets or removes the line; stock_reservation (upsert) — Re-holds the basket’s stock
Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 basket_not_found, checkout_completing, bad_quantity, not_for_sale · 429 slow down
retail.site_checkout_delivery
Choose delivery (site) — A site basket picks a delivery rate that serves its address, or a pickup point of the channel.
Needs retail:write · changes data · Also callable from a published site (visitor)
/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).https://app.getera.io/api/v1/checkouts/actions/site_checkout_delivery- Canonical address:
POST /api/v1/actions/retail.site_checkout_delivery
Input
| Field | Type | Required | Notes |
|---|---|---|---|
token | uuid | yes | Basket |
shippingMethodId | uuid | Delivery rate · id of shipping_method | |
pickupPointId | uuid | Collect from · id of warehouse |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
basket_not_found | The basket must exist and be the caller’s. |
checkout_completing | A basket being turned into an order does not change. |
delivery_missing | A rate or a pickup point is chosen. |
collection_point_unavailable | The pickup point collects for the site’s channel. |
Events it publishes: retail.checkout_updated
What it writes: checkout (update) — Writes the delivery and its charge
Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 basket_not_found, checkout_completing, delivery_missing, collection_point_unavailable · 429 slow down
retail.site_checkout_details
Give checkout details (site) — The email, the customer and the delivery address of a site basket; answers the delivery rates and pickup points that apply.
Needs retail:write · changes data · Also callable from a published site (visitor)
/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).https://app.getera.io/api/v1/checkouts/actions/site_checkout_details- Canonical address:
POST /api/v1/actions/retail.site_checkout_details
Input
| Field | Type | Required | Notes |
|---|---|---|---|
token | uuid | yes | Basket |
email | string | yes | Email · max 320 |
customerId | uuid | yes | Customer · id of customer |
addressId | uuid | Delivery address · id of address | |
note | string | Note · max 1000 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
basket_not_found | The basket must exist and be the caller’s. |
checkout_completing | A basket being turned into an order does not change. |
invalid_email | The email must be usable. |
customer_mismatch | A guest is the record of the email they typed; a customer is themself. |
address_not_found | The address is the snapshot the checkout just wrote. |
Events it publishes: retail.checkout_updated
What it writes: checkout (update) — Writes the email, the customer and the address
Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 basket_not_found, checkout_completing, invalid_email, customer_mismatch, address_not_found · 429 slow down
retail.site_checkout_voucher
Use a coupon (site) — A site basket takes a coupon code — judged by the discounts rules on its lines — or drops it; the basket shows what comes off and the order carries it.
Needs retail:write · changes data · Also callable from a published site (visitor)
/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).https://app.getera.io/api/v1/checkouts/actions/site_checkout_voucher- Canonical address:
POST /api/v1/actions/retail.site_checkout_voucher
Input
| Field | Type | Required | Notes |
|---|---|---|---|
token | uuid | yes | Basket |
code | string | Coupon code · max 255 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
basket_not_found | The basket must exist and be the caller’s. |
checkout_completing | A basket being turned into an order does not change. |
voucher_not_found | The code must name a voucher. |
voucher_not_applicable | The voucher must be running, priced in the channel and its conditions met by the basket. |
voucher_usage_exceeded | The code must not be used up. |
Events it publishes: retail.checkout_updated
What it writes: checkout (update) — Writes the code, the discount and the new total
Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 basket_not_found, checkout_completing, voucher_not_found, voucher_not_applicable, voucher_usage_exceeded · 429 slow down
retail.site_place_order
Place an order (site) — A site basket becomes an order awaiting payment by the chosen method; stock is allocated and the confirmation email is asked for.
Needs retail:write · removes or cannot be undone · Also callable from a published site (visitor)
/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).https://app.getera.io/api/v1/orders/actions/site_place_order- Canonical address:
POST /api/v1/actions/retail.site_place_order
Input
| Field | Type | Required | Notes |
|---|---|---|---|
token | uuid | yes | Basket |
paymentMethod | string | Payment method · Empty when a gift card on the basket pays it all. · max 40 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
basket_not_found | The basket must exist and be the caller’s. |
checkout_completing | A basket is turned into an order once. |
payment_method_unavailable | The method is a connected payment app, or the test method outside production. |
details_missing | The basket has an email and a customer. |
voucher_usage_exceeded | A coupon on the basket must still be usable when the order is placed. |
Events it publishes: retail.order_placed, retail.customer_notification_requested, retail.stock_low, retail.order_discounts_applied
What it writes: sales_order (insert) — The order; sales_order_line (insert) — Its lines, frozen; allocation (insert) — The stock it holds; checkout (delete) — The basket is gone
Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 basket_not_found, checkout_completing, payment_method_unavailable, details_missing, voucher_usage_exceeded · 429 slow down
retail.sync_charge_status
Update what an order has been paid — Machine-only (finance-reactions.ts): money moved on the order, so its payment status follows.
Needs retail:write · changes data
403 machine_only).https://app.getera.io/api/v1/orders/{id}/actions/sync_charge_status- Canonical address:
POST /api/v1/actions/retail.sync_charge_status
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Order · id of sales_order |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The order must exist. |
Events it publishes: retail.order_charge_status_changed
What it writes: sales_order (update) — Writes the payment status and the total charged
Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found · 429 slow down
retail.transfer_stock
Move stock between warehouses — Two ledger rows, one move. What orders are holding stays where it is.
Needs retail:write · changes data
https://app.getera.io/api/v1/stocks/actions/transfer_stock- Canonical address:
POST /api/v1/actions/retail.transfer_stock
Input
| Field | Type | Required | Notes |
|---|---|---|---|
fromWarehouseId | uuid | yes | From · id of warehouse |
toWarehouseId | uuid | yes | To · id of warehouse |
itemVariantId | uuid | yes | Variant · id of item_variant |
quantity | number | yes | Quantity · min 1 |
note | string | Note · max 500 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
no_change | A transfer moves at least one. |
same_warehouse | The two warehouses must differ. |
warehouse_not_found | Both warehouses must exist. |
variant_not_found | The variant must exist. |
insufficient_stock | The source must have that many free. |
Events it publishes: retail.stock_transferred, retail.stock_low
What it writes: stock (upsert) — Moves the count; stock_movement (insert) — Writes both sides of the move
curl -X POST https://app.getera.io/api/v1/stocks/actions/transfer_stock \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"fromWarehouseId":"WAREHOUSE_ID","toWarehouseId":"WAREHOUSE_ID","itemVariantId":"ITEM_VARIANT_ID","quantity":1}'{
"ok": true,
"result": "…",
"events": [
"retail.stock_transferred",
"retail.stock_low"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.stocks.transferStock({
fromWarehouseId: "WAREHOUSE_ID",
toWarehouseId: "WAREHOUSE_ID",
itemVariantId: "ITEM_VARIANT_ID",
quantity: 1
})era stocks transfer_stock \
--from-warehouse-id "WAREHOUSE_ID" \
--to-warehouse-id "WAREHOUSE_ID" \
--item-variant-id "ITEM_VARIANT_ID" \
--quantity 1{
"name": "era__retail__transfer_stock",
"arguments": {
"fromWarehouseId": "WAREHOUSE_ID",
"toWarehouseId": "WAREHOUSE_ID",
"itemVariantId": "ITEM_VARIANT_ID",
"quantity": 1
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 no_change, same_warehouse, warehouse_not_found, variant_not_found, insufficient_stock · 429 slow down
retail.update_order
Edit an order — Its note, email, addresses and — while still editable — its delivery rate.
Needs retail:write · changes data
https://app.getera.io/api/v1/orders/{id}/actions/update_order- REST alias:
PATCH /api/v1/orders/{id} - Canonical address:
POST /api/v1/actions/retail.update_order
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Order · id of sales_order |
customerId | uuid | Customer · id of customer | |
customerNote | string | Note · max 2000 | |
email | |||
billingAddressId | uuid | Billing address · id of address | |
shippingAddressId | uuid | Delivery address · id of address | |
shippingMethodId | uuid | Delivery rate · id of shipping_method | |
fulfilmentMethod | string (enum) | Delivery · Allowed values: shipping, pickup |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The order must exist. |
customer_not_found | The customer must exist and be active. |
order_not_editable | A despatched order cannot change its delivery method. |
shipping_method_not_available | The rate must be priced in this order’s channel. |
Events it publishes: retail.order_updated, retail.order_discounts_applied
What it writes: sales_order (update) — Edits the order; sales_order_line (update) — Re-applies discounts while editable; sales_order_discount (insert) — Records what was applied
curl -X POST https://app.getera.io/api/v1/orders/ORDER_ID/actions/update_order \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"customerId":"CUSTOMER_ID"}'{
"ok": true,
"result": "…",
"events": [
"retail.order_updated",
"retail.order_discounts_applied"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orders.updateOrder("ORDER_ID", {
customerId: "CUSTOMER_ID"
})era orders update_order ORDER_ID \
--customer-id "CUSTOMER_ID"{
"name": "era__retail__update_order",
"arguments": {
"id": "ORDER_ID",
"customerId": "CUSTOMER_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, customer_not_found, order_not_editable, shipping_method_not_available · 429 slow down
retail.update_order_metadata
Edit order metadata — Replaces the order’s key → value metadata, public or private.
Needs retail:write · changes data
https://app.getera.io/api/v1/orders/{id}/actions/update_order_metadata- Canonical address:
POST /api/v1/actions/retail.update_order_metadata
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Order · id of sales_order |
metadata | object / array | Metadata | |
privateMetadata | object / array | Private metadata |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The order must exist. |
Events it publishes: retail.order_updated
What it writes: sales_order (update) — Writes the metadata
curl -X POST https://app.getera.io/api/v1/orders/ORDER_ID/actions/update_order_metadata \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"metadata":{}}'{
"ok": true,
"result": "…",
"events": [
"retail.order_updated"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.orders.updateOrderMetadata("ORDER_ID", {
metadata: {}
})era orders update_order_metadata ORDER_ID --data '{"metadata":{}}'{
"name": "era__retail__update_order_metadata",
"arguments": {
"id": "ORDER_ID",
"metadata": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found · 429 slow down
retail.update_warehouse
Edit a warehouse — Its name, address, contact and whether customers may collect there.
Needs retail:manage · changes data
https://app.getera.io/api/v1/warehouses/{id}/actions/update_warehouse- REST alias:
PATCH /api/v1/warehouses/{id} - Canonical address:
POST /api/v1/actions/retail.update_warehouse
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Warehouse · id of warehouse |
name | string | Name · max 250 | |
addressId | uuid | Address · id of address | |
streetAddress1 | string | Street · max 250 | |
streetAddress2 | string | Street (second line) · max 250 | |
cityArea | string | District · max 120 | |
city | string | City · max 120 | |
countryArea | string | Region · max 120 | |
postalCode | string | Postal code · max 32 | |
country | string | Country · max 2 | |
phone | phone | Phone | |
email | |||
clickAndCollectOption | string (enum) | Collection · Allowed values: disabled, local, all | |
isPrivate | boolean | Hidden from customers |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The warehouse must exist. |
address_not_found | The address must exist. |
Events it publishes: retail.warehouse_saved
What it writes: warehouse (update) — Edits the warehouse
curl -X POST https://app.getera.io/api/v1/warehouses/WAREHOUSE_ID/actions/update_warehouse \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"name":"Name"}'{
"ok": true,
"result": "…",
"events": [
"retail.warehouse_saved"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.warehouses.updateWarehouse("WAREHOUSE_ID", {
name: "Name"
})era warehouses update_warehouse WAREHOUSE_ID \
--name "Name"{
"name": "era__retail__update_warehouse",
"arguments": {
"id": "WAREHOUSE_ID",
"name": "Name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold retail:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, address_not_found · 429 slow down
Data models
Allocation
Table allocation · Resource allocations
GET /api/v1/allocationsGET /api/v1/allocations/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
order_line_id | string (uuid) · uuid | sales_order_line | ||
stock_id | string (uuid) · uuid | stock | ||
quantity_allocated | integer · int4 | |||
created_at | string (date-time) · timestamptz |
Primary key: id
Unique: order_line_id, stock_id
Checks:
CHECK ((quantity_allocated >= 0))
CheckoutLine
Table checkout_line · Resource checkout_lines
GET /api/v1/checkout_linesGET /api/v1/checkout_lines/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
item_variant_id | string (uuid) · uuid | item_variant | ||
quantity | integer · int4 | |||
is_gift | boolean · bool | |||
price_override | string (int64) · int8 | yes | ||
currency | string · bpchar | max 3 | ||
undiscounted_unit_amount | string (int64) · int8 | |||
total_amount | string (int64) · int8 | |||
metadata | object · jsonb | |||
created_at | string (date-time) · timestamptz |
Primary key: id
Unique: checkout_token, item_variant_id
Checks:
CHECK ((currency ~ '^[A-Z]{3}$'::text))CHECK (((price_override IS NULL) OR (price_override >= 0)))CHECK ((quantity >= 1))CHECK ((total_amount >= 0))CHECK ((undiscounted_unit_amount >= 0))
Checkout
Table checkout · Resource checkouts
GET /api/v1/checkoutsGET /api/v1/checkouts/{id}POST /api/v1/checkouts→retail.create_checkout
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
channel_id | string (uuid) · uuid | channel | ||
currency | string · bpchar | max 3 | ||
country | string · bpchar | yes | max 2 | |
language_code | string · text | yes | ||
customer_id | string (uuid) · uuid | yes | customer | |
email | string · text | yes | ||
note | string · text | |||
billing_address_id | string (uuid) · uuid | yes | address | |
shipping_address_id | string (uuid) · uuid | yes | address | |
save_billing_address | boolean · bool | |||
save_shipping_address | boolean · bool | |||
shipping_method_id | string (uuid) · uuid | yes | shipping_method | |
shipping_method_name | string · text | yes | ||
collection_point_id | string (uuid) · uuid | yes | warehouse | |
delivery_methods_stale_at | string (date-time) · timestamptz | yes | ||
subtotal_amount | string (int64) · int8 | |||
shipping_amount | string (int64) · int8 | |||
discount_amount | string (int64) · int8 | |||
total_amount | string (int64) · int8 | |||
discount_name | string · text | yes | ||
authorize_status | string · text | none, partial, full | ||
charge_status | string · text | none, partial, full, overcharged | ||
automatically_refundable | boolean · bool | |||
voucher_code | string · text | yes | ||
is_voucher_usage_increased | boolean · bool | |||
price_expiration | string (date-time) · timestamptz | |||
discount_expiration | string (date-time) · timestamptz | |||
completing_started_at | string (date-time) · timestamptz | yes | ||
redirect_url | string · text | yes | ||
tracking_code | string · text | yes | ||
metadata | object · jsonb | |||
created_at | string (date-time) · timestamptz | |||
last_change | string (date-time) · timestamptz | |||
abandoned_notice_at | string (date-time) · timestamptz | yes |
Primary key: token
Checks:
CHECK ((currency ~ '^[A-Z]{3}$'::text))CHECK ((discount_amount >= 0))CHECK ((shipping_amount >= 0))CHECK ((subtotal_amount >= 0))CHECK ((total_amount >= 0))
ItemCollectionItem
Table item_collection_item · Resource collection_items
GET /api/v1/collection_itemsGET /api/v1/collection_items/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
collection_id | string (uuid) · uuid | item_collection | ||
item_id | string (uuid) · uuid | item | ||
sort_order | integer · int4 | |||
created_at | string (date-time) · timestamptz |
Primary key: id
Unique: collection_id, item_id
ItemCollection
Table item_collection · Resource collections
GET /api/v1/collectionsGET /api/v1/collections/{id}PATCH /api/v1/collections/{id}→retail.save_collectionDELETE /api/v1/collections/{id}→retail.delete_collection
| 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 | |||
slug | string · text | |||
description | string · text | yes | ||
background_file_id | string (uuid) · uuid | yes | file | |
background_alt | string · text | yes | ||
seo_title | string · text | yes | ||
seo_description | string · text | yes | ||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz | |||
rule | object · jsonb | yes |
Primary key: id
FulfilmentLine
Table fulfilment_line · Resource fulfilment_lines
GET /api/v1/fulfilment_linesGET /api/v1/fulfilment_lines/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
fulfilment_id | string (uuid) · uuid | fulfilment | ||
order_line_id | string (uuid) · uuid | sales_order_line | ||
quantity | integer · int4 | |||
stock_id | string (uuid) · uuid | yes | stock | |
created_at | string (date-time) · timestamptz |
Primary key: id
Checks:
CHECK ((quantity >= 1))
Fulfilment
Table fulfilment · Resource fulfilments
GET /api/v1/fulfilmentsGET /api/v1/fulfilments/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
order_id | string (uuid) · uuid | sales_order | ||
sequence | integer · int4 | |||
status | string · text | fulfilled, refunded, returned, replaced, refunded_and_returned, canceled, waiting_for_approval | ||
tracking_number | string · text | |||
currency | string · bpchar | yes | max 3 | |
shipping_refund_amount | string (int64) · int8 | yes | ||
total_refund_amount | string (int64) · int8 | yes | ||
metadata | object · jsonb | |||
created_by_user_id | string (uuid) · uuid | yes | app_user | |
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz | |||
carrier | string · text | yes | ||
tracking_url | string · text | yes | ||
customer_notified_at | string (date-time) · timestamptz | yes |
Primary key: id
Unique: order_id, sequence
Checks:
CHECK (((currency IS NULL) OR (currency ~ '^[A-Z]{3}$'::text)))CHECK ((sequence >= 1))CHECK (((shipping_refund_amount IS NULL) OR (shipping_refund_amount >= 0)))CHECK (((total_refund_amount IS NULL) OR (total_refund_amount >= 0)))
ItemCollectionChannelListing
Table item_collection_channel_listing · Resource item_collection_channel_listings
GET /api/v1/item_collection_channel_listingsGET /api/v1/item_collection_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 | ||
collection_id | string (uuid) · uuid | item_collection | ||
channel_id | string (uuid) · uuid | channel | ||
is_published | boolean · bool | |||
published_at | string (date-time) · timestamptz | yes |
Primary key: id
Unique: collection_id, channel_id
SalesOrderDiscount
Table sales_order_discount · Resource order_discounts
GET /api/v1/order_discountsGET /api/v1/order_discounts/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
order_id | string (uuid) · uuid | sales_order | ||
order_line_id | string (uuid) · uuid | yes | sales_order_line | |
type | string · text | promotion, order_promotion, voucher, manual | ||
scope | string · text | order, line, shipping | ||
value_type | string · text | fixed, percentage | ||
value | string (decimal) · numeric | |||
amount | string (int64) · int8 | |||
currency | string · bpchar | max 3 | ||
name | string · text | yes | ||
reason | string · text | yes | ||
promotion_rule_id | string (uuid) · uuid | yes | ||
voucher_id | string (uuid) · uuid | yes | ||
voucher_code | string · text | yes | ||
created_at | string (date-time) · timestamptz |
Primary key: id
Checks:
CHECK ((amount >= 0))CHECK (((scope = 'line'::text) = (order_line_id IS NOT NULL)))CHECK ((currency ~ '^[A-Z]{3}$'::text))CHECK ((value >= (0)::numeric))
SalesOrderGiftCard
Table sales_order_gift_card · Resource order_gift_cards
GET /api/v1/order_gift_cardsGET /api/v1/order_gift_cards/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
order_id | string (uuid) · uuid | sales_order | ||
gift_card_id | string (uuid) · uuid | yes | ||
gift_card_event_id | string (uuid) · uuid | yes | ||
code_tail | string · text | |||
amount | string (int64) · int8 | |||
currency | string · bpchar | max 3 | ||
idempotency_key | string · text | |||
created_at | string (date-time) · timestamptz | |||
status | string · text | pending, applied, failed | ||
pending_code | string · text | yes | ||
failure_reason | string · text | yes | ||
resolved_at | string (date-time) · timestamptz | yes |
Primary key: id
Unique: workspace_id, idempotency_key
Checks:
CHECK ((amount > 0))CHECK (((status <> 'applied'::text) OR (gift_card_id IS NOT NULL)))CHECK ((currency ~ '^[A-Z]{3}$'::text))CHECK (((status <> 'pending'::text) OR (pending_code IS NOT NULL)))
OrderGrantedRefundLine
Table order_granted_refund_line · Resource order_granted_refund_lines
GET /api/v1/order_granted_refund_linesGET /api/v1/order_granted_refund_lines/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
granted_refund_id | string (uuid) · uuid | order_granted_refund | ||
order_line_id | string (uuid) · uuid | sales_order_line | ||
quantity | integer · int4 | |||
reason | string · text | yes |
Primary key: id
Unique: granted_refund_id, order_line_id
Checks:
CHECK ((quantity > 0))
OrderGrantedRefund
Table order_granted_refund · Resource order_granted_refunds
GET /api/v1/order_granted_refundsGET /api/v1/order_granted_refunds/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
order_id | string (uuid) · uuid | sales_order | ||
return_id | string (uuid) · uuid | yes | order_return | |
amount | string (int64) · int8 | |||
currency | string · bpchar | max 3 | ||
reason | string · text | yes | ||
shipping_costs_included | boolean · bool | |||
created_by_user_id | string (uuid) · uuid | yes | app_user | |
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Checks:
CHECK ((amount > 0))CHECK ((currency ~ '^[A-Z]{3}$'::text))
SalesOrderLine
Table sales_order_line · Resource order_lines
GET /api/v1/order_linesGET /api/v1/order_lines/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
order_id | string (uuid) · uuid | sales_order | ||
item_id | string (uuid) · uuid | yes | item | |
item_variant_id | string (uuid) · uuid | yes | item_variant | |
item_name | string · text | |||
variant_name | string · text | |||
sku | string · text | yes | ||
is_shipping_required | boolean · bool | |||
quantity | integer · int4 | |||
quantity_fulfilled | integer · int4 | |||
is_gift | boolean · bool | |||
currency | string · bpchar | max 3 | ||
unit_amount | string (int64) · int8 | |||
total_amount | string (int64) · int8 | |||
undiscounted_unit_amount | string (int64) · int8 | |||
undiscounted_total_amount | string (int64) · int8 | |||
unit_discount_amount | string (int64) · int8 | |||
unit_discount_type | string · text | yes | fixed, percentage | |
unit_discount_reason | string · text | yes | ||
is_price_overridden | boolean · bool | yes | ||
voucher_code | string · text | yes | ||
draft_base_price_expire_at | string (date-time) · timestamptz | yes | ||
display_order | integer · int4 | |||
metadata | object · jsonb | |||
created_at | string (date-time) · timestamptz | |||
tax_class_id | string (uuid) · uuid | yes | ||
tax_class_name | string · text | yes | ||
tax_rate | string (decimal) · numeric | yes | ||
tax_amount | string (int64) · int8 | |||
tax_included | boolean · bool | |||
order_discount_amount | string (int64) · int8 |
Primary key: id
Checks:
CHECK ((quantity_fulfilled <= quantity))CHECK ((currency ~ '^[A-Z]{3}$'::text))CHECK ((order_discount_amount >= 0))CHECK ((quantity >= 1))CHECK ((quantity_fulfilled >= 0))CHECK ((tax_amount >= 0))CHECK ((total_amount >= 0))CHECK ((undiscounted_total_amount >= 0))CHECK ((undiscounted_unit_amount >= 0))CHECK ((unit_amount >= 0))CHECK ((unit_discount_amount >= 0))
OrderReturnLine
Table order_return_line · Resource order_return_lines
GET /api/v1/order_return_linesGET /api/v1/order_return_lines/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
return_id | string (uuid) · uuid | order_return | ||
order_line_id | string (uuid) · uuid | sales_order_line | ||
quantity | integer · int4 | |||
quantity_received | integer · int4 | |||
quantity_restocked | integer · int4 | |||
condition | string · text | yes | resellable, damaged | |
warehouse_id | string (uuid) · uuid | yes | warehouse | |
note | string · text | yes |
Primary key: id
Unique: return_id, order_line_id
Checks:
CHECK ((quantity_received <= quantity))CHECK ((quantity_restocked <= quantity_received))CHECK ((quantity > 0))CHECK ((quantity_received >= 0))CHECK ((quantity_restocked >= 0))
OrderReturn
Table order_return · Resource order_returns
GET /api/v1/order_returnsGET /api/v1/order_returns/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
order_id | string (uuid) · uuid | sales_order | ||
sequence | integer · int4 | |||
status | string · text | requested, received, inspected, closed, rejected | ||
reason | string · text | yes | ||
note | string · text | yes | ||
created_by_user_id | string (uuid) · uuid | yes | app_user | |
received_at | string (date-time) · timestamptz | yes | ||
inspected_at | string (date-time) · timestamptz | yes | ||
closed_at | string (date-time) · timestamptz | yes | ||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Unique: order_id, sequence
Checks:
CHECK ((sequence >= 1))
SalesOrder
Table sales_order · Resource orders
GET /api/v1/ordersGET /api/v1/orders/{id}POST /api/v1/orders→retail.create_draft_orderPATCH /api/v1/orders/{id}→retail.update_orderDELETE /api/v1/orders/{id}→retail.delete_draft_order
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
number | string (int64) · int8 | |||
status | string · text | draft, unconfirmed, unfulfilled, partially fulfilled, partially_returned, returned, fulfilled, canceled, expired | ||
origin | string · text | checkout, draft, reissue, bulk_create, marketplace | ||
authorize_status | string · text | none, partial, full | ||
charge_status | string · text | none, partial, full, overcharged | ||
channel_id | string (uuid) · uuid | channel | ||
currency | string · bpchar | max 3 | ||
language_code | string · text | yes | ||
customer_id | string (uuid) · uuid | yes | customer | |
user_email | string · text | yes | ||
customer_note | string · text | |||
tracking_client_id | string · text | yes | ||
billing_address_id | string (uuid) · uuid | yes | address | |
shipping_address_id | string (uuid) · uuid | yes | address | |
shipping_method_id | string (uuid) · uuid | yes | shipping_method | |
shipping_method_name | string · text | yes | ||
collection_point_id | string (uuid) · uuid | yes | warehouse | |
collection_point_name | string · text | yes | ||
subtotal_amount | string (int64) · int8 | |||
shipping_amount | string (int64) · int8 | |||
undiscounted_shipping_amount | string (int64) · int8 | |||
discount_total_amount | string (int64) · int8 | |||
tax_total_amount | string (int64) · int8 | |||
total_amount | string (int64) · int8 | |||
undiscounted_total_amount | string (int64) · int8 | |||
total_charged_amount | string (int64) · int8 | |||
total_authorized_amount | string (int64) · int8 | |||
voucher_code | string · text | yes | ||
original_order_id | string (uuid) · uuid | yes | sales_order | |
lines_count | integer · int4 | |||
redirect_url | string · text | yes | ||
external_reference | string · text | yes | ||
metadata | object · jsonb | |||
created_by_user_id | string (uuid) · uuid | yes | app_user | |
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz | |||
expired_at | string (date-time) · timestamptz | yes | ||
gift_card_amount | string (int64) · int8 | |||
manual_discount_type | string · text | yes | fixed, percentage | |
manual_discount_percentage | string (decimal) · numeric | yes | ||
manual_discount_fixed_amount | string (int64) · int8 | yes | ||
manual_discount_reason | string · text | yes | ||
shipping_tax_rate | string (decimal) · numeric | |||
shipping_tax_included | boolean · bool | |||
shipping_tax_class_id | string (uuid) · uuid | yes | ||
shipping_tax_class_name | string · text | yes | ||
shipping_tax_amount | string (int64) · int8 | |||
fulfilment_method | string · text | shipping, pickup | ||
private_metadata | object · jsonb |
Primary key: id
Unique: workspace_id, number
Checks:
CHECK ((currency ~ '^[A-Z]{3}$'::text))CHECK ((discount_total_amount >= 0))CHECK ((gift_card_amount >= 0))CHECK ((lines_count >= 0))CHECK (((manual_discount_fixed_amount IS NULL) OR (manual_discount_fixed_amount > 0)))CHECK (((manual_discount_percentage IS NULL) OR ((manual_discount_percentage > (0)::numeric) AND (manual_discount_percentage <= (100)::numeric))))CHECK ((((manual_discount_type IS NULL) AND (manual_discount_percentage IS NULL) AND (manual_discount_fixed_amount IS NULL)) OR ((manual_discount_type = 'percentage'::text) AND (manual_discount_percentage IS NOT NULL) AND (manual_discount_fixed_amount IS NULL)) OR ((manual_discount_type = 'fixed'::text) AND (manual_discount_fixed_amount IS NOT NULL) AND (manual_discount_percentage IS NULL))))CHECK ((shipping_amount >= 0))CHECK ((subtotal_amount >= 0))CHECK ((tax_total_amount >= 0))CHECK ((total_amount >= 0))CHECK ((undiscounted_shipping_amount >= 0))CHECK ((undiscounted_total_amount >= 0))
PreorderAllocation
Table preorder_allocation · Resource preorder_allocations
GET /api/v1/preorder_allocationsGET /api/v1/preorder_allocations/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
order_line_id | string (uuid) · uuid | sales_order_line | ||
item_variant_id | string (uuid) · uuid | item_variant | ||
channel_id | string (uuid) · uuid | channel | ||
quantity | integer · int4 | |||
created_at | string (date-time) · timestamptz |
Primary key: id
Unique: order_line_id
Checks:
CHECK ((quantity > 0))
ItemChannelListing
Table item_channel_listing · Resource product_listings
GET /api/v1/product_listingsGET /api/v1/product_listings/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
item_id | string (uuid) · uuid | item | ||
channel_id | string (uuid) · uuid | channel | ||
is_published | boolean · bool | |||
published_at | string (date-time) · timestamptz | yes | ||
visible_in_listings | boolean · bool | |||
available_for_purchase_at | string (date-time) · timestamptz | yes | ||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Unique: item_id, channel_id
ChannelWarehouse
Table channel_warehouse · Resource sales_channel_warehouses
GET /api/v1/sales_channel_warehousesGET /api/v1/sales_channel_warehouses/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
channel_id | string (uuid) · uuid | channel | ||
warehouse_id | string (uuid) · uuid | warehouse | ||
sort_order | integer · int4 |
Primary key: id
Unique: channel_id, warehouse_id
Channel
Table channel · Resource sales_channels
GET /api/v1/sales_channelsGET /api/v1/sales_channels/{id}PATCH /api/v1/sales_channels/{id}→retail.save_channelDELETE /api/v1/sales_channels/{id}→retail.delete_channel
| 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 | |||
slug | string · text | |||
is_active | boolean · bool | |||
currency_code | string · bpchar | max 3 | ||
default_country | string · bpchar | yes | max 2 | |
metadata | object · jsonb | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Checks:
CHECK ((currency_code ~ '^[A-Z]{3}$'::text))
ShippingMethodChannelListing
Table shipping_method_channel_listing · Resource shipping_method_channel_listings
GET /api/v1/shipping_method_channel_listingsGET /api/v1/shipping_method_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 | ||
shipping_method_id | string (uuid) · uuid | shipping_method | ||
channel_id | string (uuid) · uuid | channel | ||
currency | string · bpchar | max 3 | ||
price_amount | string (int64) · int8 | |||
minimum_order_price_amount | string (int64) · int8 | yes | ||
maximum_order_price_amount | string (int64) · int8 | yes | ||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Unique: shipping_method_id, channel_id
Checks:
CHECK (((maximum_order_price_amount IS NULL) OR (maximum_order_price_amount >= 0)))CHECK (((minimum_order_price_amount IS NULL) OR (minimum_order_price_amount >= 0)))CHECK (((minimum_order_price_amount IS NULL) OR (maximum_order_price_amount IS NULL) OR (minimum_order_price_amount <= maximum_order_price_amount)))CHECK ((currency ~ '^[A-Z]{3}$'::text))CHECK ((price_amount >= 0))
ShippingMethodExcludedItem
Table shipping_method_excluded_item · Resource shipping_method_excluded_items
GET /api/v1/shipping_method_excluded_itemsGET /api/v1/shipping_method_excluded_items/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
shipping_method_id | string (uuid) · uuid | shipping_method | ||
item_id | string (uuid) · uuid | item |
Primary key: id
Unique: shipping_method_id, item_id
ShippingMethodPostalCodeRule
Table shipping_method_postal_code_rule · Resource shipping_method_postal_code_rules
GET /api/v1/shipping_method_postal_code_rulesGET /api/v1/shipping_method_postal_code_rules/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
shipping_method_id | string (uuid) · uuid | shipping_method | ||
start_code | string · text | |||
end_code | string · text | yes | ||
inclusion_type | string · text | include, exclude |
Primary key: id
ShippingMethod
Table shipping_method · Resource shipping_methods
GET /api/v1/shipping_methodsGET /api/v1/shipping_methods/{id}PATCH /api/v1/shipping_methods/{id}→retail.save_shipping_methodDELETE /api/v1/shipping_methods/{id}→retail.delete_shipping_method
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
shipping_zone_id | string (uuid) · uuid | shipping_zone | ||
name | string · text | |||
description | string · text | yes | ||
type | string · text | price, weight | ||
minimum_order_weight_grams | string (int64) · int8 | yes | ||
maximum_order_weight_grams | string (int64) · int8 | yes | ||
minimum_delivery_days | integer · int4 | yes | ||
maximum_delivery_days | integer · int4 | yes | ||
metadata | object · jsonb | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz | |||
tax_class_id | string (uuid) · uuid | yes | tax_class |
Primary key: id
Checks:
CHECK (((minimum_order_weight_grams IS NULL) OR (maximum_order_weight_grams IS NULL) OR (minimum_order_weight_grams <= maximum_order_weight_grams)))CHECK (((minimum_delivery_days IS NULL) OR (maximum_delivery_days IS NULL) OR (minimum_delivery_days <= maximum_delivery_days)))CHECK (((type <> 'price'::text) OR ((minimum_order_weight_grams IS NULL) AND (maximum_order_weight_grams IS NULL))))CHECK (((maximum_delivery_days IS NULL) OR (maximum_delivery_days >= 0)))CHECK (((maximum_order_weight_grams IS NULL) OR (maximum_order_weight_grams >= 0)))CHECK (((minimum_delivery_days IS NULL) OR (minimum_delivery_days >= 0)))CHECK (((minimum_order_weight_grams IS NULL) OR (minimum_order_weight_grams >= 0)))
ShippingZoneChannel
Table shipping_zone_channel · Resource shipping_zone_channels
GET /api/v1/shipping_zone_channelsGET /api/v1/shipping_zone_channels/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
shipping_zone_id | string (uuid) · uuid | shipping_zone | ||
channel_id | string (uuid) · uuid | channel |
Primary key: id
Unique: shipping_zone_id, channel_id
ShippingZone
Table shipping_zone · Resource shipping_zones
GET /api/v1/shipping_zonesGET /api/v1/shipping_zones/{id}PATCH /api/v1/shipping_zones/{id}→retail.save_shipping_zoneDELETE /api/v1/shipping_zones/{id}→retail.delete_shipping_zone
| 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 | ||
countries | string[] · bpchar[] | |||
is_default | boolean · bool | |||
metadata | object · jsonb | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
StockMovement
Table stock_movement · Resource stock_movements
GET /api/v1/stock_movementsGET /api/v1/stock_movements/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
stock_id | string (uuid) · uuid | stock | ||
warehouse_id | string (uuid) · uuid | warehouse | ||
item_variant_id | string (uuid) · uuid | item_variant | ||
quantity_delta | integer · int4 | |||
quantity_after | integer · int4 | |||
reason | string · text | count, received, damaged, correction, fulfilment, fulfilment_canceled, returned, transfer_out, transfer_in | ||
note | string · text | yes | ||
order_id | string (uuid) · uuid | yes | sales_order | |
fulfilment_id | string (uuid) · uuid | yes | fulfilment | |
created_by_user_id | string (uuid) · uuid | yes | app_user | |
created_at | string (date-time) · timestamptz | |||
transfer_id | string (uuid) · uuid | yes | ||
return_id | string (uuid) · uuid | yes |
Primary key: id
Checks:
CHECK ((quantity_delta <> 0))
StockReservation
Table stock_reservation · Resource stock_reservations
GET /api/v1/stock_reservationsGET /api/v1/stock_reservations/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
checkout_line_id | string (uuid) · uuid | checkout_line | ||
stock_id | string (uuid) · uuid | stock | ||
quantity | integer · int4 | |||
expires_at | string (date-time) · timestamptz | |||
committed_at | string (date-time) · timestamptz | yes | ||
released_at | string (date-time) · timestamptz | yes | ||
created_at | string (date-time) · timestamptz |
Primary key: id
Unique: checkout_line_id, stock_id
Checks:
CHECK ((NOT ((committed_at IS NOT NULL) AND (released_at IS NOT NULL))))CHECK ((quantity > 0))
Stock
Table stock · Resource stocks
GET /api/v1/stocksGET /api/v1/stocks/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
warehouse_id | string (uuid) · uuid | warehouse | ||
item_variant_id | string (uuid) · uuid | item_variant | ||
quantity | integer · int4 | |||
quantity_allocated | integer · int4 | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz | |||
safety_stock | integer · int4 |
Primary key: id
Unique: warehouse_id, item_variant_id
Checks:
CHECK ((quantity_allocated >= 0))CHECK ((safety_stock >= 0))
ItemVariantChannelListing
Table item_variant_channel_listing · Resource variant_listings
GET /api/v1/variant_listingsGET /api/v1/variant_listings/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
item_variant_id | string (uuid) · uuid | item_variant | ||
channel_id | string (uuid) · uuid | channel | ||
currency | string · bpchar | max 3 | ||
price_amount | string (int64) · int8 | yes | ||
cost_price_amount | string (int64) · int8 | yes | ||
prior_price_amount | string (int64) · int8 | yes | ||
discounted_price_amount | string (int64) · int8 | yes | ||
discounted_price_dirty | boolean · bool | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz | |||
preorder_quantity_threshold | integer · int4 | yes |
Primary key: id
Unique: item_variant_id, channel_id
Checks:
CHECK (((cost_price_amount IS NULL) OR (cost_price_amount >= 0)))CHECK ((currency ~ '^[A-Z]{3}$'::text))CHECK (((discounted_price_amount IS NULL) OR (discounted_price_amount >= 0)))CHECK (((preorder_quantity_threshold IS NULL) OR (preorder_quantity_threshold >= 0)))CHECK (((price_amount IS NULL) OR (price_amount >= 0)))CHECK (((prior_price_amount IS NULL) OR (prior_price_amount >= 0)))
VariantPreorder
Table variant_preorder · Resource variant_preorders
GET /api/v1/variant_preordersGET /api/v1/variant_preorders/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
item_variant_id | string (uuid) · uuid | item_variant | ||
end_date | string (date-time) · timestamptz | yes | ||
global_threshold | integer · int4 | yes | ||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Unique: item_variant_id
Checks:
CHECK (((global_threshold IS NULL) OR (global_threshold >= 0)))
WarehouseShippingZone
Table warehouse_shipping_zone · Resource warehouse_shipping_zones
GET /api/v1/warehouse_shipping_zonesGET /api/v1/warehouse_shipping_zones/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
warehouse_id | string (uuid) · uuid | warehouse | ||
shipping_zone_id | string (uuid) · uuid | shipping_zone |
Primary key: id
Unique: warehouse_id, shipping_zone_id
Warehouse
Table warehouse · Resource warehouses
GET /api/v1/warehousesGET /api/v1/warehouses/{id}POST /api/v1/warehouses→retail.create_warehousePATCH /api/v1/warehouses/{id}→retail.update_warehouseDELETE /api/v1/warehouses/{id}→retail.delete_warehouse
| 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 | |||
slug | string · text | |||
address_id | string (uuid) · uuid | address | ||
email | string · text | yes | ||
click_and_collect_option | string · text | disabled, local, all | ||
is_private | boolean · bool | |||
external_reference | string · text | yes | ||
metadata | object · jsonb | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Events
Workflows and webhooks react to these events.
| Event | What happens | Payload |
|---|---|---|
retail.channel_deleted | A channel that never sold was deleted. | id, slug |
retail.channel_order_imported | An order from a marketplace or another shop arrived. | id, number, channelId, provider, externalId, customerId, currency, totalAmount, status, lines |
retail.channel_order_updated | A marketplace order changed status at its marketplace. | id, number, channelId, provider, externalId, from, to, remoteStatus |
retail.channel_price_changed | A per-channel price changed. | itemVariantId, channelId, amount, currency |
retail.channel_saved | A sales channel was created or edited. | id, slug |
retail.channel_warehouses_changed | A channel’s warehouse order changed. | channelId, count |
retail.checkout_abandoned | A basket with an email on it was left untouched. | token, customerId, customerName, email, channelId, currency, totalAmount, totalText, lineCount, idleMinutes |
retail.checkout_created | A basket was opened. | token, channelId |
retail.checkout_updated | A basket changed. | token |
retail.collection_deleted | A collection was removed. | id |
retail.collection_items_changed | A collection’s membership changed. | id, count |
retail.collection_saved | A collection was created or edited. | id, slug |
retail.customer_notification_requested | An order confirmation, despatch or tracking change the customer should hear about; notifications emails it (a reaction). | kind, orderId, number, fulfilmentId, sequence, email, customerName, carrier, trackingNumber, trackingUrl, totalAmount, currency |
retail.draft_order_created | A draft order was started. | id, number |
retail.draft_order_deleted | A staff-created draft was discarded. | id, number |
retail.fulfilment_approved | A despatch was signed off. | id, orderId |
retail.fulfilment_canceled | A despatch was cancelled. | id, orderId |
retail.fulfilment_created | A despatch was created. | id, orderId, status, customerId, number, sequence, carrier, trackingNumber |
retail.fulfilment_tracking_updated | A tracking number was set. | id, orderId |
retail.gift_card_applied | A gift card paid towards an order. | id, orderId, giftCardId, amount, currency |
retail.gift_card_removed | A gift card came off an order. | id, orderId, orderNumber, giftCardId, amount, currency |
retail.gift_card_requested | A gift card was put on an order; gift_cards redeems it (a reaction). | id, orderId, amount, currency, idempotencyKey |
retail.listing_changed | Something was listed or delisted in a channel. | itemId, channelId, isPublished |
retail.order_canceled | An order was cancelled. | id, number, releasedQuantity, reason, giftCardsReleased |
retail.order_charge_status_changed | What an order has been paid changed (none, partial, full, overcharged). | id, from, to, collectedAmount |
retail.order_confirmed | An order was confirmed. | id, number |
retail.order_discounts_applied | Promotions and a coupon were (re)applied to an order. | id, currency, voucherCode, total, discounts |
retail.order_expired | An unconfirmed order expired. | id, number, releasedQuantity |
retail.order_lines_changed | An order’s lines changed. | id, count |
retail.order_placed | An order was placed. | id, number, channelId, customerId, customerOrderCount, currency, totalAmount, origin, fulfilmentMethod, lines, shippingAmount, undiscountedShippingAmount, giftCardAmount, taxOnTopAmount, voucherCode, discountTotal, discounts, shippingTaxAmount, shippingTaxRate, shippingTaxIncluded, shippingTaxClassId |
retail.order_returned | Goods came back. | id, quantity, complete |
retail.order_updated | An order was edited. | id |
retail.preorder_changed | A variant went on or off preorder. | itemVariantId, enabled, endDate |
retail.preorder_ended | A preorder ended and its orders moved onto a shelf. | itemVariantId, orders |
retail.refund_granted | The shop granted a refund; finance moves the money. | id, orderId, amount, currency, reason, idempotencyKey |
retail.return_closed | A return was closed. | id, orderId |
retail.return_inspected | A return was inspected and restocked. | id, orderId, restocked |
retail.return_received | A returned parcel arrived. | id, orderId, quantity |
retail.return_rejected | A return was refused. | id, orderId, reason |
retail.return_requested | A return was opened. | id, orderId, sequence, quantity, reason |
retail.shipping_exclusions_changed | The products a delivery rate refuses changed. | shippingMethodId, count |
retail.shipping_method_deleted | A delivery rate was removed. | id |
retail.shipping_method_saved | A delivery rate was created or edited. | id, type |
retail.shipping_price_changed | A delivery rate was priced in a channel. | shippingMethodId, channelId, amount, currency |
retail.shipping_rules_changed | A rate’s postal-code rules changed. | shippingMethodId, count |
retail.shipping_zone_deleted | A shipping zone was removed. | id |
retail.shipping_zone_saved | A shipping zone was created or edited. | id, countries |
retail.stock_changed | A shelf was counted. | warehouseId, itemVariantId, quantity |
retail.stock_low | A shelf’s free stock fell to or below the low-stock threshold. | stockId, warehouseId, itemVariantId, quantity, free, threshold |
retail.stock_policy_changed | A shelf’s safety stock changed. | stockId, safetyStock |
retail.stock_reservations_expired | Expired basket stock claims were released. | count |
retail.stock_reserved | A basket holds tracked stock for fifteen minutes. | token, quantity, expiresInMinutes |
retail.stock_transferred | Stock moved between two warehouses. | transferId, fromWarehouseId, toWarehouseId, itemVariantId, quantity |
retail.warehouse_deleted | A warehouse was closed. | id |
retail.warehouse_saved | A warehouse was created or edited. | id, slug |
retail.warehouse_zones_changed | A warehouse’s shipping zones changed. | warehouseId, count |