Order API

One endpoint. Your server tells us about each order placed through the app, and every status change after that.

Endpoint

POST https://easyvial.app/api/v1/orders?store={slug}

Authentication

Every request is signed with your store's signing secret (dashboard → Store setup). Compute an HMAC-SHA256 of the exact raw request body with the secret as the key, hex-encode it, and send it in X-EasyVial-Signature. Examples in five languages →

HeaderRequiredValue
Content-Typeyesapplication/json
X-EasyVial-SignatureyesLowercase hex HMAC-SHA256 of the raw body
X-EasyVial-Delivery-IDnoYour own unique ID for the delivery — shown in our logs to help debugging

Request body

FieldTypeRequiredDescription
idstringyesYour order number. Must be unique within your store. Max 64 chars.
order_keystringrecommendedPer-order secret — the same value you pass to EasyVial.trackOrder(). Links the order to the customer's phone.
statusenumyespending · processing · on-hold · completed · cancelled · refunded · failed
totalstring | numberyesOrder total in major units, e.g. "129.98". Include shipping and tax as charged.
refunded_totalstring | numbernoTotal refunded so far, major units. Default 0.
currencystringnoISO 4217 code. Default USD.
utm_sourcestringnoFrom the landing URL. easyvial marks the order as an app order on its own.
utm_mediumstringnoapp or push
utm_campaignstringnoPush campaign ID, when the order came from a push
created_atISO 8601noWhen the order was placed
tracking_numberstringnoAdd when the order ships. Customers get in-transit, out-for-delivery and delivered notifications automatically.
carrierstringnousps · ups · fedex · dhl. Guessed from the number if omitted.
tracking_urlstringnoYour own tracking page, if you have one. Otherwise we link to the carrier.

Status meanings

StatusCustomer seesFee
pendingAwaiting payment—
processing“Your order is confirmed” notificationCharged
on-holdOn hold—
completed“Your order has shipped” notificationCharged
cancelledCancelledReversed
refundedRefundedReversed (partial refunds: reduced by refunded_total)
failedFailed—

Responses

StatusBodyMeaning
200{"ok":true,"attributed":true}Recorded. attributed says whether it counts as an app order.
400{"error":"missing_id" | "bad_status" | "bad_json"}Fix the request; don't retry as-is
401{"error":"invalid_signature"}Wrong secret, or the body changed after signing (re-serialising JSON is the usual cause)
404{"error":"store_not_found"}Wrong slug
5xx—Retry with backoff (e.g. 1 min, 5 min, 30 min)

Idempotency

Requests are idempotent per id: the latest request wins, and sending the same status twice changes nothing. Fees are recalculated from the current status and refunded total every time, so retries never double-charge.

Example

curl
BODY='{"id":"10423","order_key":"k_9f2c1a","status":"processing","total":"129.98","currency":"USD"}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SIGNING_SECRET" -hex | sed 's/^.* //')

curl -X POST "https://easyvial.app/api/v1/orders?store=your-slug" \
  -H "Content-Type: application/json" \
  -H "X-EasyVial-Signature: $SIG" \
  --data "$BODY"
WooCommerce stores don't use this endpoint — the webhooks we register do the same job.