Authorization: ShippoToken <token> header — not Bearer. Create one at https://goshippo.com/user/apikeys/; a token starting shippo_test_ acts in test mode, where labels are free and carriers are simulated. Every request this plugin sends also carries SHIPPO-API-VERSION: 2018-02-08, so behaviour cannot drift to whatever “latest” means.
Things to know before you ship:
- Weights and dimensions are strings. Shippo types
weight,length,widthandheightas decimal strings ("12","5.5"), and the plugin keeps them that way. A number is rejected rather than silently coerced. - Async is the default.
shipments.createandtransactions.createreturn before the work is done unless you passasync: false. A shipment created that way hasstatus: QUEUEDand norates; read them withshipments.rates. A transaction withoutlabel_urlis still being bought. - A label purchase spends money.
transactions.createcharges the account’s carrier for the chosen rate, andbatches.purchasecharges once per shipment. Neither is retried on a 5xx, since the carrier may already have applied it.refunds.createis the way back, and the carrier can refuse. label_urlexpires. ItsExpiresquery parameter is a Unix timestamp; re-read the transaction withtransactions.getrather than reusing an old URL.- Rate limits are per endpoint and per verb, per minute, and much tighter in test mode — a list request allows 10 a minute in test, 50 in live. A 429 is retried with a 60-second wait, since every window is a minute.
- Auth is checked before routing. An unknown path answers 401, not 404, so a 401 always means the token.
- A bad country is silently truncated. Shippo accepted
country: "USA"and storedUS, answering 201, so the two-letter rule is enforced here instead — a typo fails before it reaches an address you then ship to. - Route webhook deliveries per tenant in the URL. A Shippo delivery carries no account id, so
processWebhookfalls back to thedefaulttenant. Put?tenantId=on each subscription URL and pass{ tenantId }as the fourth argument; read the query fromrequest.url, and read the body withrequest.text()rather thanrequest.json(). - Webhooks carry the whole object. The body is
{ event, test, data }, wheredatais the full track, transaction or batch — not a notification pointing at one. - Webhook security is opt-in, and this plugin verifies HMAC when you configure a secret. Shippo offers three protections: an inbound IP allowlist, a self-generated
?token=on the subscription URL, and HMAC. HMAC is arranged with Shippo support (it is not a field onPOST /webhooks) and they issue a shared secret; set it aswebhookSecretor store it withset_webhook_signature, and the plugin checksShippo-Auth-Signature— SHA-256 over<timestamp>.<raw body>— on every delivery, in constant time. With no secret configured, deliveries are accepted unsigned and the handler reportssignatureVerified: false, so an unchecked delivery is visible rather than implied. Either way the receiving URL must be private, and the raw body must reachprocessWebhookas text for verification to work. - One webhook per label. Shippo creates a tracking webhook automatically for every label it sells, so
webhooks.listgrows on its own.
- 57 typed API operations
- 1 incoming webhook event types
Setup
1
Install
2
Add the plugin
corsair.ts
multiTenancy: true, scope calls with corsair.withTenant(id). See Quick start for KEK + Hub keys, and Multi-tenancy for account isolation.3
Choose authentication
- API Key (Recommended)
Create a token at your Shippo API settings page and store it with More: API Key
set_api_key, or pass key to shippo(). A token starting shippo_test_ acts in test mode, where labels are free and carriers are simulated. A call with no saved key throws AuthMissingError, whose connectUrl sends the tenant to the connect page.If Shippo issued you an HMAC secret for webhooks, pass it as webhookSecret or store it with set_webhook_signature; deliveries are then verified before any hook runs.4
Connect a tenant
Mint a connect link and send the tenant to it. Hub hosts the page and delivers the result to your app — see Connect / OAuth.
Example API calls
List recent shipmentsWebhooks
This plugin registers 1 webhook event type. Configure the provider to POST to your Corsair HTTP handler, then usewebhookHooks on the plugin factory.
See Webhooks for every event path and payload shape, and Webhooks concept for routing.
What’s next
API reference
Every
shippo.api.* operation with input and output types.Webhooks
Event paths, payloads, and
webhookHooks examples.Connect / OAuth
createLink, Hub delivery, and tenant connect flows.
Use with agents
Expose this plugin’s operations as MCP tools.