Shipping quote

POST /api/v1/shipping-quote write:order

Fulfillment, step 1. Validates the ship-to address and returns live carrier rates (ascending by amount) for the listed cards. Opens a PENDING_PAYMENT order and snapshots the quoted rates, so committing bills exactly what you were quoted. Budget ~2s (p50) to ~3.5s (p95) for this call: carrier rating is live and synchronous and finishes when the slowest carrier asked has answered — pass carriers to rate only the ones you sell (USPS alone answers in about a second). The batch is atomic — one bad card fails the whole quote; you never get a partial order. An uncommitted quote is not a conflict: re-quoting silently supersedes it and cancels the old order, so you can re-quote a card freely until you commit. Hand the order_id + a chosen rate_id to POST /api/v1/orders.

Try it POST /api/v1/shipping-quote write

These inputs are shared across all docs pages, so an id entered here carries over.

request body
object · 2 keys
{
  "unique_ids": [
    "CARD-EXAMPLE-1"
  ],
  "address": {
    "name": "Demo Buyer",
    "street1": "1 Market St",
    "city": "San Francisco",
    "state": "CA",
    "zip": "94105",
    "country": "US"
  }
}
response

Not run yet. Press Run to make a live call against https://service.rip.fun (through this demo's server-side proxy; the API key never reaches the browser).

curl (tracks the inputs above)
curl -X POST 'https://service.rip.fun/api/v1/shipping-quote' \
  -H 'X-API-Key: rip_…' \
  -H 'Content-Type: application/json' \
  -d '{"unique_ids":["CARD-EXAMPLE-1"],"address":{"name":"Demo Buyer","street1":"1 Market St","city":"San Francisco","state":"CA","zip":"94105","country":"US"}}'

Request fields

FieldTypeRequiredDescription
unique_idsstring[]yesOne or more card unique_ids. Always an array, even for a single card — a singular unique_id key returns 400. Every card must belong to you and be OFFCHAIN
address.namestringyesRecipient full name
address.street1stringyesStreet line 1 (`street2` optional)
address.citystringyesCity
address.statestringyesState / region
address.zipstringyesPostal code
address.countrystringyesISO country code, e.g. `US` (`email`, `company` optional)
address.phonestring—Recipient phone. REQUIRED for any non-US destination — carriers will not issue an international label without one. Any national ("0439 927 773") or international ("+61 439 927 773") format is accepted; we normalise it to E.164 for the destination country. Optional for US addresses
carriersstring[]—Which carriers to rate: "usps", "ups", "dhl_express", "lso". Only these are rated and offered. The quote waits for the slowest carrier asked — UPS takes ~1.8s, USPS ~0.9s — so a checkout that only sells USPS halves its quote time with ["usps"]. Omitted: every carrier that can serve the destination. A list none of which can serve the destination (["usps"] to Australia) is set aside and every eligible carrier is rated instead; see carriers in the response

Response fields (data)

FieldDescription
order_idNew PENDING_PAYMENT order id. Pass it to POST /orders to commit
referencenull on a quote. The order’s human-readable number — e.g. 101326AA0042, what warehouse staff read, print and quote back to you — is assigned when you commit, because it starts with the order’s ship-by day: MMDDYY (ship_by_at as a US Central calendar day), then a sequence shared by every order (two letters without I or O, then four digits: AA0001 … AA9999, AB0001 … ZZ9999, then AA0001 again). Read it from the commit response or GET /orders/:order_id. order_id stays the key
unique_idsEchoed card ids on the order
rates[]Live rates, cheapest first: rate_id, object_id, amount (decimal string), currency, provider, servicelevel {name, token}, estimated_days. Every rate offered is purchasable for the destination: outside the US and Canada USPS is never offered (carriers will not sell it), so expect DHL / UPS / FedEx there, and the customs declaration an international shipment needs is built for you from catalog values
carriersstring[], sorted — the carriers this quote was rated against: what you asked for in carriers, narrowed to those that can serve the destination, or every eligible carrier. If it is not what you sent, your list fit no carrier for this destination
shippo_shipment_id / shippo_address_idShipment + validated address object ids
ship_by_at / expires_atTarget ship date + the re-quote deadline we recommend (24h after quoting). The price you are billed is snapshotted at quote time either way. Shippo hard-expires a rate 7 days after it is quoted: committing an order older than that is re-quoted fresh at the current carrier price (you are still billed the snapshot), so re-quote rather than commit a stale order

Errors

StatusCodeWhen
403Insufficient permissionskey is tenant-scoped but carries neither write:order nor admin — a read-only key can list and fetch orders but not open, commit or cancel one
400unique_ids requiredunique_ids missing/empty, invalid address, or address validation failed
400phone_requiredaddress.country is not US and no address.phone was supplied — international carriers require a recipient phone. Returned before any carrier call; add the phone (any format) and re-quote
400Invalid addressaddress.street1 is longer than 100 characters, the carrier limit — the error gives its length. Returned before any carrier call; shorten it (a unit or suite number can go in address.street2) and re-quote
400unknown_carriercarriers names a carrier we have no account with (a typo, or a carrier we do not ship with); the message lists the available tokens. A carriers value that is not a non-empty string array is the plain 400 "Invalid request body". Both are returned before any carrier call
422SHIPPO_REJECTEDShippo refused the quote request itself — an address field it will not accept. messages[] carries Shippo’s reasons and retryable is false: fix the address and re-quote; sending the same request again fails the same way
404card(s) not founda unique_id is not a card of your account (the message names them)
409card unavailablea card is not OFFCHAIN (e.g. PENDING_WITHDRAWAL, or mid-redemption on another order) — the most common failure; the error names the offending cards and their statuses
409card already on an ordera card belongs to a committed, in-flight or fulfilled order — data carries that order’s state. A RETURNED order does not block: its reinstated cards are OFFCHAIN again and quote normally, which is how you re-ship a package that came back — quote the same unique_ids with a corrected address
409CARD_ALREADY_WITHDRAWINGa card is on a withdrawal that has not completed or been cancelled — it cannot be shipped and withdrawn at once
400invalid_paginationunique_ids has more than 500 entries or a non-string entry
502no ratesShippo could not be reached AND the backup rate table produced nothing for the destination — rare; retryable

See Errors for the response envelope and the full code list.

Flow

  1. POST /api/v1/shipping-quote with unique_ids + address → order_id + rates[]
  2. Pick a rate_id, POST /api/v1/orders {order_id, rate_id} → commits the order (billed the quoted amount, pass-through)
  3. The warehouse buys the label and ships — parcel sizing is computed from the card count (toploaders, sleeves/team boxes/storage boxes); you do not choose packaging
  4. GET /api/v1/orders/:order_id to poll status + tracking (or GET /api/v1/orders to list)
  5. POST /api/v1/orders/:order_id/cancel while still PENDING_PAYMENT to void + release the cards