Public Alpha. Easel is in early development, so expect rough edges.

Easel

← API Documentation

Shipping Rates API

Manage the rates a site charges to ship an order. A site ships to two zones:
domestic, its default country, and international, every other country it
ships to. Each zone has its own rates, up to 10, such as "Ground" and "2-day
air". At checkout the buyer sees their zone's rates, each with its price and
estimated delivery dates, and the cheapest is chosen until they pick another.
An order with no physical items has no shipping and no choice.

A new site starts with one rate per zone called "Standard" (in the site's
language), so it can sell before you set up shipping.

Related resources:

Endpoints

Changing or deleting a rate never changes an order that already shipped by
it: an order keeps the rate's name and transit time as they were. A shopper's
cart picks up the change when they next load checkout, and a cart whose rate
was deleted gets the cheapest one left.


How a Rate Is Priced

base_price covers a parcel of up to 1 kg. Each further kilogram the parcel
starts, even by a gram, adds surcharge_percent of base_price, rounded to
the nearest minor unit, the way carriers bill by the started kilogram. A
surcharge_percent of 0 charges base_price whatever the weight.

The parcel weighs what its shipped items weigh: each variant's
weight_grams, or its product's when the variant sets none, times the
quantity. Items without a weight, digital products, and gift cards weigh
nothing.

When the order's items, after discounts and leaving out gift cards, cost
free_over or more, the rate ships free.

For a rate with a base_price of 500 and a surcharge_percent of 20:

Parcel Price
500 g 500
1 kg 500
1.2 kg 600
2 kg 600
5 kg 900

Shipping is taxed like the items wherever the store collects tax, so
choosing a rate quotes the order's tax again.


List Shipping Rates

Retrieve every shipping rate of a site, domestic first, each zone ordered by
position, then by when they were created. A site has at most 10 rates per
zone, so the list isn't paginated and has_more is always false.

Endpoint: GET /api/v1/sites/{site_id}/shipping-rates/

Permission Required: Read

Request

curl -X GET "https://studio.easel.engineering/api/v1/sites/sit_d0b6fmv28q6vn14peun0/shipping-rates/" \
  -H "Authorization: Bearer your_api_key_here"

Response

Status: 200 OK

{
  "data": [
    {
      "id": "shr_d0b6fmv28q6vn14pesr1",
      "zone": "domestic",
      "name": "Standard",
      "base_price": 500,
      "surcharge_percent": 20,
      "free_over": 7500,
      "transit": {"min": 3, "max": 5},
      "position": 0,
      "created_at": "2023-01-01T10:00:00Z",
      "updated_at": "2023-01-01T10:00:00Z"
    },
    {
      "id": "shr_d0b6fmv28q6vn14pesr2",
      "zone": "domestic",
      "name": "2-day air",
      "base_price": 1500,
      "surcharge_percent": 20,
      "free_over": null,
      "transit": {"min": 1, "max": 2},
      "position": 1,
      "created_at": "2023-01-01T10:00:00Z",
      "updated_at": "2023-01-01T10:00:00Z"
    }
  ],
  "has_more": false
}

Create Shipping Rate

Add a shipping rate to one of a site's zones.

Endpoint: POST /api/v1/sites/{site_id}/shipping-rates/

Permission Required: Write

Request Body

{
  "zone": "domestic",
  "name": "2-day air",
  "base_price": 1500,
  "surcharge_percent": 20,
  "free_over": null,
  "transit": {"min": 1, "max": 2},
  "position": 1
}
Field Type Required Description
zone string Yes domestic or international. It can't change later
name string Yes 1 to 100 characters, unique within the zone
base_price integer No Price for a parcel of up to 1 kg, in the store's smallest currency unit, zero or more. Defaults to 0
surcharge_percent integer No Share of base_price each started kilogram past the first adds, from 0 to 1000. Defaults to 20
free_over integer|null No Ships free when the items cost this much or more, after discounts and leaving out gift cards. Omit or null to never ship free
transit object Yes Business days the carrier takes: an integer min and max, each from 0 to 60, with max at least min
position integer No Sort order within the zone from 0 to 1000, lowest first. Leave it out to put the rate after the zone's last one

Request

