@teralabs/shipstack 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +418 -0
- package/dist/index.d.mts +2741 -0
- package/dist/index.d.ts +2741 -0
- package/dist/index.js +9431 -0
- package/dist/index.mjs +9380 -0
- package/docs/API.md +176 -0
- package/docs/address-validation.md +114 -0
- package/docs/architecture.md +220 -0
- package/docs/carriers.md +180 -0
- package/docs/config.example.ts +32 -0
- package/docs/errors.md +150 -0
- package/docs/examples/actual-shipment.ts +59 -0
- package/docs/examples/basic.ts +131 -0
- package/docs/examples/nextjs-route.ts +22 -0
- package/docs/examples/staged-shipment.md +46 -0
- package/docs/index.md +286 -0
- package/docs/rates.md +145 -0
- package/docs/shipments.md +157 -0
- package/docs/tracking.md +115 -0
- package/package.json +98 -0
package/docs/API.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Shipstack API Reference
|
|
2
|
+
|
|
3
|
+
This document covers the public Shipstack surface shipped through the npm entrypoint: `ShippingClient`, `ShippingManager`, the functional API, exported rate clients, and the core types.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## ShippingClient
|
|
8
|
+
|
|
9
|
+
The `ShippingClient` is the primary stateful API for direct per-carrier operations.
|
|
10
|
+
|
|
11
|
+
### Constructor
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
new ShippingClient(config: ShippingConfig)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Methods
|
|
18
|
+
|
|
19
|
+
#### `getRates(request: RateRequest): Promise<NormalizedRate[]>`
|
|
20
|
+
Fetches normalized rates for the carrier specified on `request.carrier`.
|
|
21
|
+
|
|
22
|
+
#### `validateAddress(request: AddressValidationRequest): Promise<AddressValidationResult>`
|
|
23
|
+
Validates an address using the selected carrier.
|
|
24
|
+
|
|
25
|
+
#### `track(trackingNumbers: string | string[], carrier: Carrier): Promise<NormalizedTracking[]>`
|
|
26
|
+
Tracks one or more shipments with automatic batching and normalization.
|
|
27
|
+
|
|
28
|
+
#### `getBestValue(request: RateRequest): Promise<NormalizedRate | null>`
|
|
29
|
+
Returns the cheapest rate for the selected carrier.
|
|
30
|
+
|
|
31
|
+
#### `getFastest(request: RateRequest): Promise<NormalizedRate | null>`
|
|
32
|
+
Returns the fastest rate for the selected carrier.
|
|
33
|
+
|
|
34
|
+
#### `buildShipment(request: ShipmentRequest): Promise<StagedShipment>`
|
|
35
|
+
Builds a carrier-specific shipment payload without purchasing a label.
|
|
36
|
+
|
|
37
|
+
#### `createShipment(request: ShipmentRequest): Promise<NormalizedShipment>`
|
|
38
|
+
Creates an actual shipment and purchases a real label.
|
|
39
|
+
|
|
40
|
+
#### `ShippingClient.predict(trackingNumber: string): Carrier | "unknown"`
|
|
41
|
+
Static helper that predicts the carrier from a tracking number pattern.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## ShippingManager
|
|
46
|
+
|
|
47
|
+
`ShippingManager` is the advanced stateful helper for cross-carrier checkout workflows.
|
|
48
|
+
|
|
49
|
+
### Constructor
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
new ShippingManager(config: ShippingConfig)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Methods
|
|
56
|
+
|
|
57
|
+
#### `getRankedRates(request: Omit<RateRequest, "carrier">, carriers: Carrier[]): Promise<NormalizedRate[]>`
|
|
58
|
+
Queries multiple carriers, merges the results, sorts by price, and marks `isCheapest` and `isFastest` when available.
|
|
59
|
+
|
|
60
|
+
#### `validateAddress(request: AddressValidationRequest): Promise<AddressValidationResult>`
|
|
61
|
+
Convenience wrapper around the address API using the manager's config.
|
|
62
|
+
|
|
63
|
+
#### `track(trackingNumbers: string | string[], carrier: Carrier): Promise<NormalizedTracking[]>`
|
|
64
|
+
Convenience wrapper around the tracking API using the manager's config.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Functional API
|
|
69
|
+
|
|
70
|
+
The functional API exposes stateless helpers when you do not want to retain a client instance.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
getRates(request: RateRequest, config: ShippingConfig): Promise<NormalizedRate[]>
|
|
74
|
+
validateAddress(request: AddressValidationRequest, config: ShippingConfig): Promise<AddressValidationResult>
|
|
75
|
+
trackShipment(trackingNumbers: string | string[], carrier: Carrier, config: ShippingConfig): Promise<NormalizedTracking[]>
|
|
76
|
+
getBestValueRate(request: RateRequest, config: ShippingConfig): Promise<NormalizedRate | null>
|
|
77
|
+
getFastestService(request: RateRequest, config: ShippingConfig): Promise<NormalizedRate | null>
|
|
78
|
+
predictCarrier(trackingNumber: string): Carrier | "unknown"
|
|
79
|
+
buildShipment(request: ShipmentRequest, config: ShippingConfig): Promise<StagedShipment>
|
|
80
|
+
createShipment(request: ShipmentRequest, config: ShippingConfig): Promise<NormalizedShipment>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Direct Carrier Access
|
|
86
|
+
|
|
87
|
+
Only the low-level rate clients are exported directly from the package entrypoint.
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
createUspsRatesClient(config: UspsConfig)
|
|
91
|
+
createFedexRatesClient(config: FedexConfig)
|
|
92
|
+
createUpsRatesClient(config: UpsConfig)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Core Types
|
|
98
|
+
|
|
99
|
+
### `ShippingConfig`
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
type ShippingConfig = {
|
|
103
|
+
environment?: "sandbox" | "production";
|
|
104
|
+
usps?: {
|
|
105
|
+
enabled: boolean;
|
|
106
|
+
clientId: string;
|
|
107
|
+
clientSecret: string;
|
|
108
|
+
apiKey?: string;
|
|
109
|
+
baseUrl?: string;
|
|
110
|
+
authUrl?: string;
|
|
111
|
+
labelsBaseUrl?: string;
|
|
112
|
+
};
|
|
113
|
+
fedex?: {
|
|
114
|
+
enabled: boolean;
|
|
115
|
+
clientId: string;
|
|
116
|
+
clientSecret: string;
|
|
117
|
+
accountNumber: string;
|
|
118
|
+
baseUrl?: string;
|
|
119
|
+
};
|
|
120
|
+
ups?: {
|
|
121
|
+
enabled: boolean;
|
|
122
|
+
clientId: string;
|
|
123
|
+
clientSecret: string;
|
|
124
|
+
accountNumber: string;
|
|
125
|
+
baseUrl?: string;
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### `RateRequest`
|
|
131
|
+
Origin, destination, weight, and package dimensions for a single-carrier rate lookup.
|
|
132
|
+
|
|
133
|
+
### `AddressValidationRequest`
|
|
134
|
+
Carrier plus a structured postal address using `streetLines`, `stateOrProvinceCode`, and `countryCode`.
|
|
135
|
+
|
|
136
|
+
### `AddressValidationResult`
|
|
137
|
+
Top-level address validation response with `isValid`, optional `normalizedAddress`, and optional `messages`.
|
|
138
|
+
|
|
139
|
+
### `NormalizedRate`
|
|
140
|
+
Unified rate object returned by all carriers.
|
|
141
|
+
|
|
142
|
+
### `NormalizedTracking`
|
|
143
|
+
Normalized tracking status and event history.
|
|
144
|
+
|
|
145
|
+
### `ShipmentRequest`
|
|
146
|
+
Carrier, service, from/to addresses, and package details required to build or purchase a shipment.
|
|
147
|
+
|
|
148
|
+
### `StagedShipment`
|
|
149
|
+
Carrier-specific payload returned by `buildShipment`.
|
|
150
|
+
|
|
151
|
+
### `NormalizedShipment`
|
|
152
|
+
Unified shipment result returned by `createShipment`.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Error Types
|
|
157
|
+
|
|
158
|
+
### `ShipstackError`
|
|
159
|
+
Base error type for Shipstack operations.
|
|
160
|
+
|
|
161
|
+
Properties include:
|
|
162
|
+
|
|
163
|
+
- `message`
|
|
164
|
+
- `carrier` (`"usps" | "fedex" | "ups"`)
|
|
165
|
+
- `cause`
|
|
166
|
+
|
|
167
|
+
### `ThrottlingError`
|
|
168
|
+
Extends `ShipstackError` and adds `retryAfter?: number` when a carrier returns a throttling signal.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Full Type Definitions
|
|
173
|
+
|
|
174
|
+
The published package exposes its full TypeScript surface through `dist/index.d.ts` and `dist/index.d.mts`.
|
|
175
|
+
|
|
176
|
+
---
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Address Validation in Shipstack
|
|
2
|
+
|
|
3
|
+
Shipstack provides a unified interface for validating addresses across USPS, FedEx, and UPS.
|
|
4
|
+
All carrier responses are normalized into a consistent structure, regardless of the underlying API.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Request Structure
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
type AddressValidationRequest = {
|
|
12
|
+
carrier: "usps" | "fedex" | "ups";
|
|
13
|
+
address: {
|
|
14
|
+
streetLines: string[];
|
|
15
|
+
city: string;
|
|
16
|
+
stateOrProvinceCode: string;
|
|
17
|
+
postalCode: string;
|
|
18
|
+
countryCode: string;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Notes
|
|
24
|
+
|
|
25
|
+
- `streetLines` supports multiple address lines.
|
|
26
|
+
- `countryCode` should be an ISO-3166-1 alpha-2 code such as `US`.
|
|
27
|
+
- USPS, FedEx, and UPS may each return different levels of correction detail, but Shipstack normalizes the top-level response shape.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Example
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
const result = await client.validateAddress({
|
|
35
|
+
carrier: "fedex",
|
|
36
|
+
address: {
|
|
37
|
+
streetLines: ["123 Main St"],
|
|
38
|
+
city: "New York",
|
|
39
|
+
stateOrProvinceCode: "NY",
|
|
40
|
+
postalCode: "10001",
|
|
41
|
+
countryCode: "US"
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Using the Functional API
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { validateAddress } from "@teralabs/shipstack";
|
|
52
|
+
|
|
53
|
+
const result = await validateAddress(request, config);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Address Validation Result
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
type AddressValidationResult = {
|
|
62
|
+
isValid: boolean;
|
|
63
|
+
normalizedAddress?: {
|
|
64
|
+
street1: string;
|
|
65
|
+
street2?: string;
|
|
66
|
+
city: string;
|
|
67
|
+
state: string;
|
|
68
|
+
postalCode: string;
|
|
69
|
+
country: string;
|
|
70
|
+
isValid: boolean;
|
|
71
|
+
classification?: "RESIDENTIAL" | "COMMERCIAL" | "UNKNOWN";
|
|
72
|
+
isPoBox: boolean;
|
|
73
|
+
raw?: unknown;
|
|
74
|
+
};
|
|
75
|
+
messages?: string[];
|
|
76
|
+
raw?: unknown;
|
|
77
|
+
};
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Field Notes
|
|
81
|
+
|
|
82
|
+
- `isValid` indicates whether the carrier considers the address deliverable.
|
|
83
|
+
- `normalizedAddress` is included when the carrier returns a standardized address shape.
|
|
84
|
+
- `messages` may include warnings, suggestions, or correction notes.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Carrier-Specific Behavior
|
|
89
|
+
|
|
90
|
+
### USPS
|
|
91
|
+
- Often returns corrected addresses.
|
|
92
|
+
- May normalize abbreviations and formatting.
|
|
93
|
+
|
|
94
|
+
### FedEx
|
|
95
|
+
- Provides detailed validation messages.
|
|
96
|
+
- May classify an address as residential or commercial.
|
|
97
|
+
|
|
98
|
+
### UPS
|
|
99
|
+
- Uses stricter validation rules for some addresses.
|
|
100
|
+
- May require more complete postal information in edge cases.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Summary
|
|
105
|
+
|
|
106
|
+
Shipstack's address validation system provides:
|
|
107
|
+
|
|
108
|
+
- A unified request format
|
|
109
|
+
- A normalized top-level result
|
|
110
|
+
- Optional corrected address data
|
|
111
|
+
- Carrier-specific messages when available
|
|
112
|
+
- Compatibility with both stateful and functional APIs
|
|
113
|
+
|
|
114
|
+
---
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Shipstack Architecture
|
|
2
|
+
|
|
3
|
+
Shipstack is built around a layered, modular architecture designed for reliability, type‑safety, and framework‑agnostic operation.
|
|
4
|
+
This document explains how the system is structured internally and how each layer interacts with the others.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Shipstack consists of four major layers:
|
|
11
|
+
|
|
12
|
+
1. **Generated Clients** (OpenAPI‑based, carrier‑specific)
|
|
13
|
+
2. **API Layer** (request builders + converters)
|
|
14
|
+
3. **Aggregators** (unified workflows)
|
|
15
|
+
4. **Public API** (stateful + functional interfaces)
|
|
16
|
+
|
|
17
|
+
This structure ensures:
|
|
18
|
+
|
|
19
|
+
- Strong type‑safety
|
|
20
|
+
- Predictable behavior
|
|
21
|
+
- Easy extensibility
|
|
22
|
+
- Clear separation of concerns
|
|
23
|
+
- Zero framework assumptions
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 1. Generated Clients
|
|
28
|
+
|
|
29
|
+
Location:
|
|
30
|
+
```
|
|
31
|
+
src/usps/generated/
|
|
32
|
+
src/fedex/generated/
|
|
33
|
+
src/ups/generated/
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
These clients are auto‑generated from official OpenAPI specifications.
|
|
37
|
+
|
|
38
|
+
### Responsibilities
|
|
39
|
+
|
|
40
|
+
- Define raw request/response types
|
|
41
|
+
- Handle low‑level HTTP calls
|
|
42
|
+
- Match carrier API behavior exactly
|
|
43
|
+
- Never contain business logic
|
|
44
|
+
|
|
45
|
+
### Notes
|
|
46
|
+
|
|
47
|
+
- These files should never be edited manually.
|
|
48
|
+
- Regenerate using `npm run generate:*`.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 2. API Layer
|
|
53
|
+
|
|
54
|
+
Location:
|
|
55
|
+
```
|
|
56
|
+
src/usps/
|
|
57
|
+
src/fedex/
|
|
58
|
+
src/ups/
|
|
59
|
+
src/api/
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
This layer wraps the generated clients and provides:
|
|
63
|
+
|
|
64
|
+
### Request Builders
|
|
65
|
+
Convert normalized Shipstack requests into carrier‑specific formats.
|
|
66
|
+
|
|
67
|
+
Examples:
|
|
68
|
+
- `buildUspsRateRequest`
|
|
69
|
+
- `buildFedexShipRequest`
|
|
70
|
+
- `buildUpsTrackingRequest`
|
|
71
|
+
|
|
72
|
+
### Converters
|
|
73
|
+
Normalize carrier responses into Shipstack’s unified types.
|
|
74
|
+
|
|
75
|
+
Examples:
|
|
76
|
+
- `convertUspsRateResponse`
|
|
77
|
+
- `convertFedexTrackingResponse`
|
|
78
|
+
- `convertUpsShipResponse`
|
|
79
|
+
|
|
80
|
+
### Responsibilities
|
|
81
|
+
|
|
82
|
+
- Authentication
|
|
83
|
+
- Request shaping
|
|
84
|
+
- Response normalization
|
|
85
|
+
- Error mapping
|
|
86
|
+
- Carrier‑specific quirks
|
|
87
|
+
|
|
88
|
+
This layer ensures all carriers behave consistently from the outside.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 3. Aggregators
|
|
93
|
+
|
|
94
|
+
Location:
|
|
95
|
+
```
|
|
96
|
+
src/aggregator/
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Aggregators orchestrate multi‑step workflows and unify behavior across carriers.
|
|
100
|
+
|
|
101
|
+
### Types of Aggregators
|
|
102
|
+
|
|
103
|
+
#### Address Aggregator
|
|
104
|
+
- Calls the correct carrier validation API
|
|
105
|
+
- Normalizes results
|
|
106
|
+
- Handles corrections and messages
|
|
107
|
+
|
|
108
|
+
#### Rates Aggregator
|
|
109
|
+
- Calls the correct carrier rate API
|
|
110
|
+
- Normalizes rates
|
|
111
|
+
- Supports ranking helpers
|
|
112
|
+
|
|
113
|
+
#### Tracking Aggregator
|
|
114
|
+
- Handles batching (USPS 35, FedEx 30)
|
|
115
|
+
- Handles UPS single‑inquiry limits
|
|
116
|
+
- Normalizes tracking events
|
|
117
|
+
|
|
118
|
+
#### Shipment Aggregator
|
|
119
|
+
- **Staged mode** → builds carrier payloads
|
|
120
|
+
- **Actual mode** → purchases labels
|
|
121
|
+
- Normalizes shipment results
|
|
122
|
+
|
|
123
|
+
### Responsibilities
|
|
124
|
+
|
|
125
|
+
- Multi‑carrier orchestration
|
|
126
|
+
- Concurrency control
|
|
127
|
+
- Error handling
|
|
128
|
+
- Workflow consistency
|
|
129
|
+
|
|
130
|
+
Aggregators are the “brains” of Shipstack.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 4. Public API
|
|
135
|
+
|
|
136
|
+
Shipstack exposes two public interfaces:
|
|
137
|
+
|
|
138
|
+
### Stateful API
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
const client = new ShippingClient(config);
|
|
142
|
+
const manager = new ShippingManager(config);
|
|
143
|
+
|
|
144
|
+
client.getRates(...)
|
|
145
|
+
client.track(...)
|
|
146
|
+
client.buildShipment(...)
|
|
147
|
+
client.createShipment(...)
|
|
148
|
+
manager.getRankedRates(...)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Functional API
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
getRates(request, config)
|
|
155
|
+
validateAddress(request, config)
|
|
156
|
+
trackShipment(numbers, carrier, config)
|
|
157
|
+
buildShipment(request, config)
|
|
158
|
+
createShipment(request, config)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Both APIs call into the same aggregator layer.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Data Flow
|
|
166
|
+
|
|
167
|
+
Here’s how a typical request moves through the system:
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
User Request
|
|
171
|
+
↓
|
|
172
|
+
Public API (stateful or functional)
|
|
173
|
+
↓
|
|
174
|
+
Aggregator (rates, tracking, address, shipment)
|
|
175
|
+
↓
|
|
176
|
+
API Layer (request builder + converter)
|
|
177
|
+
↓
|
|
178
|
+
Generated Client (raw HTTP)
|
|
179
|
+
↓
|
|
180
|
+
Carrier API (USPS/FedEx/UPS)
|
|
181
|
+
↓
|
|
182
|
+
Response normalized and returned
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Why This Architecture?
|
|
188
|
+
|
|
189
|
+
### Predictability
|
|
190
|
+
Every carrier behaves differently — Shipstack makes them behave the same.
|
|
191
|
+
|
|
192
|
+
### Extensibility
|
|
193
|
+
Adding a new carrier only requires:
|
|
194
|
+
- Generated client
|
|
195
|
+
- Request builders
|
|
196
|
+
- Converters
|
|
197
|
+
- Aggregator hooks
|
|
198
|
+
|
|
199
|
+
### Safety
|
|
200
|
+
Staged shipments allow safe, frontend‑compatible workflows.
|
|
201
|
+
|
|
202
|
+
### Performance
|
|
203
|
+
Batching, concurrency limits, and request shaping ensure efficient API usage.
|
|
204
|
+
|
|
205
|
+
### Portability
|
|
206
|
+
No reliance on Node.js APIs — works in any JS runtime.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Summary
|
|
211
|
+
|
|
212
|
+
Shipstack’s architecture provides:
|
|
213
|
+
|
|
214
|
+
- A clean separation between raw carrier APIs and normalized workflows
|
|
215
|
+
- A consistent developer experience across USPS, FedEx, and UPS
|
|
216
|
+
- Safe and advanced shipment modes
|
|
217
|
+
- Strong type‑safety at every layer
|
|
218
|
+
- A runtime‑agnostic design suitable for modern web platforms
|
|
219
|
+
|
|
220
|
+
---
|
package/docs/carriers.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Carrier‑Specific Behavior in Shipstack
|
|
2
|
+
|
|
3
|
+
Shipstack provides a unified API across USPS, FedEx, and UPS — but each carrier has unique rules, quirks, and constraints.
|
|
4
|
+
This document explains those differences and how Shipstack handles them internally.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Overview
|
|
9
|
+
|
|
10
|
+
Shipstack normalizes:
|
|
11
|
+
|
|
12
|
+
- Rate structures
|
|
13
|
+
- Tracking events
|
|
14
|
+
- Address validation responses
|
|
15
|
+
- Shipment payloads
|
|
16
|
+
- Error formats
|
|
17
|
+
|
|
18
|
+
But each carrier still has its own behaviors that developers should understand.
|
|
19
|
+
|
|
20
|
+
This guide covers:
|
|
21
|
+
|
|
22
|
+
- USPS
|
|
23
|
+
- FedEx
|
|
24
|
+
- UPS
|
|
25
|
+
- How Shipstack smooths out inconsistencies
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
# USPS
|
|
30
|
+
|
|
31
|
+
USPS is the most rigid and rule‑driven carrier.
|
|
32
|
+
It has strict formatting requirements and limited metadata in some APIs.
|
|
33
|
+
|
|
34
|
+
## Strengths
|
|
35
|
+
|
|
36
|
+
- Excellent address correction
|
|
37
|
+
- Fast, lightweight APIs
|
|
38
|
+
- Strong support for small parcels
|
|
39
|
+
- Predictable service codes
|
|
40
|
+
|
|
41
|
+
## Limitations
|
|
42
|
+
|
|
43
|
+
- Tracking events may lack timestamps
|
|
44
|
+
- Delivery estimates are not always provided
|
|
45
|
+
- Bulk tracking limit: **35 items**
|
|
46
|
+
- Some services ignore dimensions
|
|
47
|
+
|
|
48
|
+
## Shipstack Behavior
|
|
49
|
+
|
|
50
|
+
- Automatically chunks tracking requests into 35‑item batches
|
|
51
|
+
- Normalizes missing timestamps to `null`
|
|
52
|
+
- Maps USPS service names to consistent `serviceCode` values
|
|
53
|
+
- Normalizes address corrections into a unified structure
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
# FedEx
|
|
58
|
+
|
|
59
|
+
FedEx provides the richest data across all carriers, especially for tracking and shipments.
|
|
60
|
+
|
|
61
|
+
## Strengths
|
|
62
|
+
|
|
63
|
+
- Detailed tracking events
|
|
64
|
+
- Reliable delivery estimates
|
|
65
|
+
- Strong international support
|
|
66
|
+
- Rich shipment metadata
|
|
67
|
+
|
|
68
|
+
## Limitations
|
|
69
|
+
|
|
70
|
+
- Requires `accountNumber` for almost all operations
|
|
71
|
+
- Rate requests may require dimensions even for small parcels
|
|
72
|
+
- Tracking batch limit: **30 items**
|
|
73
|
+
|
|
74
|
+
## Shipstack Behavior
|
|
75
|
+
|
|
76
|
+
- Automatically chunks tracking requests into 30‑item batches
|
|
77
|
+
- Normalizes FedEx’s verbose response structure
|
|
78
|
+
- Maps FedEx service codes to unified names
|
|
79
|
+
- Converts FedEx timestamps into ISO strings
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
# UPS
|
|
84
|
+
|
|
85
|
+
UPS is the strictest carrier in terms of authentication and rate limiting.
|
|
86
|
+
|
|
87
|
+
## Strengths
|
|
88
|
+
|
|
89
|
+
- Reliable tracking
|
|
90
|
+
- Strong commercial shipping support
|
|
91
|
+
- Good dimensional pricing
|
|
92
|
+
|
|
93
|
+
## Limitations
|
|
94
|
+
|
|
95
|
+
- Tracking is **single‑inquiry only**
|
|
96
|
+
- Aggressive rate limiting
|
|
97
|
+
- Requires `accountNumber` for most services
|
|
98
|
+
- OAuth tokens expire quickly
|
|
99
|
+
|
|
100
|
+
## Shipstack Behavior
|
|
101
|
+
|
|
102
|
+
- Automatically throttles UPS tracking to avoid lockouts
|
|
103
|
+
- Ensures single‑inquiry tracking is respected
|
|
104
|
+
- Normalizes UPS’s deeply nested response structure
|
|
105
|
+
- Converts UPS error codes into `ShipstackError`
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
# Comparison Table
|
|
110
|
+
|
|
111
|
+
| Feature | USPS | FedEx | UPS |
|
|
112
|
+
|--------|------|--------|------|
|
|
113
|
+
| Tracking batch size | 35 | 30 | 1 |
|
|
114
|
+
| Address correction | Strong | Moderate | Moderate |
|
|
115
|
+
| Delivery estimates | Inconsistent | Strong | Strong |
|
|
116
|
+
| Requires account number | No | Yes | Yes |
|
|
117
|
+
| Rate limiting | Low | Moderate | High |
|
|
118
|
+
| International support | Limited | Strong | Strong |
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
# How Shipstack Normalizes Carrier Differences
|
|
123
|
+
|
|
124
|
+
Shipstack smooths out inconsistencies by:
|
|
125
|
+
|
|
126
|
+
### 1. Normalizing Field Names
|
|
127
|
+
All carriers return:
|
|
128
|
+
|
|
129
|
+
- `serviceCode`
|
|
130
|
+
- `serviceName`
|
|
131
|
+
- `deliveryDays`
|
|
132
|
+
- `cost.amount`
|
|
133
|
+
- `cost.currency`
|
|
134
|
+
|
|
135
|
+
### 2. Normalizing Tracking Events
|
|
136
|
+
Regardless of carrier, tracking events become:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
{
|
|
140
|
+
description: string;
|
|
141
|
+
dateTime: string;
|
|
142
|
+
city?: string;
|
|
143
|
+
stateOrProvinceCode?: string;
|
|
144
|
+
postalCode?: string;
|
|
145
|
+
countryCode?: string;
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 3. Handling Concurrency and Batching
|
|
150
|
+
Shipstack automatically:
|
|
151
|
+
|
|
152
|
+
- Chunks USPS and FedEx tracking
|
|
153
|
+
- Throttles UPS tracking
|
|
154
|
+
- Ensures safe parallel execution
|
|
155
|
+
|
|
156
|
+
### 4. Mapping Errors
|
|
157
|
+
All carrier errors become `ShipstackError` with:
|
|
158
|
+
|
|
159
|
+
- `carrier`
|
|
160
|
+
- `message`
|
|
161
|
+
- `cause`
|
|
162
|
+
|
|
163
|
+
### 5. Unifying Shipment Workflows
|
|
164
|
+
Both staged and actual shipments use the same request structure, regardless of carrier.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
# Summary
|
|
169
|
+
|
|
170
|
+
Shipstack abstracts away the differences between USPS, FedEx, and UPS by:
|
|
171
|
+
|
|
172
|
+
- Normalizing data
|
|
173
|
+
- Handling batching and throttling
|
|
174
|
+
- Mapping errors
|
|
175
|
+
- Providing consistent request/response structures
|
|
176
|
+
- Supporting both staged and actual shipments
|
|
177
|
+
|
|
178
|
+
This allows you to build multi‑carrier workflows without worrying about carrier‑specific quirks.
|
|
179
|
+
|
|
180
|
+
---
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ShippingConfig } from "@teralabs/shipstack";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shipstack Example Configuration
|
|
5
|
+
*
|
|
6
|
+
* This file demonstrates the configuration structure required
|
|
7
|
+
* for USPS, FedEx, and UPS. All values shown here are placeholders.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const config: ShippingConfig = {
|
|
11
|
+
environment: "sandbox",
|
|
12
|
+
usps: {
|
|
13
|
+
enabled: true,
|
|
14
|
+
clientId: "YOUR_USPS_CLIENT_ID",
|
|
15
|
+
clientSecret: "YOUR_USPS_CLIENT_SECRET",
|
|
16
|
+
baseUrl: "https://sandbox.api.usps.com"
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
fedex: {
|
|
20
|
+
enabled: true,
|
|
21
|
+
clientId: "YOUR_FEDEX_CLIENT_ID",
|
|
22
|
+
clientSecret: "YOUR_FEDEX_CLIENT_SECRET",
|
|
23
|
+
accountNumber: "YOUR_FEDEX_ACCOUNT_NUMBER"
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
ups: {
|
|
27
|
+
enabled: true,
|
|
28
|
+
clientId: "YOUR_UPS_CLIENT_ID",
|
|
29
|
+
clientSecret: "YOUR_UPS_CLIENT_SECRET",
|
|
30
|
+
accountNumber: "YOUR_UPS_ACCOUNT_NUMBER"
|
|
31
|
+
}
|
|
32
|
+
};
|