RailbedDocs

Orders

An order is what a buyer tried to buy, with its items, totals, addresses and customer. Every checkout session belongs to one. Read orders to fulfil them, and report fulfilment back when you ship.

On this page

The order object

FieldTypeDescription
idstringThe order's id, ord_…
objectstring"order"
livemodebooleanfalse for Test mode, true for Live mode
numberintegerRailbed's order number, counting from 1001 in each mode. Your store's own number is store.order_number
statusstringopen, pending, paid, held, failed, expired or canceled, from its payments. See Status
needs_reviewbooleantrue when it's held and waits on you: a held payment you could accept as paid, or paid payments that don't cover total. Such an order refuses new sessions with 409 order_paid
fulfillmentstringnone, unfulfilled or fulfilled. See Fulfilment
fulfilled_atinteger or nullWhen it was marked fulfilled, in Unix seconds
customer_idstring or nullIts customer, cus_…. Null until the buyer has given an email
currencystringThe currency of every amount on the order
subtotalstringThe items' amounts added up
discountstringTaken off the subtotal, and larger than it only when store credit also covered shipping or tax. "0.00" when there's none
discount_codestring or nullThe code or codes the buyer used
shippingstringShipping charged
shipping_methodstring or nullHow it ships, such as Standard
taxstringTax charged
totalstringsubtotal − discount + shipping + tax: the latest session's amount
amount_paidstringWhat its paid payments in currency add up to. "0.00" until one is paid
itemsarrayWhat was bought, in the order you sent it. See Items
shipping_addressobject or nullWhere to send it. See Addresses
billing_addressobject or nullThe buyer's billing address
storeobject or nullWhere the order lives in your systems, for orders sent with order.id. See Store
payment_idsarrayEvery payment attempt at this order, newest first (pay_…)
createdintegerWhen the order was created, in Unix seconds
paid_atinteger or nullWhen its first payment became paid, in Unix seconds

Amounts are decimal strings, like every price in the API. They're what the buyer was charged in currency, not what arrived in your wallet: that's on each payment.

An order from a store
{
  "id": "ord_Vd3kR8mQ1xTn6LpZs0Wa",
  "object": "order",
  "livemode": true,
  "number": 1187,
  "status": "paid",
  "needs_review": false,
  "fulfillment": "unfulfilled",
  "fulfilled_at": null,
  "customer_id": "cus_Hc7nQ2wVz9KpT4mRb1Ye",
  "currency": "USD",
  "subtotal": "130.00",
  "discount": "13.00",
  "discount_code": "WELCOME10",
  "shipping": "8.95",
  "shipping_method": "Standard",
  "tax": "9.36",
  "total": "135.31",
  "amount_paid": "135.31",
  "items": [
    {
      "name": "Magnesium glycinate",
      "variant": "120 capsules",
      "sku": "HN-MG-120",
      "quantity": 2,
      "unit_amount": "32.00",
      "amount": "64.00"
    },
    {
      "name": "Daily greens",
      "variant": null,
      "sku": "HN-DG-30",
      "quantity": 1,
      "unit_amount": null,
      "amount": "54.00"
    },
    {
      "name": "Shaker bottle",
      "variant": null,
      "sku": null,
      "quantity": 1,
      "unit_amount": null,
      "amount": "12.00"
    }
  ],
  "shipping_address": {
    "name": "Maya Okafor",
    "line1": "418 Linden Avenue",
    "line2": "Apt 3B",
    "city": "Portland",
    "region": "OR",
    "postal_code": "97214",
    "country": "US"
  },
  "billing_address": null,
  "store": {
    "platform": "other",
    "id": "yourstore",
    "name": "yourstore.com",
    "url": "https://yourstore.com",
    "order_id": "1042",
    "order_number": "1042",
    "order_url": "https://yourstore.com/admin/orders/1042",
    "customer_id": "88"
  },
  "payment_ids": ["pay_Rt5Wm2KxQ8zLb3NvYc7P"],
  "created": 1790380525,
  "paid_at": 1790381342
}
200 OK

Items

FieldTypeDescription
namestringWhat the line is
variantstring or nullWhich version, such as 120 capsules or Blue, L
skustring or nullYour stock-keeping code
quantityintegerHow many units
unit_amountstring or nullOne unit's price, when it was sent. For display only
amountstringThe line's total. The order's totals add up from these

An order made without order.items has one line, named after the session's description, with quantity 1. Its amount is whatever makes the totals add up: amount + discount − shipping − tax, which is the whole amount when you send no totals.

Addresses

shipping_address and billing_address have the same fields, each a string or null: name, line1, line2, city, region (a state, province or county), postal_code and country (a two-letter code such as US). They're stored as sent, not checked against a postal service.

Store

For orders you sent with order.id, store says where the order lives, so you can find it again and link back to it:

FieldTypeDescription
platformstringwoocommerce, shopify or other
idstringThe order.store.id you sent. Empty ("") when you sent no store
namestring or nullThe store's name
urlstring or nullThe store's address
order_idstringYour order id, as sent in order.id
order_numberstring or nullThe number your buyers see
order_urlstring or nullThe order's page in your admin
customer_idstring or nullThe buyer's id in your store: the customer.id you sent

store is null for orders sent without order.id, and for orders from Railbed's own checkouts and payment links.

How orders are made

