Pular para o conteúdo

Shipments

Este conteúdo não está disponível em sua língua ainda.

Shipments in BenchKey represent carrier labels purchased through the Shippo or UPS Direct integration. The typical flow is: get rates → purchase label → optionally void within the carrier’s window.

{
"object": "shipment",
"id": "88",
"ticket_id": "314",
"carrier_account_id": "ca_abc123",
"provider": "shippo",
"carrier": "usps",
"service_code": "usps_priority",
"direction": "outbound",
"tracking_number": "9400111899223407602815",
"label_url": "https://cdn.shippo.com/labels/88.pdf",
"eta": "2024-09-12T23:59:59.000Z",
"cost": {
"amount": "7.95",
"currency": "USD"
},
"void_window_days": 28,
"voided_at": null,
"void_reason": null,
"shippo_refund_id": null,
"shippo_refund_status": null,
"shippo_refund_requested_at": null,
"shippo_refund_updated_at": null,
"shippo_refund_error": null,
"scan_first_seen_at": null,
"created_at": "2024-09-10T14:22:00.000Z",
"updated_at": "2024-09-10T14:22:00.000Z"
}
FieldTypeDescription
idstringUnique shipment ID
ticket_idstring|nullRepair ticket this label is associated with
carrier_account_idstringInternal carrier account used for the purchase
providerstringShipping integration ("shippo", "ups_direct")
carrierstringCarrier identifier, for example "usps" or "ups"
service_codestringCarrier service level code
directionstring"inbound", "outbound", or "unknown" for older records
tracking_numberstring|nullCarrier tracking number
label_urlstring|nullURL to the printable PDF label
etastring|nullEstimated delivery date/time (ISO-8601)
cost.amountstringLabel cost in dollars, e.g. "7.95"
cost.currencystringCurrency code, e.g. "USD"
void_window_daysintegerDays from purchase within which the label can be voided
voided_atstring|nullISO-8601 timestamp of when the label was voided
void_reasonstring|nullReason provided when the label was voided
shippo_refund_idstring|nullProvider refund request ID, when available
shippo_refund_statusstring|null"REQUESTING", "QUEUED", "PENDING", "SUCCESS", or "ERROR"; null when no status is recorded
shippo_refund_requested_atstring|nullISO-8601 time the refund was requested
shippo_refund_updated_atstring|nullISO-8601 time the refund status last changed
shippo_refund_errorstring|nullRecorded refund error, if any
scan_first_seen_atstring|nullTimestamp when the first carrier scan was recorded
created_atstringISO-8601 timestamp of label purchase
updated_atstringISO-8601 timestamp of last update

GET /api/v1/shipments

Returns a cursor-paginated list of shipments ordered by ID ascending.

Scope required: shipments.read

