Documentation
Everything you need to integrate the rateship SDK into your Node backend.
Everything you need to integrate the rateship SDK into your Node backend.
client.createLabel(rate) takes a NormalizedRate returned by getRates() and purchases it through the correct provider in a single API call. Label purchase is single-provider, so any failure throws, no partial-success shape.
import { RateShipError } from "rateship";
const { rates } = await client.getRates(request);
// Pick a rate (cheapest, specific carrier, whatever).
const chosen = rates.find((r) => r.carrier === "USPS") ?? rates[0];
try {
const label = await client.createLabel(chosen);
console.log(label.tracking_number); // "9400..."
console.log(label.label_url); // https://... PDF
console.log(label.label_id); // provider-native identifier
} catch (err) {
if (err instanceof RateShipError) {
console.error(`[${err.provider}] ${err.code}: ${err.message}`);
}
}Each provider stores rate-to-shipment linkage differently. The adapter reads provider-native identifiers from rate.raw , Shippo uses object_id, EasyPost needs shipment_id plus id, ShipEngine uses the top-level rate_id. Passing the whole NormalizedRate lets the SDK do the right thing without you caring about provider internals.
If you reconstruct a rate manually (missing the raw fields), you get a VALIDATION_ERROR explaining what's missing.
interface Label {
provider: "easypost" | "shippo" | "shipengine";
carrier: string;
service: string;
price_cents: number;
currency: "USD";
tracking_number: string; // required, success guarantees this exists
label_url: string; // required, PDF URL
label_id: string; // provider-native identifier (for support)
rate_id: string; // the rate this label was purchased from
created_at: string; // ISO timestamp (SDK-generated)
raw: object; // full provider response
}Providers expire rates after a while (Shippo keeps them for days, EasyPost similar). If a rate is stale when you call createLabel, you get a PROVIDER_ERROR. Treat it as "refetch rates and try again", not a fatal state.
The second arg to createLabel(rate, options?) is reserved. Provider-specific options (PDF vs PNG vs ZPL format, insurance, return labels, customs forms) land in v2.1+. At v2.0 the SDK requests standard PDF labels from each provider.
Next: Webhooks.
client.createLabel(rate) takes a NormalizedRate returned by getRates() and purchases it through the correct provider in a single API call. Label purchase is single-provider, so any failure throws, no partial-success shape.
import { RateShipError } from "rateship";
const { rates } = await client.getRates(request);
// Pick a rate (cheapest, specific carrier, whatever).
const chosen = rates.find((r) => r.carrier === "USPS") ?? rates[0];
try {
const label = await client.createLabel(chosen);
console.log(label.tracking_number); // "9400..."
console.log(label.label_url); // https://... PDF
console.log(label.label_id); // provider-native identifier
} catch (err) {
if (err instanceof RateShipError) {
console.error(`[${err.provider}] ${err.code}: ${err.message}`);
}
}Each provider stores rate-to-shipment linkage differently. The adapter reads provider-native identifiers from rate.raw , Shippo uses object_id, EasyPost needs shipment_id plus id, ShipEngine uses the top-level rate_id. Passing the whole NormalizedRate lets the SDK do the right thing without you caring about provider internals.
If you reconstruct a rate manually (missing the raw fields), you get a VALIDATION_ERROR explaining what's missing.
interface Label {
provider: "easypost" | "shippo" | "shipengine";
carrier: string;
service: string;
price_cents: number;
currency: "USD";
tracking_number: string; // required, success guarantees this exists
label_url: string; // required, PDF URL
label_id: string; // provider-native identifier (for support)
rate_id: string; // the rate this label was purchased from
created_at: string; // ISO timestamp (SDK-generated)
raw: object; // full provider response
}Providers expire rates after a while (Shippo keeps them for days, EasyPost similar). If a rate is stale when you call createLabel, you get a PROVIDER_ERROR. Treat it as "refetch rates and try again", not a fatal state.
The second arg to createLabel(rate, options?) is reserved. Provider-specific options (PDF vs PNG vs ZPL format, insurance, return labels, customs forms) land in v2.1+. At v2.0 the SDK requests standard PDF labels from each provider.
Next: Webhooks.