Every checkout session belongs to an order, and its order_id says which. The order is created in the same step as the session, so a session never exists without one.

  • From the API. Send order and customer when you create a session, and the order has your items, totals and addresses. Without them, the order has one line, the session's description, for the whole amount.
  • From your store's order id. A session sent with order.id joins the order that already has that id, if there is one. That's how a buyer's second try at paying the same order stays one order. See Store orders and retries.
  • From Railbed's checkouts and payment links. Checkout pages, pricing tables, buy buttons and payment links make orders too, with one item: the product, plan or what the link is for. A buyer who tries the same checkout again within a day, with the same email, price and plan, joins their unpaid order.

Each payment is one attempt at paying for its order. An order can have several, in payment_ids, and more than one of them can be paid if the buyer paid twice.

The API reads orders and updates their fulfilment. It doesn't create orders on their own, edit their items or delete them: an order's contents change only when a new session for the same order.id arrives.

Status

An order's status comes from its payments, and changes in the same step as theirs:

  1. paid when its paid payments cover total (they add up to at least total in the order's currency).
  2. Otherwise held when any of its payments is held for review, or is paid but doesn't cover total (a part-payment after the order grew, or one in another currency).
  3. Otherwise the latest payment's status: open, pending, failed or expired, or canceled when it's a payment link you canceled.
StatusMeaningWhat to do
openCreated, or started but not yet handed to a providerWait
pendingThe buyer went to a provider to payWait
paidPayments for it arrived, passed Railbed's checks and cover totalFulfil it, once
heldMoney arrived but failed a check, or what's paid doesn't cover total (amount_paid)Don't fulfil. Review it in the dashboard, and check with the buyer
failedThe latest attempt was declined (Test mode)Let the buyer try again
expiredThe latest attempt expired unpaidTreat as abandoned; a late payment can still make it paid
canceledYou canceled its payment linkNothing to do

The status can't be set through the API. Like a payment, an order can become paid after expired when money arrives late, and a held order becomes paid when you accept the payment. A paid order stays paid.

Fulfilment

fulfillment is your record of whether the order has been delivered. It doesn't affect the payment.

ValueMeaning
noneNothing to ship: digital goods, memberships, services. The default without a shipping address
unfulfilledWaiting to be shipped or delivered. The default when the order has a shipping address
fulfilledDelivered or on its way. fulfilled_at says when it was marked

You can set the starting value with order.fulfillment when you create the session. After that:

  • Orders sent with order.id get their fulfilment from your store, through Update an order. The dashboard shows it but doesn't change it, so the two never disagree. The WooCommerce plugin does this for you.
  • Other orders can be marked fulfilled in the dashboard's Orders once they're paid, or through the API.

Retrieve an order

GET/v1/orders/:id

Returns an order of yours in the key's mode. Another account's order, or one in the other mode, is 404 not_found.

curl https://pay.railbed.io/v1/orders/ord_Vd3kR8mQ1xTn6LpZs0Wa \
  -H "Authorization: Bearer $RAILBED_SECRET_KEY"

The response is the order object. To find the order behind a payment or webhook, use the payment's order_id (orderId in webhooks); webhooks also carry the order itself.

List orders

GET/v1/orders

Returns your orders in the key's mode, newest first by creation. Filter by customer, or find the order you sent with a given order.id.

ParameterTypeDescription
limitinteger1–100. Default 20
starting_afterstringAn order id from the previous page's next_cursor
customer_idstringOnly this customer's orders
store_order_idstringOnly the order you sent with this order.id
store_idstringOnly orders sent with this order.store.id. With store_order_id, the one order you sent with both; leave it out for orders sent without store
curl "https://pay.railbed.io/v1/orders?store_id=yourstore&store_order_id=1042" \
  -H "Authorization: Bearer $RAILBED_SECRET_KEY"
Response (trimmed)
{
  "data": [
    {
      "id": "ord_Vd3kR8mQ1xTn6LpZs0Wa",
      "object": "order",
      "number": 1187,
      "status": "paid",
      "fulfillment": "unfulfilled",
      "total": "135.31"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
200 OK

Each item is a full order object (trimmed here). Page through with next_cursor as on List payments. A store_order_id matches at most one order in each mode. An invalid limit is 400 invalid_limit; a cursor that isn't one of your orders in this mode is 400 invalid_cursor.

Update an order

PATCH/v1/orders/:id

Records the order's fulfilment. This is how your store tells Railbed an order has shipped, or that it's waiting again. Marking it fulfilled or unfulfilled appears on the order's timeline in the dashboard.

FieldTypeDescription
fulfillmentstring Requirednone, unfulfilled or fulfilled
curl -X PATCH \
  https://pay.railbed.io/v1/orders/ord_Vd3kR8mQ1xTn6LpZs0Wa \
  -H "Authorization: Bearer $RAILBED_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "fulfillment": "fulfilled" }'

The response is the updated order.

  • fulfilled sets fulfilled_at to now; unfulfilled and none clear it. Sending the value the order already has changes nothing, so retrying is safe.
  • It works on any order of yours in this mode, from any source, paid or not. Railbed doesn't check the payment here, so report only what you've actually shipped.
  • No webhook is sent for a fulfilment change.
StatusCodeWhen
400invalid_fulfillmentfulfillment is missing or not one of the three
404not_foundNot an order of yours in this mode

Updated · This page as Markdown