ParameterTypeDescription
ticket_idstringFilter to shipments linked to this ticket
carrierstringFilter by carrier identifier (e.g. "usps", "ups"), max 255 chars
providerstringFilter by provider (e.g. "shippo", "ups_direct"), max 255 chars
voidedbooleantrue, voided only; false, active only; omit for all. Must be true, false, 1, or 0; any other value returns 400 invalid_query
limitintegerPage size, 1–100 (default: 20)
cursorstringOpaque cursor from a previous response’s next_cursor
Terminal window
curl "https://app.benchkey.com/api/v1/shipments?ticket_id=314&voided=false" \
-H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa"
const res = await fetch(
"https://app.benchkey.com/api/v1/shipments?ticket_id=314&voided=false",
{ headers: { Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" } }
);
const { data, has_more, next_cursor } = await res.json();
{
"object": "list",
"data": [
{
"object": "shipment",
"id": "88",
"ticket_id": "314",
"provider": "shippo",
"carrier": "usps",
"service_code": "usps_priority",
"tracking_number": "9400111899223407602815",
"label_url": "https://cdn.shippo.com/labels/88.pdf",
"cost": { "amount": "7.95", "currency": "USD" },
"voided_at": null,
"created_at": "2024-09-10T14:22:00.000Z",
"updated_at": "2024-09-10T14:22:00.000Z"
}
],
"has_more": false,
"next_cursor": null
}

See Pagination for how to page through results.


GET /api/v1/shipments/:id

Returns a single shipment by its string ID.

Scope required: shipments.read

Terminal window
curl "https://app.benchkey.com/api/v1/shipments/88" \
-H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa"
const res = await fetch(
"https://app.benchkey.com/api/v1/shipments/88",
{ headers: { Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" } }
);
const shipment = await res.json();

Returns the shipment object. Returns 404 if no shipment with that ID exists or if the shipment’s linked ticket has been hidden or soft-deleted.


POST /api/v1/shipments/rates

Returns available carrier rates for a parcel. Pass the complete chosen rate object, including its intentToken, unchanged in the JSON body for Purchase a label. Keep the same ticket and direction used for the quote.

Scope required: shipments.write

FieldRequiredDescription
from_addressyesOriginating address object
to_addressyesDestination address object
parcelyesParcel dimensions and weight
ticket_idconditionalRequired for inbound quotes and location-restricted keys. Also supply it when quoting a Shippo label, since Shippo purchases require a ticket. Must be a string (the ticket’s order_id)
carrier_account_idnoRestrict rates to a specific carrier account. Must be a string
directionno"outbound" (default) or "inbound"; use the same value when purchasing

Address object fields: name, street1, city, state, zip, country (e.g. "US").

Parcel object fields: length, width, height, distance_unit (e.g. "in"), weight, mass_unit (e.g. "lb").

Terminal window
curl -X POST https://app.benchkey.com/api/v1/shipments/rates \
--fail-with-body --output rates.json \
-H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" \
-H "Content-Type: application/json" \
-d '{
"from_address": {
"name": "BenchKey Repair",
"street1": "123 Main St",
"city": "Austin",
"state": "TX",
"zip": "78701",
"country": "US"
},
"to_address": {
"name": "Jane Customer",
"street1": "456 Oak Ave",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"country": "US"
},
"parcel": {
"length": 9,
"width": 6,
"height": 2,
"distance_unit": "in",
"weight": 0.5,
"mass_unit": "lb"
},
"ticket_id": "314",
"direction": "outbound"
}'
const res = await fetch("https://app.benchkey.com/api/v1/shipments/rates", {
method: "POST",
headers: {
Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa",
"Content-Type": "application/json",
},
body: JSON.stringify({
from_address: {
name: "BenchKey Repair",
street1: "123 Main St",
city: "Austin",
state: "TX",
zip: "78701",
country: "US",
},
to_address: {
name: "Jane Customer",
street1: "456 Oak Ave",
city: "Dallas",
state: "TX",
zip: "75201",
country: "US",
},
parcel: { length: 9, width: 6, height: 2, distance_unit: "in", weight: 0.5, mass_unit: "lb" },
ticket_id: "314",
direction: "outbound",
}),
});
if (!res.ok) throw new Error(`Rate lookup failed: ${res.status} ${await res.text()}`);
const { rates } = await res.json();
{
"object": "rate_list",
"rates": [
{
"provider": "shippo",
"rateId": "rate_abc123",
"carrier": "usps",
"serviceLabel": "Priority Mail",
"serviceCode": "usps_priority",
"amount": 7.95,
"currency": "USD",
"estDays": 2,
"carrierAccountId": "ca_abc123",
"_shippoShipmentId": "shipment_abc123",
"intentToken": "SERVER_ISSUED_QUOTE_TOKEN"
},
{
"provider": "shippo",
"rateId": "rate_def456",
"carrier": "usps",
"serviceLabel": "Ground Advantage",
"serviceCode": "usps_ground_advantage",
"amount": 4.10,
"currency": "USD",
"estDays": 5,
"carrierAccountId": "ca_abc123",
"_shippoShipmentId": "shipment_abc123",
"intentToken": "ANOTHER_SERVER_ISSUED_QUOTE_TOKEN"
}
]
}

POST /api/v1/shipments

Purchases a carrier label using a rate returned by Get carrier rates. Keep the complete selected rate, including its server-issued intentToken; the tokens above are illustrative placeholders. Retry an uncertain purchase with the same request and inspect the result before starting another purchase. If the API reports an expired quote, request fresh rates for the same ticket and direction and select a rate again.

Scope required: shipments.write

FieldRequiredDescription
rateyesComplete selected object from POST /shipments/rates, including provider, rateId, amount (positive decimal number or string, max 2 dp), currency (3-letter code), and the unchanged intentToken. Do not reconstruct a partial object
ticket_idconditionalRequired for Shippo purchases, inbound labels, and location-restricted keys. Must be a string (the ticket’s order_id), matching the quote. The ticket must be visible and within the key’s location scope
directionno"outbound" (default) or "inbound"; must match the quote
ups_direct_ship_payloadnoRequired only when rate.provider is "ups_direct"

The purchase examples below use a Shippo rate. A UPS Direct purchase also needs ups_direct_ship_payload matching the quoted shipment.

Terminal window
# Review rates.json, then choose the desired Shippo rate from the filtered list.
jq -e '[.rates[] | select(.provider == "shippo")][0] // error("No Shippo rates returned") |
{ticket_id: "314", direction: "outbound", rate: .}' \
rates.json > label-request.json &&
curl --fail-with-body -X POST https://app.benchkey.com/api/v1/shipments \
-H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" \
-H "Content-Type: application/json" \
--data-binary @label-request.json
// Continue from the rate lookup above after reviewing the available rates.
const selectedRate = rates.find(rate => rate.provider === "shippo");
if (!selectedRate?.intentToken) throw new Error("Select a fresh server-issued rate");
const purchaseRes = await fetch("https://app.benchkey.com/api/v1/shipments", {
method: "POST",
headers: {
Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa",
"Content-Type": "application/json",
},
body: JSON.stringify({
ticket_id: "314",
direction: "outbound",
rate: selectedRate,
}),
});
if (!purchaseRes.ok) throw new Error(`Label purchase failed: ${purchaseRes.status} ${await purchaseRes.text()}`);
const shipment = await purchaseRes.json(); // HTTP 201

Returns the shipment object with HTTP 201.


POST /api/v1/shipments/:id/void

Requests a void and refund of a purchased label. Carriers enforce a void window (typically 28 days for USPS; varies by carrier). Once a package has been scanned by the carrier, voiding may be rejected upstream.

Scope required: shipments.write

FieldRequiredDescription
reasonnoOptional reason string (defaults to "manual")
Terminal window
curl -X POST "https://app.benchkey.com/api/v1/shipments/88/void" \
-H "Authorization: Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa" \
-H "Content-Type: application/json" \
-d '{ "reason": "Customer picked up in store" }'
const res = await fetch("https://app.benchkey.com/api/v1/shipments/88/void", {
method: "POST",
headers: {
Authorization: "Bearer bk_live_01J8X4_kvWz9nP3mRqTs7uYeBfGhDjLa",
"Content-Type": "application/json",
},
body: JSON.stringify({ reason: "Customer picked up in store" }),
});
const shipment = await res.json();

HTTP 200 returns the shipment object with voided_at set after the void is confirmed. HTTP 202 returns the shipment with a pending provider refund request; voided_at remains null while confirmation is outstanding.

Read shippo_refund_status and retrieve the shipment again to check the outcome. REQUESTING, QUEUED, and PENDING do not establish a completed refund. Confirm SUCCESS and a populated voided_at before treating a Shippo label as refunded; inspect shippo_refund_error if the status is ERROR.


HTTP statusCodeMeaning
400invalid_queryA list filter parameter (ticket_id, carrier, provider, voided) was supplied as an array or object instead of a scalar value, or voided is not a recognized boolean (true/false/1/0)
400invalid_cursorThe cursor value is malformed
400invalid_idThe :id path parameter is not a valid identifier
400invalid_fieldA field value is wrong type or invalid, from_address, to_address, or parcel is not an object (POST rates); rate.provider is not one of "shippo", "ups_direct"; rate.amount is not a positive decimal representable at 2 dp; rate.currency is not a 3-letter code; reason is not a string (void); ticket_id in body is not a string; carrier or provider filter exceeds 255 characters
400missing_fieldRequired label-purchase field is absent, or ticket_id is missing for an inbound quote, inbound label, or Shippo purchase
403location_requiredA location-restricted key omitted the ticket required to establish location access
422quote_intent_requiredThe selected rate omitted intentToken; request fresh rates and pass the complete selected rate
422quote_intent_<reason>The quote token could not be verified, for example quote_intent_malformed; request fresh rates
409quote_intent_expiredThe quote expired; request fresh rates and select again
409quote_intent_ticket_mismatch, quote_intent_direction_mismatchThe purchase ticket or direction differs from the quote
404not_foundNo shipment with that ID exists; or ticket_id on label purchase or rates refers to a hidden or nonexistent ticket
409conflictLabel purchase is already in progress; or void was rejected because the package has already been scanned by the carrier
422rate_failedCarrier rate lookup failed (check address/parcel details)
422purchase_failedCarrier label purchase failed
422void_failedCarrier rejected the void request (label may have been scanned)
403insufficient_scopeAPI key lacks shipments.read or shipments.write

See Errors for the full error envelope format.

Status do sistema