curl -X POST "https://studio.easel.engineering/api/v1/sites/sit_d0b6fmv28q6vn14peun0/shipping-rates/" \
  -H "Authorization: Bearer your_api_key_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: unique-request-id-12345" \
  -d '{
    "zone": "domestic",
    "name": "2-day air",
    "base_price": 1500,
    "transit": {"min": 1, "max": 2}
  }'

Response

Status: 201 Created

{
  "id": "shr_d0b6fmv28q6vn14pesr2",
  "zone": "domestic",
  "name": "2-day air",
  "base_price": 1500,
  "surcharge_percent": 20,
  "free_over": null,
  "transit": {"min": 1, "max": 2},
  "position": 1,
  "created_at": "2023-01-01T10:00:00Z",
  "updated_at": "2023-01-01T10:00:00Z"
}

Error Responses

Missing name

Status: 400 Bad Request

{
  "error": "name is required",
  "code": "MISSING_FIELD"
}

Invalid field, such as an unknown zone, a backward transit range, or a
negative price

Status: 400 Bad Request

{
  "error": "Zone must be 'domestic' or 'international'",
  "code": "INVALID_FIELD"
}

Zone full

Status: 400 Bad Request

{
  "error": "A zone can have at most 10 shipping rates",
  "code": "INVALID_FIELD"
}

Name taken in the zone

Status: 400 Bad Request

{
  "error": "Another rate in the zone has this name",
  "code": "INVALID_FIELD"
}

Get Shipping Rate

Retrieve one of a site's shipping rates.

Endpoint: GET /api/v1/sites/{site_id}/shipping-rates/{shipping_rate_id}/

Permission Required: Read

Request

curl -X GET "https://studio.easel.engineering/api/v1/sites/sit_d0b6fmv28q6vn14peun0/shipping-rates/shr_d0b6fmv28q6vn14pesr2/" \
  -H "Authorization: Bearer your_api_key_here"

Response

Status: 200 OK

Returns the Shipping Rate Object. A rate of another
site answers 404 Not Found.


Update Shipping Rate

Replace a shipping rate's fields. The body is the same as
Create Shipping Rate's, except that zone is
ignored: a rate stays in its zone. Fields you leave out take their defaults,
and leaving out position keeps the rate where it is.

Endpoint: PUT /api/v1/sites/{site_id}/shipping-rates/{shipping_rate_id}/

Permission Required: Write

Request

curl -X PUT "https://studio.easel.engineering/api/v1/sites/sit_d0b6fmv28q6vn14peun0/shipping-rates/shr_d0b6fmv28q6vn14pesr2/" \
  -H "Authorization: Bearer your_api_key_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: unique-request-id-67890" \
  -d '{
    "name": "2-day air",
    "base_price": 1600,
    "surcharge_percent": 25,
    "transit": {"min": 1, "max": 2}
  }'

Response

Status: 200 OK

Returns the updated Shipping Rate Object. Errors
are the same as Create Shipping Rate's.


Delete Shipping Rate

Delete a shipping rate. Orders that shipped by it keep its name and transit
time. If it was a zone's last rate, buyers in that zone can't check out
products that ship until you add another.

Endpoint: DELETE /api/v1/sites/{site_id}/shipping-rates/{shipping_rate_id}/

Permission Required: Write

Request

curl -X DELETE "https://studio.easel.engineering/api/v1/sites/sit_d0b6fmv28q6vn14peun0/shipping-rates/shr_d0b6fmv28q6vn14pesr2/" \
  -H "Authorization: Bearer your_api_key_here"

Response

Status: 204 No Content


Shipping Rate Object

Field Type Description
id string Shipping rate public ID
zone string domestic, the site's default country, or international, every other country it ships to
name string Name buyers see at checkout, such as Ground
base_price integer Price for a parcel of up to 1 kg, in the store's smallest currency unit
surcharge_percent integer Share of base_price each started kilogram past the first adds. 0 charges base_price whatever the weight
free_over integer|null Items after discounts, leaving out gift cards, that cost this much or more ship free. null never ships free
transit object Business days the carrier takes, as min and max. With the site's handling time, it estimates delivery dates
position integer Sort order within the zone, lowest first
created_at string ISO 8601 timestamp
updated_at string ISO 8601 timestamp