{"id":"0147a1b8-1e98-4cf6-afd7-0c8573752d6b","entityType":"agent","slug":"clawhub-shippo-shippo","name":"Shippo","canonicalUrl":"https://www.xpersona.co/agent/clawhub-shippo-shippo","canonicalPath":"/agent/clawhub-shippo-shippo","generatedAt":"2026-10-09T21:52:52.907Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-09T17:24:00.916Z","emptyReason":null},"description":"A shipping and logistics skill for Shippo. Get multi-carrier rates (USPS, UPS, FedEx, DHL, 30+), buy domestic and international labels with customs, validate... Skill: Shippo Owner: shippo Summary: A shipping and logistics skill for Shippo. Get multi-carrier rates (USPS, UPS, FedEx, DHL, 30+), buy domestic and international labels with customs, validate... Tags: latest:1.4.4 Version history: v1.4.4 | 2026-07-15T21:42:15.167Z | user Release 1.4.4: hosted OAuth MCP (mcp.shippo.com), 4-tool meta-API with PascalCase operation names. v1.4.3 | 2026-07-15T17:24:21.774Z | user Relea","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 2.2K downloads reported by the source. Last updated 10/9/2026.","installCommand":"clawhub skill install s17eaz4habs7hps35gasgwsn7584y6v3:shippo","sourceUrl":"https://clawhub.ai/shippo/shippo","homepage":"https://clawhub.ai/shippo/skills/shippo","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/shippo/shippo","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/shippo/skills/shippo","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":67,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"A shipping and logistics skill for Shippo. Get multi-carrier rates (USPS, UPS, FedEx, DHL, 30+), buy domestic and international labels with customs, validate..."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-09T17:24:00.916Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T17:24:00.916Z","emptyReason":null},"stars":null,"forks":null,"downloads":2222,"packageName":null,"latestVersion":"1.4.4","tractionLabel":"2.2K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T17:24:00.915Z","emptyReason":null},"lastUpdatedAt":"2026-10-09T17:24:00.916Z","lastCrawledAt":"2026-10-09T17:24:00.915Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-10T17:24:00.915Z","lastVerifiedAt":null,"highlights":[{"version":"1.4.4","createdAt":"2026-07-15T21:42:15.167Z","changelog":"Release 1.4.4: hosted OAuth MCP (mcp.shippo.com), 4-tool meta-API with PascalCase operation names.","fileCount":7,"zipByteSize":36141},{"version":"1.4.3","createdAt":"2026-07-15T17:24:21.774Z","changelog":"Release 1.4.3: hosted OAuth MCP (mcp.shippo.com), 4-tool meta-API with PascalCase operation names.","fileCount":7,"zipByteSize":35586},{"version":"1.4.2","createdAt":"2026-06-26T18:34:18.028Z","changelog":"Release 1.4.2: hosted OAuth MCP (mcp.shippo.com), 4-tool meta-API with PascalCase operation names.","fileCount":7,"zipByteSize":34957},{"version":"1.4.1","createdAt":"2026-06-26T16:08:39.843Z","changelog":"Release 1.4.1: hosted OAuth MCP (mcp.shippo.com), 4-tool meta-API with PascalCase operation names.","fileCount":6,"zipByteSize":33374},{"version":"1.4.0","createdAt":"2026-06-12T20:51:31.194Z","changelog":"Release 1.4.0: hosted OAuth MCP (mcp.shippo.com), 4-tool meta-API with PascalCase operation names.","fileCount":7,"zipByteSize":34783},{"version":"1.3.4","createdAt":"2026-06-09T15:49:33.089Z","changelog":"Release 1.3.4: hosted OAuth MCP (mcp.shippo.com), 4-tool meta-API with PascalCase operation names.","fileCount":7,"zipByteSize":27622},{"version":"1.3.3","createdAt":"2026-06-08T19:52:52.579Z","changelog":"Release 1.3.3: hosted OAuth MCP (mcp.shippo.com), AgentCore 4-tool meta-API, PascalCase operation names.","fileCount":7,"zipByteSize":27468},{"version":"1.3.2","createdAt":"2026-06-08T19:23:02.874Z","changelog":"Release 1.3.2: hosted OAuth MCP (mcp.shippo.com), AgentCore 4-tool meta-API, PascalCase operation names.","fileCount":7,"zipByteSize":27814}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17eaz4habs7hps35gasgwsn7584y6v3:shippo","setupComplexity":"low","setupSteps":["Setup complexity is classified as HIGH. You must provision dedicated cloud infrastructure or an isolated VM. Do not run this directly on your local workstation.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-shippo-shippo/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-shippo-shippo/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-shippo-shippo/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-shippo-shippo/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-shippo-shippo/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-shippo-shippo/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-09T21:52:52.905Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-shippo-shippo/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-shippo-shippo/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-shippo-shippo/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-shippo-shippo/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-09T17:24:00.916Z","emptyReason":null},"readme":"Skill: Shippo\n\nOwner: shippo\n\nSummary: A shipping and logistics skill for Shippo. Get multi-carrier rates (USPS, UPS, FedEx, DHL, 30+), buy domestic and international labels with customs, validate...\n\nTags: latest:1.4.4\n\nVersion history:\n\nv1.4.4 | 2026-07-15T21:42:15.167Z | user\n\nRelease 1.4.4: hosted OAuth MCP (mcp.shippo.com), 4-tool meta-API with PascalCase operation names.\n\nv1.4.3 | 2026-07-15T17:24:21.774Z | user\n\nRelease 1.4.3: hosted OAuth MCP (mcp.shippo.com), 4-tool meta-API with PascalCase operation names.\n\nv1.4.2 | 2026-06-26T18:34:18.028Z | user\n\nRelease 1.4.2: hosted OAuth MCP (mcp.shippo.com), 4-tool meta-API with PascalCase operation names.\n\nv1.4.1 | 2026-06-26T16:08:39.843Z | user\n\nRelease 1.4.1: hosted OAuth MCP (mcp.shippo.com), 4-tool meta-API with PascalCase operation names.\n\nv1.4.0 | 2026-06-12T20:51:31.194Z | user\n\nRelease 1.4.0: hosted OAuth MCP (mcp.shippo.com), 4-tool meta-API with PascalCase operation names.\n\nv1.3.4 | 2026-06-09T15:49:33.089Z | user\n\nRelease 1.3.4: hosted OAuth MCP (mcp.shippo.com), 4-tool meta-API with PascalCase operation names.\n\nv1.3.3 | 2026-06-08T19:52:52.579Z | user\n\nRelease 1.3.3: hosted OAuth MCP (mcp.shippo.com), AgentCore 4-tool meta-API, PascalCase operation names.\n\nv1.3.2 | 2026-06-08T19:23:02.874Z | user\n\nRelease 1.3.2: hosted OAuth MCP (mcp.shippo.com), AgentCore 4-tool meta-API, PascalCase operation names.\n\nv1.3.1 | 2026-06-08T19:04:07.251Z | user\n\nRelease 1.3.1: hosted OAuth MCP (mcp.shippo.com), AgentCore 4-tool meta-API, PascalCase operation names.\n\nv1.1.3 | 2026-05-13T15:52:59.026Z | user\n\nv1.1.3 — Stripped the AUTO-GENERATED HTML-comment banner from the published SKILL.md. The banner is useful for developers browsing the source repo (and still lives in the .template file), but shipping it to ClawHub means it's bytes the registry has to store and tokens any LLM consumer has to parse. CI's check-no-generated-edits.sh already protects against direct edits, so the in-file warning is redundant.\n\nv1.1.2 | 2026-05-12T22:36:54.588Z | user\n\nv1.1.2 — Rewrote the SHIPPO_API_KEY example to avoid the VARNAME=value assignment shape that ClawScan's exposed_secret_literal pattern matches even on obvious 'xxxxx' placeholders. Same intent, scanner-safe phrasing.\n\nv1.1.1 | 2026-05-12T22:32:37.728Z | user\n\nv1.1.1 — Replaced the literal-looking placeholder 'shippo_test_abc123…' in the breaking-change callout with 'shippo_test_xxxxx' to match the placeholder style used elsewhere. ClawScan flagged the abc123 form as a possible exposed secret literal on v1.1.0. No functional changes.\n\nv1.1.0 | 2026-05-12T22:30:44.107Z | user\n\nv1.1.0 — Default MCP transport changed to Gram-hosted HTTPS (app.getgram.ai/mcp/shippo-key-auth). No local Node required for clients that support type:http+headers; self-host via @shippo/shippo-mcp npm documented as the alternative for users who prefer no third-party gateway. **Breaking change:** SHIPPO_API_KEY env var now expects just the bare token (e.g. shippo_test_xxx) — the ShippoToken prefix is added by the MCP config. Display name shortened to 'Shippo'. Adds new Authentication subsection in Best Practices covering token format, dashboard URL, and the two diagnostic 401 strings ('Token does not exist' / 'Authentication credentials were not provided').\n\nv1.0.5 | 2026-05-12T22:16:16.484Z | user\n\nv1.0.5: synced from goshippo/ai v1.1.0. Added cross-cutting Authentication section to shippo-best-practices (token format, dashboard URL, 401 error strings). Generalized hardcoded MCP URLs in upgrade-shippo. Strengthened live-mode acknowledgement in label-purchase. Updated error-reference with the two exact 401 strings.\n\nv1.0.3 | 2026-04-17T14:30:45.883Z | user\n\nInitial publish (mirror of shippo-official).\n\nArchive index:\n\nArchive v1.4.4: 7 files, 36141 bytes\n\nFiles: references/carrier-guide.md (5951b), references/csv-format.md (6234b), references/customs-guide.md (9582b), references/tool-reference.md (16965b), skill-card.md (3006b), SKILL.md (56108b), _meta.json (125b)\n\nFile v1.4.4:SKILL.md\n\n---\nname: shippo\ndescription: \"A shipping and logistics skill for Shippo. Get multi-carrier rates (USPS, UPS, FedEx, DHL, 30+), buy domestic and international labels with customs, validate addresses, track packages with webhooks, and run bulk CSV batches, plus cost analysis, integration routing, and SDK-upgrade help. Runs through Shippo's hosted MCP with per-user OAuth (sign in once, nothing to copy or store). Uses Shippo's discounted carrier rates.\"\nversion: 1.4.4\nlicense: MIT\nmetadata:\n  openclaw:\n    emoji: \"📦\"\n    homepage: https://github.com/goshippo/ai\n---\n\n# Shippo Shipping Skill\n\n## Setup\n\n**MCP server:** Shippo's hosted MCP at `https://mcp.shippo.com`, with per-user Shippo OAuth. You authorize once through Shippo on first use, with nothing to copy or configure, and the client refreshes the token automatically.\n\nPoint your MCP client at the hosted server:\n\n```json\n{\n  \"mcpServers\": {\n    \"shippo\": {\n      \"type\": \"http\",\n      \"url\": \"https://mcp.shippo.com\"\n    }\n  }\n}\n```\n\nOn first use, your client runs the Shippo OAuth sign-in (in OpenClaw, `openclaw mcp login shippo`; in Claude Code, `/mcp`). No local Node process and nothing to store.\n\n**Prerequisites:** A Shippo account and at least one carrier account (Shippo provides managed accounts for USPS, UPS, FedEx, DHL Express by default). See `references/tool-reference.md` for the full tool catalog.\n\n**Purchases are live:** label purchases charge the authorized Shippo account for real. Confirm carrier, service, and cost with the user before any purchase.\n\n**Response envelope:** The MCP wraps most API responses in a Speakeasy envelope shaped like `{\"ContentType\": \"application/json\", \"StatusCode\": <code>, \"RawResponse\": {}, \"<PayloadName>\": {...actual response...}}`. The payload field is named after the response schema on success (e.g. `ParsedAddress`, `AddressPaginatedList`, `AddressValidationResultV2`, `AddressWithMetadataResponse`, `Shipment`, `CarrierAccountPaginatedList`) and after the HTTP status code on some errors (e.g. `fourHundredAndNineApplicationJsonObject` for a 409, the body may be `{}`). To extract the payload, find the field whose key is not `ContentType`, `StatusCode`, or `RawResponse`, and branch on `StatusCode` for success vs error.\n\n**Non-envelope errors:** Some failures bypass the envelope entirely and surface as an MCP-level error instead, the tool response has `isError: true` with a single text block containing a plaintext message like `Unexpected API response status or content-type: Status 404 Content-Type application/json Body: {\"detail\":\"Not found.\"}`. Argument-validation failures come back as JSON-RPC error code `-32602`. Handle both paths when reporting errors to the user.\n\n---\n\n## Best Practices\n\nLatest Shippo API version: **2018-02-08**. Send via the `Shippo-API-Version` header.\n\n### Using the Shippo MCP\n\nThe hosted Shippo MCP at `https://mcp.shippo.com` exposes exactly **4 tools** (a meta-API), not the underlying operations directly:\n\n- `shippo_list_tools`: discover which operation you need.\n- `shippo_describe_tool`: get that operation's input schema.\n- `shippo_read_execute_tool`: run a read (lists, gets, lookups).\n- `shippo_write_execute_tool`: run a write or mutation (creates, purchases, voids).\n\nEvery operation name in this skill (`ValidateAddress`, `CreateShipment`, `CreateTransaction`, `GetTrack`, etc.) is invoked **through** these wrappers, never called as a tool on its own. Standard discovery pattern: `shippo_list_tools` to find the operation, then `shippo_describe_tool` for its schema, then `shippo_read_execute_tool` or `shippo_write_execute_tool` to run it. The read/write split lets approval policies gate mutations separately. In the Claude apps these 4 tools may be deferred (loaded on demand), so an initial \"tool has not been loaded yet\" is normal: discover via the wrappers rather than guessing operation names.\n\n### Integration routing\n\n| Building…                                          | Recommended primitive          | See                                                                                        |\n|----------------------------------------------------|--------------------------------|--------------------------------------------------------------------------------------------|\n| Checkout flow with live shipping rates             | Rates at Checkout              | Rate Shopping (+ `shippo/references/rate-shopping-guide.md`)                               |\n| Single label purchase                              | Shipments + Transactions       | Label Purchase                                                                              |\n| Bulk label generation from CSV                     | Batches + Manifests            | Batch Shipping (+ `shippo/references/csv-format.md`)                                        |\n| Track packages across carriers                     | Tracking + webhooks            | Tracking                                                                                    |\n| Validate user addresses before save                | Addresses v2                   | Address Validation (+ `shippo/references/address-formats.md`)                               |\n| Analyze shipping spend / optimize carriers         | Shipments + Transactions list  | Shipping Analysis                                                                           |\n| International shipments                            | Customs Items + Declarations   | Label Purchase (+ `shippo/references/customs-guide.md` + `shippo/references/international-shipping.md`) |\n\nRead the relevant skill or reference before answering integration questions or writing code.\n\n### Critical rules\n\n- **Always validate addresses before purchasing labels.** Most \"no rates\" / \"label failed\" errors trace back to unvalidated addresses.\n- **Label purchases charge your live Shippo account for real.** Always confirm carrier, service, and cost with the user before any purchase.\n- **Always confirm purchase before `CreateTransaction`.** Show carrier/service/cost/eta and require explicit user confirmation.\n- **Parcel dimensions and weight must be strings, not numbers.** Use `\"10\"`, never `10`.\n- **Label URLs are S3 signed URLs.** Always display the complete URL, truncating breaks the signature.\n- **Rates expire after 7 days.** Re-create the shipment for fresh rates.\n- **By-id parameter names are case-sensitive** (mostly PascalCase: `ShipmentId`, `TransactionId`, `OrderId`). Use the exact name from `shippo_describe_tool`; do not guess snake_case.\n- **Never retry a 403/404 tool error with the same arguments.** Ownership and not-found errors are permanent for those inputs; verify the ID via the matching `List*` operation first. The generic `An internal error occurred. Please retry later.` relay most often traces to an input issue too, so verify inputs before retrying, and retry the identical call at most once.\n\n### Response handling\n\nThe MCP wraps responses in a Speakeasy envelope. Some failures bypass the envelope. See `shippo/references/response-envelope.md` and `shippo/references/error-reference.md` for parsing logic and error-handling patterns.\n\n### Connecting\n\nThe hosted MCP at `https://mcp.shippo.com` uses per-user Shippo OAuth. You authorize once through Shippo (in Claude Code, run `/mcp` and sign in), and the session refreshes automatically. There is nothing to copy or configure. Once you are connected, the workflow guidance below is unchanged.\n\n- **Two 401 strings to recognize:**\n  - `\"Token does not exist\"`: the credential is invalid, revoked, or for a different account. Re-authorize the Shippo OAuth session.\n  - `\"Authentication credentials were not provided\"`: no credential reached Shippo. The OAuth session is not authorized yet, or it has expired. Re-authorize the Shippo OAuth session.\n\n### Purchases are live\n\nLabel and batch purchases charge the authorized Shippo account for real money. Before any `CreateTransaction` or `PurchaseBatch`, show the carrier, service level, cost, and ETA, and get explicit user confirmation. Do not proceed without it.\n\n### Key documentation\n\n- [API Concepts](https://docs.goshippo.com/docs/api_concepts/apiversioning): request shapes, versioning, auth\n- [Address Validation Guide](https://docs.goshippo.com/docs/addresses/address_validation): validation depth varies by country\n- [Customs Reference](https://docs.goshippo.com/docs/exporting/internationalshipments): incoterms, contents types, HS codes\n- [Carrier Accounts](https://docs.goshippo.com/docs/shipping/carrieraccounts): managed vs custom accounts\n- [Webhooks](https://docs.goshippo.com/docs/tracking/webhooks): event types, signature verification\n\n(Once Mintlify migration completes, `.md` URL suffixes will provide raw markdown access for AI agents.)\n\n---\n\n## Address Validation\n\n### Address Field Format\n\nThe Shippo API uses **v1 field names** for address components in most endpoints (including `CreateShipment`). Always use:\n\n| Field | Description | Example |\n|---|---|---|\n| `name` | Full name | `Jane Smith` |\n| `street1` | Street address line 1 | `731 Market St` |\n| `street2` | Street address line 2 (optional) | `Suite 200` |\n| `city` | City | `San Francisco` |\n| `state` | State or province | `CA` |\n| `zip` | Postal code | `94103` |\n| `country` | ISO 3166-1 alpha-2 country code | `US` |\n| `email` | Email (required for international senders) | `jane@example.com` |\n| `phone` | Phone (required for international senders) | `+1-555-123-4567` |\n\nNote: `CreateAddress` and `ValidateAddress` take the v2 field names (`address_line_1`, `city_locality`, `state_province`, `postal_code`), but when passing addresses inline to `CreateShipment`, you must use the v1 names above.\n\n---\n\n### Validate a Structured Address\n\n1. Collect at minimum: `street1`, `city`, `state`, `zip`, `country` (ISO 3166-1 alpha-2).\n2. Call `CreateAddress` with the address fields. This creates the address and returns an object ID.\n3. Call `ValidateAddress` with the address fields to get validation results. Note: this endpoint takes address fields as query parameters, not an object ID.\n4. Check `analysis.validation_result.value` in the response. Values: `\"valid\"`, `\"invalid\"`, or `\"partially_valid\"` (address found with corrections applied). Check `analysis.validation_result.reasons` for details.\n5. Report the standardized address back. Highlight any corrected fields (listed in `changed_attributes`). Note `analysis.address_type` (`\"residential\"`, `\"commercial\"`, or `\"unknown\"`) -- residential classification affects carrier surcharges.\n6. If invalid: relay the reason descriptions. If the API returns a `recommended_address`, present it to the user.\n7. If `partially_valid`: show what was corrected and ask the user to confirm the corrections are acceptable.\n\n---\n\n### Parse a Freeform Address\n\n1. Call `ParseAddress` with the raw string (e.g., \"123 Main St, Springfield IL 62704\").\n2. Review the structured output for completeness. The parse response uses v2 field names: `address_line_1`, `city_locality`, `state_province`, `postal_code`.\n3. Note: the parse response does not include `country`. You must ask the user for the country or infer it, then add it before proceeding.\n4. Validate the parsed result by passing the fields to `CreateAddress` then `ValidateAddress` (follow the structured address workflow above from step 2).\n\n---\n\n### International Addresses\n\n- Always require the `country` field. Do not guess.\n- Pass non-Latin characters as-is; the API handles encoding.\n- Validation depth varies by country. US, CA, GB, AU, and major EU countries have deep validation. Others may only confirm structural completeness. Inform the user of this limitation.\n\n---\n\n### Bulk Address Validation\n\nThere is no batch validation endpoint. Call `CreateAddress` per address. Track results (row number, valid/invalid, corrections, errors, residential classification) and report a summary when done. For 50+ addresses, set expectations about processing time and provide progress updates.\n\n---\n\n### Re-validate an Existing Address\n\nCall `ValidateAddress` with the address fields. This endpoint validates by address fields, not by object ID.\n\n---\n\n### Duplicate Addresses\n\nIf `CreateAddress` returns a \"Duplicate address\" error, the address already exists in the account. Retrieve it via `ListAddresses` or proceed directly to validation.\n\n---\n\n### Quick Reference\n\n**Validate an address:**\n`CreateAddress` (saves address) + `ValidateAddress` (validates with same fields)\n\n**Parse then validate:**\n`ParseAddress` -> add country -> `CreateAddress` + `ValidateAddress`\n\n---\n\n## Rate Shopping\n\n### Get Rates for a Shipment\n\n1. Collect: origin address, destination address, parcel (length, width, height, distance_unit, weight, mass_unit). All dimension and weight values must be **strings** (e.g., `\"10\"` not `10`).\n2. Optionally validate both addresses with `ValidateAddress` (see Address Validation).\n3. Call `CreateShipment` with `address_from`, `address_to` (as inline address objects using v1 field names -- `street1`, `city`, `state`, `zip`, `country` -- not object IDs), and `parcels`.\n4. The response `rates` array contains available options. Present a table: carrier, service level, price, estimated days.\n5. Note: the same carrier may return duplicate rates from multiple carrier accounts. Present the best rate per carrier/service combination.\n6. Each rate carries an `object_id`. To buy a label, pass the chosen rate's `object_id` to the purchase flow (see Label Purchase); you do not re-send the address or parcel.\n\n---\n\n### Rate Expiration\n\nRates expire after 7 days. If a user tries to purchase a rate that was retrieved more than 7 days ago, create a new shipment to get fresh rates.\n\n---\n\n### Filter by Speed\n\nMap user requests: \"overnight\" = estimated_days 1, \"2-day\" = estimated_days <= 2, \"within N days\" = estimated_days <= N. Filter the rates array accordingly. If nothing matches, show the fastest available option.\n\n---\n\n### International Rates\n\nSome carriers may return international rates without a customs declaration, but others will not. If no rates are returned, try attaching a customs declaration to the shipment. Some carriers also require a phone number on the destination address for international rate retrieval. Inform the user that customs will be required at label purchase time regardless. See `shippo/references/customs-guide.md` for customs details.\n\n---\n\n### Checkout Rates (Line Items)\n\nCall `CreateLiveRate` instead of `CreateShipment`. Accepts `address_from`, `address_to`, and `line_items` (each with title, quantity, total_price, currency, weight, weight_unit).\n\n---\n\n### Rates in a Specific Currency\n\nCall `ListShipmentRatesByCurrencyCode` with the preferred ISO currency code (USD, EUR, GBP, CAD, etc.).\n\n---\n\n### Recommendation\n\nIdentify the cheapest (lowest `amount`), fastest (lowest `estimated_days`), and best-value options from the rates array. These are not API fields -- compute them by sorting the rates array yourself. State the trade-off: \"Option A is $X cheaper but takes Y more days than Option B.\"\n\n---\n\n### Troubleshooting: No Rates\n\n- Verify both addresses passed validation (most common cause).\n- Confirm parcel dimensions are reasonable (not zero, not exceeding carrier limits).\n- Shippo provides managed carrier accounts by default for major carriers. If no rates are returned, the issue is more likely address validation, unsupported route, or parcel dimensions -- not missing carrier accounts. You can verify with `ListCarrierAccounts` if needed.\n- Rates expire after 7 days. If stale, create a new shipment to get fresh rates.\n\n---\n\n### Quick Reference\n\n**Get rates:**\n(optional) `ValidateAddress` (x2) -> `CreateShipment` (with inline addresses) -> read `rates` array\n\n---\n\n## Label Purchase\n\n### Purchases Are Live\n\nLabel purchases charge the authorized Shippo account for real. **Before purchasing, explicitly state \"this will charge your Shippo account\" with the carrier, service, and cost, and require the user to acknowledge.** Do not purchase without that confirmation.\n\n---\n\n### Purchase Confirmation Gate\n\nBefore every call to `CreateTransaction`, summarize the following and ask the user for explicit confirmation:\n- Carrier and service level\n- Estimated cost\n- Estimated delivery time\n- Origin and destination\n\n**Do not proceed without explicit user confirmation.**\n\n---\n\n### Domestic Label\n\n1. Optionally validate both addresses with `ValidateAddress` (see Address Validation).\n2. Call `CreateShipment` with `address_from`, `address_to` (as inline address objects using v1 field names -- `street1`, `city`, `state`, `zip`, `country`), `parcels`, and `async: false`.\n3. Present rates to the user. Let them choose.\n4. **Confirm purchase** (see Purchase Confirmation Gate above).\n5. Call `CreateTransaction` with: `rate` (selected rate object_id), `label_file_type` (default `PDF_4x6`), `async: false`.\n6. Check response `status`:\n   - `SUCCESS`: return `tracking_number`, `label_url` (display the COMPLETE URL -- S3 signed URLs break if truncated), and `tracking_url_provider`.\n   - `QUEUED`/`WAITING`: poll `GetTransaction` until resolved.\n   - `ERROR`: report messages from the `messages` array.\n\n---\n\n### International Label\n\nAll domestic steps apply, plus customs handling before shipment creation. See `shippo/references/customs-guide.md` for the full customs workflow.\n\n1. Optionally validate addresses with `ValidateAddress`. Sender must include `email` and `phone`. Ask if missing.\n2. Create customs items: call `CreateCustomsItem` per item (description, quantity, net_weight, mass_unit, value_amount, value_currency, origin_country, tariff_number). Alternatively, you can skip this step and pass inline item objects directly in the declaration (step 3).\n3. Create the customs declaration: call `CreateCustomsDeclaration` with contents_type, non_delivery_option, certify: true, certify_signer, and the items (either object_ids from step 2, or inline item objects). See `shippo/references/customs-guide.md` for field details.\n4. Call `CreateShipment` with all standard fields plus `customs_declaration` (the declaration object_id).\n5. Present rates, **confirm purchase** (see Purchase Confirmation Gate), then purchase label and return results as in the domestic flow.\n\n#### Contents Type Decision Tree\n\nUse this to determine the correct `contents_type` value:\n\n| Scenario | Value |\n|---|---|\n| Selling to the recipient (commercial sale) | `MERCHANDISE` |\n| Sending a free gift | `GIFT` |\n| Sending a product sample | `SAMPLE` |\n| Paper documents only | `DOCUMENTS` |\n| Customer returning a purchased item | `RETURN_MERCHANDISE` |\n| Charitable donation | `HUMANITARIAN_DONATION` |\n| None of the above | `OTHER` (requires `contents_explanation`) |\n\n#### Incoterms Decision Logic\n\nThe `incoterm` field on the customs declaration controls who pays duties and taxes:\n\n- **B2C / e-commerce (default):** Use `DDU` (Delivered Duty Unpaid) -- recipient pays duties at delivery.\n- **Seller prepays duties:** Use `DDP` (Delivered Duty Paid) -- seller covers all duties and taxes.\n- **FedEx/DHL only:** `FCA` (Free Carrier) is available for advanced trade scenarios.\n\nIf the user does not specify, default to `DDU` for standard e-commerce shipments.\n\n---\n\n### Return Labels\n\nTo generate a return label, swap `address_from` and `address_to` so the original recipient becomes the sender and the original sender becomes the recipient. All other steps (shipment creation, rate selection, label purchase) remain the same.\n\n---\n\n### Label Format Options\n\nDefault to `PDF_4x6` unless the user specifies otherwise. Supported formats: `PDF_4x6`, `PDF_4x8`, `PDF_A4`, `PDF_A5`, `PDF_A6`, `PDF`, `PDF_2.3x7.5`, `PNG`, `PNG_2.3x7.5`, `ZPLII`.\n\n---\n\n### Label Customization Options\n\nWhen purchasing a label via `CreateTransaction`, the following options may be set on the shipment or rate:\n\n- **Signature confirmation**: set `signature_confirmation` on the shipment's `extra` field. Values: `STANDARD`, `ADULT`, `CERTIFIED`, `INDIRECT`, `CARRIER_CONFIRMATION`.\n- **Insurance**: set `insurance` on the shipment's `extra` field with `amount`, `currency`, and `provider`.\n- **Saturday delivery**: set `saturday_delivery` to `true` in the shipment's `extra` field. Only supported by certain carriers and service levels.\n- **Reference fields**: pass `metadata` on the transaction for order numbers or internal references.\n\n---\n\n### Label from Existing Rate\n\nIf the user already has a rate object_id: optionally call `GetRate` to confirm details, then **confirm purchase** (see Purchase Confirmation Gate), then call `CreateTransaction` directly.\n\n---\n\n### Voiding a Label\n\nCall `CreateRefund` with the transaction object_id.\n\n**Refund limitations:** Void/refund eligibility depends on carrier and timing. Not all labels can be refunded after purchase. If `CreateRefund` fails, advise the user to contact Shippo support.\n\n---\n\n### Quick Reference\n\n**Domestic label:**\n(optional) `ValidateAddress` (x2) -> `CreateShipment` (with inline addresses) -> user picks rate -> confirm -> `CreateTransaction`\n\n**International label:**\n(optional) `ValidateAddress` (x2) -> `CreateCustomsItem` (per item) -> `CreateCustomsDeclaration` -> `CreateShipment` (with inline addresses + customs_declaration) -> user picks rate -> confirm -> `CreateTransaction`\n\n**Return label:**\nSame as domestic/international, but swap `address_from` and `address_to`.\n\n**Order-to-label:**\n`CreateOrder` -> `CreateShipment` (using order address/item data) -> user picks rate -> confirm -> `CreateTransaction` -> packing slip (REST fallback, see below)\n\n---\n\n### Orders and Packing Slips\n\nUse orders to represent e-commerce fulfillment requests. An order captures the shipping address, line items, and totals -- then feeds into the standard label purchase workflow.\n\n#### Tools\n\n- **`CreateOrder`**: Create an order with line items, shipping address, and order details.\n- **`GetOrder`**: Retrieve an order by its object_id.\n- **`ListOrders`**: List all orders.\n- **Packing slip (known gap):** Generate a packing slip PDF for an order. There is no packing-slip tool in the MCP catalog. The underlying REST endpoint exists at `GET /orders/{ORDER_ID}/packingslip/` (returns a 24-hour S3 PDF link). Fall back to a direct REST call, or advise the user to use the Shippo dashboard until the MCP gap is closed.\n\n#### Workflow\n\n1. Call `CreateOrder` with the shipping address, line items (title, quantity, sku, total_price, etc.), and order-level fields.\n2. Use the order's address and item data to call `CreateShipment`, then follow the standard label purchase flow (rate selection, confirmation, `CreateTransaction`).\n3. After purchasing the label, generate a packing slip via the REST fallback (see Tools above for the known MCP gap).\n\n---\n\n## Tracking\n\n### Track by Number\n\n1. Determine carrier and tracking number. Carrier must be a lowercase Shippo token (e.g., `usps`, `ups`, `fedex`, `dhl_express`). See `shippo/references/carrier-guide.md` for tracking number format hints per carrier. If uncertain, ask the user.\n2. Call `GetTrack` with `carrier` and `tracking_number`.\n3. Key response fields: `tracking_status` (status, status_details, status_date, location), `tracking_history`, `eta`.\n4. Each tracking event includes a `substatus` object with `code`, `text`, and `action_required` (boolean). Include substatus details when presenting tracking history -- these provide more specific information about what happened at each step.\n5. Present: current status, location, ETA, substatus details, and chronological event history (most recent first).\n\n---\n\n### Status Values\n\nSee `shippo/references/carrier-guide.md` for carrier-specific status nuances. Standard values:\n\n| Status | Meaning |\n|---|---|\n| PRE_TRANSIT | Label created, carrier has not received the package |\n| TRANSIT | Package is in transit |\n| DELIVERED | Delivered |\n| RETURNED | Being returned or returned to sender |\n| FAILURE | Delivery failed |\n| UNKNOWN | No tracking information from carrier |\n\nThe `eta` field is provided by most major carriers (USPS, UPS, FedEx, DHL Express) but availability is carrier-dependent, it may be `null` for regional carriers or for shipments before the carrier has finalized routing. Treat absence as informational, not as an error condition.\n\n---\n\n### Find Trackable Packages\n\nCall `ListTransactions`. Filter for `object_status: SUCCESS`. Each successful transaction has `tracking_number` and carrier info. Then call `GetTrack` for selected items.\n\n---\n\n### Register a Tracking Webhook\n\n1. Get the user's HTTPS webhook URL.\n2. Call `createWebhook` with `url` and `event: track_updated`.\n3. Optionally call `CreateTrack` with carrier and tracking number to register a specific shipment for push updates.\n\n---\n\n### Quick Reference\n\n**Track a package:**\n`GetTrack` with carrier + tracking number\n\n**Find past shipment tracking:**\n`ListTransactions` -> filter SUCCESS -> `GetTrack`\n\n---\n\n## Batch Shipping\n\n### Purchases Are Live\n\nBatch purchases charge the authorized Shippo account for real. Before `PurchaseBatch`, show the shipment count, carrier/service, and estimated total cost, and require explicit user confirmation.\n\n---\n\n### Purchase Confirmation Gate\n\nBefore every call to `PurchaseBatch`, summarize the following and ask the user for explicit confirmation:\n- Total number of shipments to be purchased\n- Carrier and service level (or selection rule if varied)\n- Estimated total cost\n- Number of domestic vs international shipments\n\n**Do not proceed without explicit user confirmation.**\n\n---\n\n### CSV Batch Processing\n\nSee `shippo/references/csv-format.md` for the column specification.\n\n1. Read and parse the CSV. Validate required columns are present. Report row count.\n2. Validate each row for non-empty required fields. Report invalid rows with reasons.\n3. Detect international rows (sender_country != recipient_country). Create customs declarations for those rows. See `shippo/references/customs-guide.md`. Use correct customs enum values: `RETURN_MERCHANDISE` (not `RETURN`) for returned goods, `HUMANITARIAN_DONATION` (not `HUMANITARIAN`) for charitable donations.\n4. Build the `batch_shipments` array with inline address and parcel objects per row.\n5. Call `CreateBatch` with the array.\n6. Poll `GetBatch` until status is `VALID` or `INVALID`. See Polling Intervals below.\n7. If the status is `INVALID`, some batch shipments failed validation: see \"Fixing an INVALID batch\" below, fix them, and re-poll until `VALID`. Report per-shipment failures either way before proceeding.\n8. **Confirm purchase** (see Purchase Confirmation Gate above).\n9. Call `PurchaseBatch` to buy labels for all valid shipments.\n10. Poll `GetBatch` until status changes from `PURCHASING` to `PURCHASED`. See Polling Intervals below.\n11. Report: total attempted, succeeded, failed. For successes: tracking_number and label_url (complete URL). For failures: error messages.\n\n#### Retrieving batch labels\n\nA purchased batch does not put each label URL inline on the batch object. Each entry in `batch_shipments[]` carries a `transaction` field, which is a Transaction object_id. Call `GetTransaction` on it to get that shipment's `label_url` and `tracking_number`. The batch-level `label_url` is a merged multi-label PDF (up to 100 labels per file) and cannot be split per order.\n\n#### Batch Size Guidance\n\nFor batches over 500 shipments, consider splitting into multiple batches. Large batches take longer to validate and purchase, and a single failure can be harder to diagnose.\n\n---\n\n### Polling Intervals\n\n- For batches under 100 shipments: poll every 3-5 seconds.\n- For batches with 100+ shipments: poll every 5-10 seconds.\n- Report progress to the user every 30 seconds.\n- Stop after 60 retries and suggest the user check back later using `GetBatch` with the batch object_id.\n\n---\n\n### Batch with Rate Shopping\n\n1. Call `CreateShipment` per shipment to get rate quotes (see Rate Shopping).\n2. Present rates. User picks a service level rule (e.g., \"cheapest for each\" or a specific carrier/service).\n3. Build `batch_shipments` with `servicelevel_token` per item.\n4. Create, validate, **confirm purchase**, purchase, report as above.\n\n---\n\n### Managing an Existing Batch\n\n- Add shipments: `AddShipmentsToBatch` (before purchase only). Note: adding an invalid shipment will change the entire batch status to `INVALID`. Check per-shipment statuses after adding.\n- Remove shipments: `RemoveShipmentsFromBatch` (before purchase only).\n\n---\n\n### Fixing an INVALID batch\n\nIf `GetBatch` returns status `INVALID`, one or more batch shipments failed validation and the batch cannot be purchased until they are fixed.\n\n1. **Find the failures.** Call `GetBatch` with `object_results=creation_failed` to return only the failed shipments (paginate with `?page=` if there are many), or read each `batch_shipments[].status` (`VALID` / `INVALID` / `INCOMPLETE` / `TRANSACTION_FAILED`) and its `messages` for the reason. The batch-level `errors` array collects the same per-shipment failures in one place.\n2. **Fix them,** either:\n   - Remove: `RemoveShipmentsFromBatch` with the failed batch-shipment `object_id`s (from `batch_shipments[].object_id`, not the shipment object_id) to drop them, or\n   - Correct and re-add: `AddShipmentsToBatch` with corrected shipment objects (fixed address, parcel, or servicelevel).\n3. **Re-poll `GetBatch`** until status is `VALID`.\n4. Then **confirm purchase** (see Purchase Confirmation Gate) and `PurchaseBatch`.\n\n---\n\n### End-of-Day Manifest\n\n1. Collect: `carrier_account` (object_id), `shipment_date` (YYYY-MM-DD, default today), `address_from` (pickup address).\n2. Optionally collect specific transaction object_ids to scope the manifest. You must pass specific transaction object_ids -- there is no auto-include for a date range.\n3. Call `CreateManifest`.\n4. Poll `GetManifest` until status is `SUCCESS` or `ERROR`.\n5. Return the manifest PDF URL(s) and shipment count.\n\n---\n\n### Quick Reference\n\n**CSV batch:**\nParse CSV -> `CreateCustomsDeclaration` (international rows) -> `CreateBatch` -> poll `GetBatch` -> confirm -> `PurchaseBatch` -> poll `GetBatch`\n\n**Manifest:**\n`CreateManifest` (with transaction object_ids) -> poll `GetManifest`\n\n---\n\n## Shipping Analysis\n\n### Geographic Cost Analysis\n\n1. Confirm origin address, destination list (or use representative cities), and parcel details.\n2. Call `ListCarrierAccounts` to see configured carriers.\n3. Call `CreateShipment` per destination to collect rates. Creating shipments is free; only `CreateTransaction` costs money.\n4. Write results to `analysis/` directory (markdown report + CSV). Columns: Route, Destination, Carrier, Service, Cost, Currency, EstimatedDays, Zone.\n\n---\n\n### Package Optimization\n\n1. Confirm the route.\n2. Define dimension profiles to test (or use user-provided ones).\n3. Check `ListCarrierParcelTemplates` and `ListUserParcelTemplates` for flat-rate and saved templates. See `shippo/references/rate-shopping-guide.md` for dimensional weight and flat-rate guidance.\n4. Call `CreateShipment` per profile on the same route.\n5. Compare: cheapest rate, carrier options, fastest option per profile. Note where flat-rate templates beat custom dimensions and where dimensional weight causes price jumps. See `shippo/references/carrier-guide.md` for carrier-specific weight limits and surcharges.\n\n---\n\n### Carrier Comparison\n\n1. Call `CreateShipment` for the route.\n2. Group the `rates` array by `provider`.\n3. Per carrier: cheapest service, fastest service, number of service levels, price range.\n\n---\n\n### Historical Cost Optimization\n\n1. Call `ListShipments` and `ListTransactions` to get past activity.\n2. Cross-reference: what the user paid vs. what alternatives were available.\n3. Identify patterns: carrier concentration, service-level mismatch, consistent overpayment.\n4. For a sample of shipments with tracking numbers, call `GetTrack` to check actual vs. estimated delivery times.\n5. If fewer than 5 successful transactions exist (not just shipments -- shipments are rate quotes, transactions represent actual spend), redirect to forward-looking analysis.\n\n---\n\n### Output Conventions\n\nWrite reports to the `analysis/` directory. Create it if it does not exist. Include both markdown and CSV. CSV must have a header row. Markdown must include a timestamp and input parameters.\n\n---\n\n### Quick Reference\n\n**Cost analysis:**\n`ListCarrierAccounts` -> `CreateShipment` (per destination) -> read `rates` arrays -> write report\n\n**Carrier comparison:**\n`CreateShipment` -> group `rates` by `provider` -> summarize\n\n**Historical review:**\n`ListShipments` + `ListTransactions` -> cross-reference -> `GetTrack` (sample) -> write report\n\n---\n\n## Upgrades\n\nThe Shippo MCP is hosted at `https://mcp.shippo.com`. It is OAuth-only and auto-updates server-side, so there is nothing to install or upgrade on your side. This skill covers what stays your responsibility: API version awareness, webhook payload versioning, and troubleshooting the hosted session.\n\n### API version handling\n\nThe current Shippo API version is **2018-02-08**. Shippo uses a single long-lived API version, and the hosted server manages it for you server-side. You do not set the `Shippo-API-Version` header yourself when going through the hosted MCP.\n\nWhat backward-compatibility means in practice:\n\n- Most changes are backward-compatible: new optional fields, new resources, additional webhook events. Existing calls keep working.\n- Breaking changes are rare and announced via release notes.\n- Because the server picks the version, you don't pin anything client-side. Your job is to handle new fields gracefully (see webhook versioning below) rather than to manage versions.\n\nShippo API changes are tracked in [the API changelog](https://docs.goshippo.com/changelog). As of 2026-06, no recent breaking changes affect the workflows covered by this skill set.\n\n### Webhook event versioning\n\nWebhook events can include new fields without bumping the API version. To handle them gracefully:\n\n- Default to ignoring unknown fields in your webhook handler, never fail-closed on a field you don't recognize.\n- Subscribe only to the specific event types you need (`track_updated`, `transaction_created`, `transaction_updated`, etc.).\n- Verify webhook signatures using the `Shippo-Signature` header per [webhook docs](https://docs.goshippo.com/docs/tracking/webhooks).\n\n### Troubleshooting the hosted MCP\n\n#### `401` or `403` errors\n\nThe OAuth session has expired or is not authorized. Re-authorize the Shippo OAuth session: in Claude Code, run `/mcp` and sign in again.\n\n#### Tools changed or missing after a server update\n\nThe hosted server auto-updates, so the tool catalog can shift without any action on your side. Re-list the current tools via `shippo_list_tools` to see what is available now.\n\n#### \"Not found\" errors for objects you expect to exist\n\nMost likely the object does not exist on the authorized account, or it belongs to a different account. Confirm you are signed in to the account that owns the object (re-authorize via `/mcp` if needed).\n\n### Auditing an existing integration\n\nBefore making a change to a production integration:\n\n1. Don't pin anything client-side. The hosted server manages the API version, so there's nothing to pin.\n2. Verify webhook handlers ignore unknown fields.\n3. Review the [API changelog](https://docs.goshippo.com/changelog) for any breaking changes.\n4. Re-list tools via `shippo_list_tools` after an update to catch renamed or added operations.\n\n---\n\n## Support Ticket Builder\n\nTurn a single shipment identifier into a complete, **classified**, well-structured\nsupport package for the Shippo support team. The agent classifies the issue,\ngathers every relevant fact from the Shippo MCP (running issue-type-specific\nlookups, not just the lost-package set), computes the triage timeline, and emits\ntwo things:\n\n1. A **human copy-paste block** for the ticket body.\n2. A **structured JSON block** tagged with a routing queue, so the ticket can be\n   piped into the ticketing system and land in the right pipeline without a\n   human re-classifying it.\n\nThis dual output is the point: completeness *and* correct routing are what kill\nthe back-and-forth.\n\nAudience: Shippo support agents. Output uses Shippo terminology, object IDs, and\nan internal routing tag. It is not customer-facing copy.\n\n### When to use\n\nUse this skill when someone wants to escalate or document a shipping problem and\nasks for a support ticket / message to Shippo support, e.g. \"package is stuck,\"\n\"label was charged but never shipped,\" \"why was I charged more than the rate I\nsaw,\" \"refund this label I never used,\" \"where is this delivery,\" \"the address\nlooks wrong,\" \"tracking updates aren't coming through,\" \"can't get rates from\nthis carrier.\" It produces **text + JSON to copy and paste**; it does not open a\nJira ticket or send Slack/email itself.\n\n### Step 1: Classify the issue (do this first)\n\nPick exactly one **canonical issue type** from the customer's description. The\nissue type drives both the routing tag and which extra lookups you run in Step 4.\nIf the wording is ambiguous, ask one clarifying question before building.\n\n| Issue type (canonical) | Triggers / signals | Routing tag |\n|---|---|---|\n| `lost_or_delayed` | stuck, late, no movement, \"where is my package\", lost | `queue:tracking-ops` |\n| `unused_label_refund` | \"never shipped\", \"refund this label\", bought-but-unused | `queue:billing-refunds` |\n| `billing_adjustment` | \"charged more than the rate\", surcharge, reweigh, dim-weight, address-correction fee | `queue:billing-adjustments` |\n| `address_exception` | undeliverable, returned to sender, bad/invalid address, address correction | `queue:address-exceptions` |\n| `customs_international` | customs hold, duties/taxes, missing HS code, commercial invoice, international | `queue:customs-intl` |\n| `carrier_account` | \"can't get rates from <carrier>\", connection failed, registration pending | `queue:carrier-onboarding` |\n| `tracking_webhook` | \"tracking updates aren't coming through\", webhook not firing | `queue:integrations` |\n| `other` | anything that doesn't fit above | `queue:general-triage` |\n\n> The routing tags above are a **stable, machine-parseable routing schema**;\n> the receiving team maps each `queue:*` tag to its own ticketing queue, so the\n> exact queue strings are configurable to match your support system.\n> The skill's value is producing a consistent, machine-parseable tag; the exact\n> strings should match your ticketing system.\n\n### Inputs accepted\n\nThe user may start from any **one** of these. Ask which one they have if it is\nambiguous; do not guess an ID type.\n\n| Input | What it anchors |\n|---|---|\n| **Tracking number + carrier** | Drives `GetTrack` directly. Best for delivery/lost-package issues. |\n| **Transaction (label) object ID** | Cleanest anchor: label creation time + tracking number + the rate/shipment link, all derivable. |\n| **Shipment object ID** | Gives from/to addresses, requested `shipment_date`, and rates; tracking number comes from the purchased transaction. |\n\n> **Resolving a tracking number to its label.** First detect the carrier and map\n> it to the Shippo carrier *token* (see the note below), then call `GetTrack`.\n> When the label was purchased through Shippo, the `GetTrack` response carries the\n> transaction `object_id`; use that with `GetTransaction` to pull the label and\n> billing facts. If the label was not bought through Shippo (no transaction comes\n> back), there is nothing to resolve: build the ticket from `GetTrack` plus\n> whatever the user supplied and mark the label fields \"Not available.\"\n> `ListTransactions` has no server-side `tracking_number` filter, so paging it to\n> match by hand is a rarely-useful last resort, not the primary path.\n\n> **Carrier token:** `GetTrack` expects a Shippo carrier *token*, not a display\n> name, e.g. `usps`, `ups`, `fedex`, `dhl_express`, `dhl_ecommerce`,\n> `canada_post`. If you only have a display name (often from a rate's\n> `provider`), map it to the token. If unsure, ask the user for the carrier.\n\n### Shippo MCP tools used\n\nDiscover/confirm with `shippo_list_tools` and `shippo_describe_tool`; execute\nread-only lookups with `shippo_read_execute_tool`. **Everything this skill needs\nis a `read` operation; never call a `write` tool (e.g. `CreateRefund`) from\nthis skill; the ticket only documents and recommends.**\n\nCore reads (all issue types):\n\n- `GetTransaction`: label creation time (`object_created`), `tracking_number`, `status`, `rate` reference, `eta`, `metadata` (order/internal reference)\n- `GetShipment`: `address_from`, `address_to`, requested `shipment_date`, `parcels`, `rates`, `customs_declaration`, `extra` (added services + references), `messages`\n- `GetTrack`: current `tracking_status`, full `tracking_history[]`, `eta`, and (for Shippo-purchased labels) the `transaction` object reference\n\nIssue-type-specific reads (Step 4):\n\n- `GetRate`: purchased `amount`, `currency`, `provider`, `servicelevel`, `estimated_days` (billing)\n- `GetParcel`: declared `length/width/height`, `distance_unit`, `weight`, `mass_unit` (billing)\n- `ListRefunds` / `GetRefund`: existing refund object + `status` (refund)\n- `ValidateAddress` / `ValidateAddressByID`: `is_valid`, `messages`, residential flag (address)\n- `GetCustomsDeclaration` / `GetCustomsItem`: `contents_type`, `incoterm`, `eel_pfc`, per-item `tariff_number` (HS code), `value_amount`, `origin_country` (customs)\n- `ListCarrierAccounts` / `GetCarrierAccount` / `GetCarrierRegistrationStatus`: `active`, registration status (carrier-account)\n- `listWebhooks` / `getWebhook`: `url`, `event`, `active` (webhook)\n\n### Step 2: Resolve the anchor object\n\nAlways work toward having the four core objects: **transaction**, **shipment**,\n**addresses**, and **tracking**. Stop early only when the issue genuinely needs\nnothing more (e.g. a pure tracking-status question with no label on file).\n\n- **Transaction ID** → `GetTransaction`. Read `object_created` (label creation\n  time), `tracking_number`, `tracking_url_provider`, `status`, and the `rate`\n  reference. Inspect for a `shipment` reference to get the shipment ID.\n- **Shipment ID** → `GetShipment`. Read `address_from`, `address_to`,\n  `shipment_date` (the **requested** ship date), `parcels`, `rates`,\n  `customs_declaration`. Find the purchased rate/transaction for the tracking #.\n- **Tracking number + carrier** → map the carrier to its token and call\n  `GetTrack`. For a Shippo-purchased label the response carries the transaction\n  `object_id`; follow it with `GetTransaction` to get the billing/label facts.\n\n### Step 3: Pull the core facts\n\nPull the shipment (`GetShipment`) for `address_from`, `address_to`,\n`shipment_date` if not already loaded, and tracking (`GetTrack` with carrier\ntoken + tracking number) for `tracking_status`, `tracking_history[]`, and `eta`.\n\n> From each address object capture only its `object_id` and coarse geography\n> (`city`, `state`, `zip`, `country`) for the ticket, **not** `name`,\n> `street1`, or `street2` (see PII minimization in guardrails).\n\n- **First carrier scan** = the earliest `tracking_history` event representing\n  physical acceptance by the carrier (the first `TRANSIT`/`DELIVERED`-class scan,\n  or the carrier's \"accepted/picked up\" event). Pre-transit / \"label created\" /\n  \"shipment info received\" pseudo-events do **not** count; call those out\n  separately if present.\n- **Added services and order reference (capture them):** surface the shipment's\n  `extra` block (added services such as `signature_confirmation`, `insurance`,\n  Saturday delivery, QR-code labels) and the customer's own order / internal\n  reference number. That reference can live in two places depending on the\n  integration: the transaction's `metadata` field (the documented home for order\n  numbers) and/or the shipment `extra` reference fields. Capture it from wherever\n  it actually appears, so the agent can tie the ticket back to the order without\n  searching on an order number. The `extra` schema is nuanced and\n  carrier/service-dependent, so **read the actual response fields rather than\n  assuming names**: the `label-purchase` skill documents the common added-service\n  options (signature, insurance, Saturday delivery) and\n  `shippo/references/carrier-guide.md` covers per-carrier availability. Surface\n  only what is actually present; omit the rest.\n- **`messages` noise:** a shipment's `messages` array often carries routine\n  \"carrier doesn't support option\" / \"out of service area\" entries. These are\n  informational. Only surface messages tied to a carrier that actually appears\n  in `rates`.\n- **Read the actual response fields:** do not assume names. If a field is\n  absent, record \"Not available\" rather than inventing a value.\n\n### Step 4: Run the issue-type branch\n\nAfter the core facts, run **only** the lookups for the classified issue type and\nfill the matching section of the output. Skip branches that don't apply.\n\n- **`lost_or_delayed`**: no extra reads; the core timeline carries it. Emphasize\n  \"last scan → now\" and \"overdue vs ETA.\"\n- **`unused_label_refund`**: Was the label ever scanned? Re-check `GetTrack`: if\n  there is a real carrier scan, the label is **used** (not eligible as an unused\n  refund). Say so. Compute **label age** from `object_created` to now. Call\n  `ListRefunds` (and `GetRefund`) to report any existing refund object + its\n  `status`. Do **not** assert a specific eligibility window from memory; state\n  the facts (used/unused, age, existing refund) and let the queue apply policy.\n- **`billing_adjustment`**: `GetRate` for the purchased `amount`/`currency`;\n  `GetParcel` (or shipment `parcels`) for **declared** dims/weight; compare the\n  transaction's charged amount to the quoted rate. Flag the likely cause:\n  dimensional-weight reweigh (declared vs billed dims), address-correction\n  surcharge, or service upgrade. Report declared-vs-billed as the core evidence.\n  Note: the reweigh/adjustment amount and the carrier's *billed* dims may not be\n  exposed by these read ops; if so, record \"Not available\" rather than inferring.\n- **`address_exception`**: run `ValidateAddress`/`ValidateAddressByID` on\n  `address_to`; report `is_valid`, any validation `messages`, and the\n  residential/commercial flag. Note whether validation was bypassed at purchase.\n- **`customs_international`**: pull `GetCustomsDeclaration` + each\n  `GetCustomsItem`. Check completeness: `contents_type`, `incoterm`,\n  `eel_pfc`/AES exemption, and per item a `tariff_number` (HS code),\n  `value_amount`, and `origin_country`. Flag missing HS codes / values, the\n  usual cause of customs holds.\n- **`carrier_account`**: `ListCarrierAccounts`, then `GetCarrierAccount` /\n  `GetCarrierRegistrationStatus` for the relevant carrier. Report `active` and\n  registration status; an incomplete registration is the usual \"no rates\" cause.\n- **`tracking_webhook`**: `listWebhooks` + `getWebhook`. Report whether an\n  `active` webhook exists for the relevant `track_updated`/tracking event and the\n  configured `url`.\n\n### Timeline to compute\n\nThese derived metrics pre-diagnose the issue so support doesn't have to:\n\n- **Label created → first carrier scan**: how long the label sat before entering\n  the network. A large gap is the classic \"bought but never shipped\" signature.\n- **Requested `shipment_date` → first carrier scan**: picked up on/near intent?\n- **First scan → last scan**: total time in transit so far.\n- **Last scan → now**: days of silence; a long gap signals a stalled/lost parcel.\n- **ETA vs. now**: is it overdue?\n\nState each as an absolute date/time **and** a duration (e.g. \"Label created\n2026-06-01 14:02 UTC; first scan 2026-06-05 09:11 UTC, a 3d 19h gap\"). Use UTC\nand label it. In the JSON block, also emit each gap in whole hours.\n\n### Output\n\nEmit **both** blocks below, each as its own fenced block. Replace every `<...>`\nplaceholder; use \"Not available\" for anything you could not retrieve; never\ninvent values.\n\n**Provenance (required).** Both blocks carry a generation stamp so support can\ntell at a glance that the ticket was machine-assembled, and so ticket quality\ncan be tracked over time. Stamp:\n\n- the **skill name** (`shippo-support-ticket`),\n- the **source** (`Shippo MCP`),\n- the **generation time in UTC** (ISO 8601).\n\nNever alter or omit the stamp, and never present an auto-generated ticket as if\nit were hand-written.\n\nAfter the blocks, add a short plain-language **triage summary**\n(1-3 sentences) naming the most likely problem based on the classification +\ntimeline, and list any data you could not retrieve.\n\n#### Block A: Human ticket (copy-paste)\n\n```\nSubject: [<issue_type>] <one-line summary>, tracking <tracking_number>\n\nROUTING\n  Issue type:      <canonical issue type>\n  Routing tag:     <queue:...>\n  Confidence:      <high | medium | low; note if classified from sparse info>\n\nISSUE\n  Reported by:     <customer name / email, if known>\n  Summary:         <2-3 sentence description in plain language>\n\nSHIPMENT\n  Shipment ID:     <shipment object_id>\n  Transaction ID:  <transaction object_id>\n  Carrier:         <carrier display name> (<carrier token>)\n  Service level:   <servicelevel name>\n  Tracking #:      <tracking_number>\n  Tracking URL:    <tracking_url_provider>\n  Parcel:          <declared dimensions + weight, if available>\n  References:      <order/internal ref from transaction metadata or shipment extra, else \"none\">\n  Added services:  <signature / insurance / QR code / etc. from extra, else \"none\">\n\nADDRESSES (no street-level PII; run GetAddress on an ID for full details)\n  From address ID: <address_from object_id>\n  From region:     <city> <state> <zip> <country>\n  To address ID:   <address_to object_id>\n  To region:       <city> <state> <zip> <country>\n\nTIMELINE (all times UTC)\n  Label created:           <object_created>\n  Requested ship date:     <shipment_date>\n  First carrier scan:      <status_date> @ <location>   (<status>)\n  Last/most recent scan:   <status_date> @ <location>   (<status>)\n  Curren\n\nFile v1.4.4:_meta.json\n\n{\n  \"ownerId\": \"kn770w02ykf4ca0bj09cfcw2n1835r4y\",\n  \"slug\": \"shippo\",\n  \"version\": \"1.4.4\",\n  \"publishedAt\": 1784151735167\n}\n\nFile v1.4.4:references/carrier-guide.md\n\n<!--\n  ⚠️  DO NOT EDIT, auto-generated from skills/shippo/references/carrier-guide.md by scripts/build-clawhub-bundle.js\n  Edits here will be overwritten on the next sync.\n  To change this content, edit the canonical source and re-run the sync script.\n-->\n\n# Carrier Guide\n\nPer-carrier nuances, requirements, and gotchas for the major carriers supported by Shippo.\n\n---\n\n## USPS\n\n### Setup\n- A **managed USPS account** is available by default on all Shippo accounts. No additional configuration needed.\n- Carrier token: `usps`\n\n### Key Details\n- **HS codes required** for ALL international commercial shipments as of September 2025. Minimum 6 digits.\n- **No DDP support.** USPS always ships DDU (recipient pays duties/taxes). Do not set `incoterm` to `DDP` for USPS.\n- **Flat-rate options** available via parcel templates (e.g., `USPS_FlatRateEnvelope`, `USPS_SmallFlatRateBox`, `USPS_MediumFlatRateBox1`, `USPS_LargeFlatRateBox`). When using flat rate, parcel dimensions are ignored -- only weight matters for eligibility.\n- **EEL/PFC required** for international shipments. USPS will warn/reject if `eel_pfc` is missing on customs declarations.\n- **Tracking number format:** 20-22 digits, or starts with `9` followed by 20+ digits (e.g., `9400111899223100001234`).\n- **Max weight:** 70 lbs domestic, 66 lbs international (varies by destination).\n- **Signature confirmation:** Available via `extra.signature_confirmation` (`STANDARD`, `ADULT`). Not available on all service levels.\n\n### Common Service Levels\n| Token | Name |\n|---|---|\n| `usps_priority` | Priority Mail |\n| `usps_priority_express` | Priority Mail Express |\n| `usps_ground_advantage` | Ground Advantage |\n| `usps_first` | First-Class Mail |\n| `usps_media_mail` | Media Mail |\n\n---\n\n## UPS\n\n### Setup\n- **Requires Terms & Conditions acceptance** via the Shippo web app before API use. If the user gets auth errors for UPS, direct them to accept T&C in the Shippo dashboard.\n- Carrier token: `ups`\n\n### Key Details\n- **Supports DDP** (Delivered Duty Paid). Set `incoterm` to `DDP` on the customs declaration.\n- **Signature confirmation options:** `STANDARD`, `ADULT`, `CERTIFIED`, `INDIRECT`\n- **Tracking number format:** Starts with `1Z` followed by 16 alphanumeric characters (e.g., `1Z999AA10123456784`).\n- **Residential surcharge** applies automatically when the destination is residential. Shippo flags residential addresses during validation.\n- **Saturday delivery** available for select services via `extra.saturday_delivery = true`.\n- **Max weight:** 150 lbs per package.\n\n### Common Service Levels\n| Token | Name |\n|---|---|\n| `ups_ground` | UPS Ground |\n| `ups_next_day_air` | UPS Next Day Air |\n| `ups_2nd_day_air` | UPS 2nd Day Air |\n| `ups_3_day_select` | UPS 3 Day Select |\n| `ups_express` | UPS Worldwide Express |\n| `ups_expedited` | UPS Worldwide Expedited |\n\n---\n\n## FedEx\n\n### Setup\n- Carrier token: `fedex`\n- Requires a FedEx account connected via the Shippo dashboard.\n\n### Key Details\n- **Supports DDP** via `duties_payor` field and `incoterm` = `DDP`.\n- **FCA incoterm** available for B2B / drop-ship scenarios.\n- **Dangerous goods / dry ice** supported via `extra.dangerous_goods` and `extra.dry_ice` fields.\n- **Tracking number format:** 12 or 15 digits (e.g., `123456789012` or `123456789012345`).\n- **Residential surcharge** applied automatically.\n- **Saturday delivery** available via `extra.saturday_delivery = true`.\n- **Signature options:** `STANDARD`, `ADULT`, `DIRECT`, `INDIRECT`\n- **Max weight:** 150 lbs per package (FedEx Ground), 2200 lbs (FedEx Freight).\n- **HS codes** strongly recommended for international shipments.\n\n### Common Service Levels\n| Token | Name |\n|---|---|\n| `fedex_ground` | FedEx Ground |\n| `fedex_home_delivery` | FedEx Home Delivery |\n| `fedex_2_day` | FedEx 2Day |\n| `fedex_express_saver` | FedEx Express Saver |\n| `fedex_standard_overnight` | FedEx Standard Overnight |\n| `fedex_priority_overnight` | FedEx Priority Overnight |\n| `fedex_international_economy` | FedEx International Economy |\n| `fedex_international_priority` | FedEx International Priority |\n\n---\n\n## DHL Express\n\n### Setup\n- Carrier token: `dhl_express`\n- Requires a DHL Express account connected via the Shippo dashboard.\n\n### Key Details\n- **DDP and DAP support.** Strong international coverage. DHL is often the best option for international shipments outside North America.\n- **HS codes** strongly recommended for all international shipments.\n- **Tracking number format:** 10 digits (e.g., `1234567890`).\n- **Max weight:** 150 lbs per package.\n- **Signature:** Included by default on most service levels.\n- **Volumetric weight** (dimensional weight) is calculated using a divisor of 5000 (cm) or 139 (inches), which is more aggressive than domestic carriers.\n\n### Common Service Levels\n| Token | Name |\n|---|---|\n| `dhl_express_worldwide` | DHL Express Worldwide |\n| `dhl_express_12_00` | DHL Express 12:00 |\n| `dhl_express_09_00` | DHL Express 9:00 |\n\n---\n\n## Surcharges to Watch For\n\nThese surcharges are common across carriers and can significantly impact final cost:\n\n| Surcharge | Carriers | Trigger |\n|---|---|---|\n| **Residential delivery** | UPS, FedEx | Delivering to a residential address |\n| **Saturday delivery** | UPS, FedEx | Delivery scheduled for Saturday |\n| **Signature confirmation** | All | Requesting signature on delivery |\n| **Additional handling** | UPS, FedEx | Packages exceeding certain dimensions or weight thresholds |\n| **Address correction** | UPS, FedEx | Carrier corrects an invalid address |\n| **Fuel surcharge** | All | Applied automatically, varies by carrier and period |\n| **Remote area / extended delivery** | DHL, FedEx | Delivery to remote or rural areas |\n\n---\n\n## Label Size Defaults by Carrier\n\n- **USPS:** 4x6 thermal labels standard\n- **UPS:** 4x6 or 4x8\n- **FedEx:** 4x6 standard, some services require 4x8\n- **DHL Express:** 4x6 standard\n\nWhen in doubt, use `PDF_4x6` -- it works across all carriers.\n\nFile v1.4.4:references/csv-format.md\n\n<!--\n  ⚠️  DO NOT EDIT, auto-generated from skills/shippo/references/csv-format.md by scripts/build-clawhub-bundle.js\n  Edits here will be overwritten on the next sync.\n  To change this content, edit the canonical source and re-run the sync script.\n-->\n\n# CSV Batch Format Specification\n\nThis document defines the CSV column format for batch shipment processing.\n\n---\n\n## Required Columns\n\nEvery row must have non-empty values for all required columns. Rows missing required values should be skipped and reported to the user.\n\n| Column | Description | Example |\n|---|---|---|\n| `shipment_id` | Unique identifier for the row (user's reference) | `ORD-001` |\n| `sender_name` | Sender full name | `Jane Smith` |\n| `sender_street1` | Sender street address | `731 Market St` |\n| `sender_city` | Sender city | `San Francisco` |\n| `sender_state` | Sender state/province | `CA` |\n| `sender_zip` | Sender postal code | `94103` |\n| `sender_country` | Sender country (ISO 3166-1 alpha-2) | `US` |\n| `sender_email` | Sender email (column required; value may be empty for domestic rows but must be non-empty for international) | `jane@example.com` |\n| `sender_phone` | Sender phone (column required; value may be empty for domestic rows but must be non-empty for international) | `+1-555-123-4567` |\n| `recipient_name` | Recipient full name | `John Doe` |\n| `recipient_street1` | Recipient street address | `456 Oak Ave` |\n| `recipient_city` | Recipient city | `Portland` |\n| `recipient_state` | Recipient state/province | `OR` |\n| `recipient_zip` | Recipient postal code | `97201` |\n| `recipient_country` | Recipient country (ISO 3166-1 alpha-2) | `US` |\n| `package_length` | Parcel length (as a number) | `12` |\n| `package_width` | Parcel width (as a number) | `8` |\n| `package_height` | Parcel height (as a number) | `6` |\n| `package_weight` | Parcel weight (as a number) | `2.5` |\n| `weight_unit` | Mass unit: `lb`, `kg`, `g`, or `oz` | `lb` |\n| `distance_unit` | Dimension unit: `in`, `cm`, `ft`, `m`, `mm`, or `yd` | `in` |\n\n---\n\n## Optional Columns\n\nWhen these columns are absent or empty for a row, omit the corresponding fields from the API call. Do not send empty strings.\n\n| Column | Description | Example |\n|---|---|---|\n| `recipient_email` | Recipient email address | `john@example.com` |\n| `recipient_phone` | Recipient phone number | `+1-555-987-6543` |\n| `sender_street2` | Sender address line 2 (apt, suite) | `Suite 200` |\n| `recipient_street2` | Recipient address line 2 (apt, suite) | `Apt 4B` |\n| `package_description` | Item description (used for customs) | `Cotton t-shirts` |\n| `declared_value` | Declared value (used for customs/insurance) | `45.00` |\n| `customs_contents_type` | Customs contents type per row | `MERCHANDISE` |\n| `metadata` | Free-form reference or order number | `PO-20240115` |\n\n---\n\n## Sample CSV\n\n```csv\nshipment_id,sender_name,sender_street1,sender_city,sender_state,sender_zip,sender_country,sender_email,sender_phone,recipient_name,recipient_street1,recipient_city,recipient_state,recipient_zip,recipient_country,package_length,package_width,package_height,package_weight,weight_unit,distance_unit,recipient_email,package_description,declared_value\nORD-001,Jane Smith,731 Market St,San Francisco,CA,94103,US,jane@example.com,+1-555-123-4567,John Doe,456 Oak Ave,Portland,OR,97201,US,12,8,6,2.5,lb,in,,Standard package,\nORD-002,Jane Smith,731 Market St,San Francisco,CA,94103,US,jane@example.com,+1-555-123-4567,Alice Brown,789 Elm St,Seattle,WA,98101,US,10,10,10,5,lb,in,alice@example.com,Fragile item,50.00\nORD-003,Jane Smith,731 Market St,San Francisco,CA,94103,US,jane@example.com,+1-555-123-4567,Bob Wilson,22 Rue de Rivoli,Paris,,75004,FR,8,6,4,1.2,lb,in,bob@example.fr,Cotton t-shirts,75.00\n```\n\nNotes about the sample:\n- Row 1 (ORD-001): Domestic US shipment with minimal optional fields.\n- Row 2 (ORD-002): Domestic US shipment with recipient email and declared value.\n- Row 3 (ORD-003): International shipment (US to FR). The `state` field is empty for Paris, which is acceptable for countries that do not use state/province. This row triggers customs declaration creation.\n\n---\n\n## Parsing and Validation Rules\n\n### Column Matching\n- Match columns by header name (case-insensitive). Trim whitespace from headers.\n- Extra columns not in the spec should be ignored.\n- If required columns are missing from the header row, report the missing columns and stop processing.\n\n### Row Validation\n- Skip empty rows silently.\n- For each row, check that all required columns have non-empty values.\n- Collect invalid rows (with row number and the specific missing/invalid fields) and report them to the user before proceeding.\n- Do not include invalid rows in the batch.\n\n### Data Type Handling\n- `package_length`, `package_width`, `package_height`, and `package_weight` should be read as numbers from the CSV, then passed as **strings** to the Shippo API (e.g., CSV value `12` becomes API value `\"12\"`).\n- `country` fields must be 2-letter ISO codes. If a row has a full country name (e.g., \"United States\"), attempt to map it or flag the row for user correction.\n- `weight_unit` must be one of: `lb`, `kg`, `g`, `oz`.\n- `distance_unit` must be one of: `in`, `cm`, `ft`, `m`, `mm`, `yd`.\n\n### International Detection\n- Compare `sender_country` and `recipient_country` for each row.\n- If they differ, the row is international and requires a customs declaration.\n- For international rows, `sender_email` and `sender_phone` are mandatory (carriers require them for customs). Flag any international row missing these fields.\n\n### Encoding\n- Expect UTF-8 encoding. If parsing fails, ask the user to verify the file is UTF-8 encoded.\n- Handle common CSV dialects: comma-delimited, with or without quoted fields.\n\n### Shared Sender Optimization\n- If all rows share the same sender address, note this to the user. The sender fields still must be present in every row for the CSV format, but the agent can confirm once and reuse.\n\n### Error Reporting Format\nWhen reporting validation errors, include:\n- Row number (1-indexed, not counting the header)\n- The shipment_id value (if present)\n- The specific field(s) that are missing or invalid\n- A summary count: \"N of M rows are valid and will be processed\"\n\nFile v1.4.4:references/customs-guide.md\n\n<!--\n  ⚠️  DO NOT EDIT, auto-generated from skills/shippo/references/customs-guide.md by scripts/build-clawhub-bundle.js\n  Edits here will be overwritten on the next sync.\n  To change this content, edit the canonical source and re-run the sync script.\n-->\n\n# Customs Declaration Guide\n\nThis document covers creating customs declarations for international shipments. A customs declaration is required whenever sender and recipient are in different countries.\n\n---\n\n## Overview: Two-Step Process\n\nInternational labels require customs documentation before the shipment can be created:\n\n1. **Create customs items** -- one per distinct product in the shipment.\n2. **Create the customs declaration** -- references the items and contains certifications.\n3. **Attach the declaration** to the shipment via the `customs_declaration` field.\n\n---\n\n## Step 1: Create Customs Items\n\nCall `CreateCustomsItem` once per distinct item type in the shipment.\n\n### Required Fields\n\n| Field | Type | Description |\n|---|---|---|\n| `description` | string | Plain-language description of the item (e.g., \"cotton t-shirt\") |\n| `quantity` | integer | Number of units |\n| `net_weight` | string | Weight per unit (as a string, e.g., \"0.5\") |\n| `mass_unit` | string | One of: `g`, `kg`, `lb`, `oz` |\n| `value_amount` | string | Declared monetary value per unit (as a string, e.g., \"25.00\") |\n| `value_currency` | string | ISO 4217 currency code (e.g., `USD`, `EUR`, `GBP`) |\n| `origin_country` | string | ISO 3166-1 alpha-2 country code where the item was manufactured (e.g., `US`, `CN`) |\n\n### Optional Fields\n\n| Field | Type | Description |\n|---|---|---|\n| `tariff_number` | string | HS/HTS harmonized tariff code (6-10 digits). Required by most carriers. Ask the user if not provided. |\n| `sku_code` | string | SKU or product code |\n| ` eccn_ear99` | string | Export Control Classification Number |\n| `metadata` | string | Free-form metadata |\n\n### Example\n\n```json\n{\n  \"description\": \"Cotton t-shirt, blue, size M\",\n  \"quantity\": 3,\n  \"net_weight\": \"0.3\",\n  \"mass_unit\": \"lb\",\n  \"value_amount\": \"15.00\",\n  \"value_currency\": \"USD\",\n  \"origin_country\": \"US\",\n  \"tariff_number\": \"6109100012\"\n}\n```\n\n### HS / Tariff Codes\n\nHS (Harmonized System) codes classify goods for customs. They are typically 6 digits internationally, extended to 8-10 digits for country-specific tariff schedules (HTS in the US).\n\n**USPS requires 6-digit HS codes on ALL international commercial shipments (as of September 2025). FedEx and DHL strongly recommend them. Shipments without HS codes risk delays or rejection.**\n\n- If the user does not know the HS code, ask them to describe the product. Common codes:\n  - Clothing: 6109 (t-shirts), 6110 (sweaters), 6204 (women's suits/trousers)\n  - Electronics: 8471 (computers), 8517 (phones), 8528 (monitors)\n  - Books: 4901\n  - Toys: 9503\n  - Cosmetics: 3304\n- If unsure, the user should consult their country's tariff schedule or a customs broker.\n\n---\n\n## Step 2: Create the Customs Declaration\n\nCall `CreateCustomsDeclaration` with the item object_ids from step 1.\n\n### Required Fields\n\n| Field | Type | Description |\n|---|---|---|\n| `certify` | boolean | Must be `true`. Certifies the information is accurate. |\n| `certify_signer` | string | Full name of the person certifying the declaration. |\n| `contents_type` | string | Type of shipment contents. See values below. |\n| `non_delivery_option` | string | What to do if the package is undeliverable. See values below. |\n| `items` | array | Array of customs item object_ids from step 1. |\n\n### Optional Fields\n\n| Field | Type | Description |\n|---|---|---|\n| `contents_explanation` | string | Required if `contents_type` is `OTHER`. Free-text explanation. |\n| `exporter_reference` | string | Exporter reference number |\n| `importer_reference` | string | Importer reference number |\n| `invoice` | string | Invoice number |\n| `license` | string | Export license number |\n| `certificate` | string | Certificate number |\n| `notes` | string | Additional notes |\n| `eel_pfc` | string | Electronic Export License or Post Office Filing Citation. Values: `NOEEI_30_37_a`, `NOEEI_30_37_h`, `NOEEI_30_36`, `AES_ITN`. **Strongly recommended for US-origin shipments** -- USPS will warn if absent. For shipments to Canada, use `NOEEI_30_36` regardless of value. For non-Canada destinations: shipments under $2,500 use `NOEEI_30_37_a`; shipments over $2,500 require an AES/ITN number. |\n| `incoterm` | string | Incoterms trade term (e.g., `DDP`, `DDU`, `DAP`, `FCA`). Some carriers require this to return rates. |\n| `b13a_filing_option` | string | Canada B13A filing. Values: `FILED_ELECTRONICALLY`, `SUMMARY_REPORTING`, `NOT_REQUIRED` |\n| `metadata` | string | Free-form metadata |\n\n### contents_type Values\n\n| Value | When to Use |\n|---|---|\n| `MERCHANDISE` | Commercial goods being sold |\n| `GIFT` | Gifts with no commercial value |\n| `SAMPLE` | Product samples |\n| `RETURN_MERCHANDISE` | Returned merchandise |\n| `HUMANITARIAN_DONATION` | Humanitarian aid or donations |\n| `DOCUMENTS` | Paper documents only |\n| `OTHER` | Anything else (must provide `contents_explanation`) |\n\n### non_delivery_option Values\n\n| Value | Meaning |\n|---|---|\n| `RETURN` | Return the package to sender if undeliverable |\n| `ABANDON` | Abandon the package (carrier disposes of it) |\n\nIf the user does not specify, ask them. `RETURN` is the safer default for valuable goods.\n\n### Example\n\n```json\n{\n  \"certify\": true,\n  \"certify_signer\": \"Jane Smith\",\n  \"contents_type\": \"MERCHANDISE\",\n  \"non_delivery_option\": \"RETURN\",\n  \"items\": [\n    \"customs_item_abc123\",\n    \"customs_item_def456\"\n  ],\n  \"invoice\": \"INV-2024-0042\",\n  \"eel_pfc\": \"NOEEI_30_37_a\"\n}\n```\n\n---\n\n### Commercial Invoice\n\nShippo automatically generates 3 copies of the commercial invoice for international shipments. The invoice URL is returned in the transaction response after label purchase.\n\n---\n\n## Step 3: Attach to Shipment\n\nWhen calling `CreateShipment`, include the `customs_declaration` field set to the object_id returned from step 2:\n\n```json\n{\n  \"address_from\": { ... },\n  \"address_to\": { ... },\n  \"parcels\": [ ... ],\n  \"customs_declaration\": \"customs_declaration_xyz789\"\n}\n```\n\n---\n\n## Sender Address Requirements for International Shipments\n\nThe sender address (`address_from`) must include:\n- `email` -- required by carriers for customs processing\n- `phone` -- required by carriers for customs processing\n\nIf the user's sender address is missing these fields, ask before proceeding.\n\n---\n\n## Batch Processing with Customs\n\nWhen processing a CSV batch that includes international rows:\n\n1. Identify international rows by comparing `sender_country` and `recipient_country`.\n2. For each international row, create customs items from the row data (use `package_description` for the item description, `package_weight` for net_weight, `declared_value` for value_amount).\n3. Create a customs declaration per international row.\n4. Include the declaration object_id in the batch shipment object's `customs_declaration` field.\n5. Ask the user once for shared values: `contents_type`, `non_delivery_option`, and `certify_signer`. Reuse across all international rows unless the CSV provides per-row values.\n\n---\n\n## Common Issues\n\n### Missing HS/Tariff Code\nMost carriers require tariff_number for customs clearance. If omitted, the shipment may be delayed or rejected at customs. Always ask the user for this value.\n\n### Value Declaration\nUnder-declaring item values is illegal and can result in fines or seizure. Ensure `value_amount` reflects the actual market value of the goods.\n\n### EEL/PFC for US Exports\nFor shipments to Canada, use `NOEEI_30_36` regardless of value. For non-Canada destinations: shipments under $2,500 use `NOEEI_30_37_a` (most common exemption); shipments over $2,500 require an AES/ITN filing -- set `eel_pfc` to the ITN number.\n\n### Restricted and Prohibited Items\nThe Shippo API does not enforce import/export restrictions. The user is responsible for ensuring their goods are legal to ship to the destination country. If the user mentions shipping batteries, liquids, food, plants, or weapons, advise them to check destination country import regulations.\n\n---\n\n## Decision Trees\n\n### contents_type Selection\n\nUse this logic to determine `contents_type` when the user does not specify:\n\n1. Is the user selling the items? --> `MERCHANDISE`\n2. Is it a gift with no commercial value? --> `GIFT`\n3. Is it a product sample? --> `SAMPLE`\n4. Are the contents paper documents only? --> `DOCUMENTS`\n5. Is it a return/exchange of previously purchased goods? --> `RETURN_MERCHANDISE`\n6. Is it a charitable/humanitarian donation? --> `HUMANITARIAN_DONATION`\n7. None of the above? --> `OTHER` (must also provide `contents_explanation`)\n\nWhen in doubt, ask the user. `MERCHANDISE` is the most common value.\n\n### Incoterms Selection\n\nIncoterms define who pays duties and taxes. Use this logic:\n\n- **B2C e-commerce (default):** `DDU` (Delivered Duty Unpaid) -- the recipient pays duties/taxes on delivery. This is the standard default.\n- **Seller prepays duties/taxes:** `DDP` (Delivered Duty Paid) -- the sender pays all duties and taxes. Not supported by USPS (always DDU). Supported by UPS, FedEx, DHL.\n- **FedEx/DHL warehouse or third-party handoff:** `FCA` (Free Carrier) -- seller delivers to a named place (carrier facility). Used for B2B or drop-ship scenarios.\n- **DHL Express:** Also supports `DAP` (Delivered at Place), similar to DDU.\n\nIf the user does not specify, use `DDU` for B2C shipments. If the user says they want to prepay duties for their customers, use `DDP`.\n\nFile v1.4.4:references/tool-reference.md\n\n<!--\n  ⚠️  DO NOT EDIT, auto-generated from skills/shippo/references/tool-reference.md by scripts/build-clawhub-bundle.js\n  Edits here will be overwritten on the next sync.\n  To change this content, edit the canonical source and re-run the sync script.\n-->\n\n# Shippo MCP Operation Reference\n\nReference for the Shippo operations callable through the hosted MCP server, grouped by category. Includes required/optional parameters, data types, and async behavior.\n\n**How these are invoked:** the server exposes a 4-tool meta-API (`shippo_list_tools`, `shippo_describe_tool`, `shippo_read_execute_tool`, `shippo_write_execute_tool`). The names below (e.g. `CreateShipment`, `ValidateAddress`, `GetTrack`) are operation names you pass to `shippo_read_execute_tool` (reads) or `shippo_write_execute_tool` (writes), not standalone MCP tools. See the `shippo-best-practices` skill for the discover-then-execute pattern.\n\n**Data type note:** Dimensions (length, width, height) and weight values must be passed as **strings**, not numbers (e.g., `\"12\"` not `12`). This applies to parcels, customs items, and all weight/dimension fields.\n\n**Parameter naming note:** Parameter names are **case-sensitive**, and the by-id operations use the exact spelling returned by `shippo_describe_tool` -- mostly PascalCase (`ShipmentId`, `OrderId`, `TransactionId`, `BatchId`, `CarrierAccountId`), with a few exceptions (`webhookId`, and v2 address operations use `address_id`). Body and query fields are snake_case. When unsure, call `shippo_describe_tool` first rather than guessing a casing.\n\n---\n\n## Addresses\n\n### `CreateAddress` (preferred)\nCreate and validate a new address using v2 field names. Returns validation results.\n- **Required:** `name` (string), `address_line_1` (string), `city_locality` (string), `country_code` (string, ISO 3166-1 alpha-2)\n- **Optional:** `address_line_2` (string), `address_line_3` (string), `state_province` (string), `postal_code` (string), `phone` (string), `email` (string), `company` (string), `is_residential` (boolean)\n\n### `ValidateAddress`\nValidate a US or international address by its fields using v2 field names. Returns validation results plus a recommended address.\n- **Required:** `address_line_1` (string), `country_code` (string, ISO 3166-1 alpha-2)\n- **US needs:** `state_province` + `city_locality` + `address_line_1`, **or** `address_line_1` + `postal_code`\n- **International needs:** `city_locality` + `address_line_1`\n- **Optional:** `city_locality` (string), `state_province` (string), `postal_code` (string), `address_line_2` (string), `organization` (string), `name` (string)\n\n### `ParseAddress`\nParse a freeform address string into structured components. Returns v2 field names (no country).\n- **Required:** `address_string` (string, freeform address text)\n\n### `ValidateAddressByID` (legacy)\nValidate an existing address by object ID using v1 field names.\n- **Required:** `AddressId` (string)\n\n### `GetAddress`\nRetrieve a previously created address by ID.\n- **Required:** `address_id` (string)\n\n### `ListAddresses`\nList all stored addresses. Supports pagination.\n- **Optional:** `page` (integer), `results` (integer, page size)\n\n---\n\n## Shipments\n\n### `CreateShipment`\nCreate a new shipment and retrieve available rates. **Async:** if `async` is true (default), returns immediately and rates must be polled via `GetShipment` or `ListShipmentRates`.\n- **Required:** `address_from` (object or string ID, v1 field names for inline), `address_to` (object or string ID, v1 field names for inline), `parcels` (array of parcel objects or string IDs)\n- **Optional:** `customs_declaration` (string, object ID), `extra` (object, for signature, insurance, etc.), `metadata` (string), `async` (boolean, default true), `carrier_accounts` (array of carrier account IDs to filter rates)\n- **Note:** Inline address objects use v1 names: `name`, `street1`, `city`, `state`, `zip`, `country`\n\n### `GetShipment`\nRetrieve a shipment by ID. Use to poll for rates after async creation.\n- **Required:** `ShipmentId` (string)\n\n### `ListShipments`\nList all shipments. Supports pagination.\n- **Optional:** `page` (integer), `results` (integer, page size)\n\n### `ListShipmentRates`\nRetrieve rates for an existing shipment by ID.\n- **Required:** `ShipmentId` (string)\n\n---\n\n## Rates\n\n### `GetRate`\nRetrieve a specific rate by ID.\n- **Required:** `RateId` (string)\n\n### `ListShipmentRatesByCurrencyCode`\nRetrieve shipment rates filtered to a specific currency.\n- **Required:** `ShipmentId` (string), `CurrencyCode` (string, ISO 4217 e.g., `USD`)\n- **Optional:** `page` (integer), `results` (integer)\n\n### `CreateLiveRate`\nGenerate live rates for a checkout flow with line items and address.\n- **Required:** `address_to` (object), `line_items` (array), `parcel` (object or template)\n- **Optional:** `address_from` (object), `carrier_accounts` (array)\n\n### `GetDefaultParcelTemplate`\nShow the current default parcel template for checkout rates. No parameters.\n\n### `DeleteDefaultParcelTemplate`\nClear the current default parcel template. No parameters.\n\n### `UpdateDefaultParcelTemplate`\nUpdate the default parcel template for checkout rates.\n- **Required:** `object_id` (string, parcel template ID)\n\n---\n\n## Transactions (Labels)\n\n### `CreateTransaction`\nPurchase a shipping label from an existing rate. **Async:** returns immediately with status `QUEUED`; poll via `GetTransaction` until status is `SUCCESS` or `ERROR`.\n- **Required:** `rate` (string, rate object_id)\n- **Optional:** `label_file_type` (string, e.g., `PDF_4x6`, `PNG`, `ZPLII`), `async` (boolean, default true), `metadata` (string)\n- **Response includes:** `label_url`, `tracking_number`, `tracking_url_provider` when status is `SUCCESS`\n\n### `GetTransaction`\nRetrieve a transaction (label) by ID. Use to poll async label purchases.\n- **Required:** `TransactionId` (string)\n\n### `ListTransactions`\nList all transactions. Supports filtering and pagination.\n- **Optional:** `page` (integer), `results` (integer), `object_status` (string), `tracking_status` (string)\n\n---\n\n## Tracking\n\n### `GetTrack`\nGet current tracking status for a carrier + tracking number.\n- **Required:** `Carrier` (string, carrier token e.g., `usps`, `ups`, `fedex`, `dhl_express`), `TrackingNumber` (string)\n\n### `CreateTrack`\nRegister a shipment for tracking webhook notifications.\n- **Required:** `carrier` (string), `tracking_number` (string)\n- **Optional:** `metadata` (string)\n\n---\n\n## Batches\n\n### `CreateBatch`\nCreate a new batch of shipments. **Async:** returns immediately with status `VALIDATING`; poll via `GetBatch` until status is `VALID` or `INVALID`.\n- **Required:** `default_carrier_account` (string, carrier account ID), `default_servicelevel_token` (string), `batch_shipments` (array of batch shipment objects)\n- **Optional:** `label_filetype` (string), `metadata` (string), `label_size` (string)\n- **Each batch shipment object requires:** `shipment` (object with `address_from`, `address_to`, `parcels`, and optionally `customs_declaration`)\n\n### `GetBatch`\nRetrieve a batch by ID. Includes status and per-shipment results.\n- **Required:** `BatchId` (string)\n- **Optional:** `object_results` (string) filters `batch_shipments` by per-shipment result -- e.g. `creation_failed`, `creation_succeeded`, `purchase_failed`, `purchase_succeeded` -- to pull just the failed shipments out of a large batch.\n- On `INVALID`, the actionable validation errors are the per-shipment `batch_shipments[].messages`, not a batch-level message.\n\n### `PurchaseBatch`\nPurchase labels for all valid shipments in a batch. **Async:** triggers purchase; poll `GetBatch` until status is `PURCHASED`.\n- **Required:** `BatchId` (string)\n- **Retrieving labels:** a purchased batch does not put each label URL inline on the batch. Each `batch_shipments[].transaction` is a Transaction object_id; call `GetTransaction` on it for that shipment's `label_url` and `tracking_number`. The batch-level `label_url` is a merged multi-label PDF (up to 100 labels per file) that cannot be split per order.\n\n### `AddShipmentsToBatch`\nAdd shipments to an existing batch (before purchase only).\n- **Required:** `BatchId` (string), `body` (array of batch shipment objects)\n\n### `RemoveShipmentsFromBatch`\nRemove shipments from an existing batch (before purchase only).\n- **Required:** `BatchId` (string), `shipment_ids` (array of string IDs, in the request body)\n\n---\n\n## Customs\n\n### `CreateCustomsDeclaration`\nCreate a customs declaration for international shipments.\n- **Required:** `certify` (boolean, must be true), `certify_signer` (string), `contents_type` (string), `non_delivery_option` (string), `items` (array of customs item object_ids)\n- **Optional:** `contents_explanation` (string, required if contents_type is OTHER), `exporter_reference` (string), `importer_reference` (string), `invoice` (string), `license` (string), `certificate` (string), `notes` (string), `eel_pfc` (string), `incoterm` (string), `b13a_filing_option` (string), `metadata` (string)\n\n### `GetCustomsDeclaration`\nRetrieve a customs declaration by ID.\n- **Required:** `CustomsDeclarationId` (string)\n\n### `ListCustomsDeclarations`\nList all customs declarations. Supports pagination.\n- **Optional:** `page` (integer), `results` (integer)\n\n### `CreateCustomsItem`\nCreate a customs item (individual line item within a declaration).\n- **Required:** `description` (string), `quantity` (integer), `net_weight` (string), `mass_unit` (string), `value_amount` (string), `value_currency` (string), `origin_country` (string)\n- **Optional:** `tariff_number` (string), `sku_code` (string), `eccn_ear99` (string), `metadata` (string)\n\n### `GetCustomsItem`\nRetrieve a customs item by ID.\n- **Required:** `CustomsItemId` (string)\n\n### `ListCustomsItems`\nList all customs items. Supports pagination.\n- **Optional:** `page` (integer), `results` (integer)\n\n---\n\n## Manifests\n\n### `CreateManifest`\nCreate an end-of-day manifest (SCAN form) for carrier pickup. **Async:** returns with status `QUEUED`; poll via `GetManifest`.\n- **Required:** `carrier_account` (string, carrier account ID), `shipment_date` (string, ISO 8601 date), `address_from` (object or string ID)\n- **Optional:** `transactions` (array of transaction IDs; if omitted, includes all eligible), `async` (boolean)\n\n### `GetManifest`\nRetrieve a manifest by ID.\n- **Required:** `ManifestId` (string)\n\n### `ListManifests`\nList all manifests. Supports pagination.\n- **Optional:** `page` (integer), `results` (integer)\n\n---\n\n## Parcels\n\n### `CreateParcel`\nCreate a new parcel object.\n- **Required:** `length` (string), `width` (string), `height` (string), `distance_unit` (string: `in`, `cm`, `ft`, `m`, `mm`, `yd`), `weight` (string), `mass_unit` (string: `lb`, `kg`, `g`, `oz`)\n- **Optional:** `template` (string, carrier parcel template token), `metadata` (string)\n\n### `GetParcel`\nRetrieve an existing parcel by ID.\n- **Required:** `ParcelId` (string)\n\n### `ListParcels`\nList all parcels. Supports pagination.\n- **Optional:** `page` (integer), `results` (integer)\n\n---\n\n## Parcel Templates\n\n### `ListCarrierParcelTemplates`\nList all carrier-provided parcel templates (e.g., USPS Flat Rate). Filterable by carrier.\n- **Optional:** `carrier` (string, carrier token), `include` (string)\n\n### `GetCarrierParcelTemplate`\nRetrieve a specific carrier parcel template.\n- **Required:** `CarrierParcelTemplateToken` (string)\n\n### `ListUserParcelTemplates`\nList all user-created parcel templates. No required parameters.\n\n### `CreateUserParcelTemplate`\nCreate a new user parcel template.\n- **Required:** `name` (string), `length` (string), `width` (string), `height` (string), `distance_unit` (string), `weight` (string), `mass_unit` (string)\n- **Optional:** `template` (string)\n\n### `GetUserParcelTemplate`\nRetrieve a user parcel template by ID.\n- **Required:** `UserParcelTemplateObjectId` (string)\n\n### `UpdateUserParcelTemplate`\nUpdate an existing user parcel template.\n- **Required:** `UserParcelTemplateObjectId` (string)\n- **Optional:** Same fields as create\n\n### `DeleteUserParcelTemplate`\nDelete a user parcel template.\n- **Required:** `UserParcelTemplateObjectId` (string)\n\n---\n\n## Carrier Accounts\n\n### `ListCarrierAccounts`\nList all carrier accounts. Supports pagination and filtering.\n- **Optional:** `page` (integer), `results` (integer), `carrier` (string), `account_id` (string)\n\n### `CreateCarrierAccount`\nCreate a new carrier account.\n- **Required:** `carrier` (string), `account_id` (string), `parameters` (object, carrier-specific)\n\n### `GetCarrierAccount`\nRetrieve a carrier account by ID.\n- **Required:** `CarrierAccountId` (string)\n\n### `UpdateCarrierAccount`\nUpdate a carrier account.\n- **Required:** `CarrierAccountId` (string)\n- **Optional:** `account_id` (string), `parameters` (object)\n\n### `GetCarrierRegistrationStatus`\nGet carrier registration status.\n- **Required:** `carrier` (string)\n\n### `InitiateOauth2Signin`\nConnect a carrier account using OAuth 2.0.\n- **Required:** `CarrierAccountObjectId` (string), `redirect_uri` (string)\n\n---\n\n## Orders\n\n### `CreateOrder`\nCreate a new order.\n- **Required:** `to_address` (object), `line_items` (array), `placed_at` (string, ISO 8601), `order_number` (string), `order_status` (string), `shipping_cost` (string), `shipping_cost_currency` (string)\n- **Optional:** `from_address` (object), `weight` (string), `weight_unit` (string), `notes` (string), `shipping_method` (string)\n\n### `GetOrder`\nRetrieve an order by ID.\n- **Required:** `OrderId` (string)\n\n### `ListOrders`\nList all orders. Supports pagination.\n- **Optional:** `page` (integer), `results` (integer), `order_status` (array of strings), `shop_app` (string)\n\n### Packing slip (known gap)\nThere is no packing-slip tool in the catalog. To retrieve a packing slip for an order, fall back to the REST API: `GET /orders/{order_id}/packingslip`.\n\n---\n\n## Refunds\n\n### `CreateRefund`\nCreate a refund (void a label). Must be requested within 30 days of purchase for most carriers.\n- **Required:** `transaction` (string, transaction object_id)\n- **Optional:** `async` (boolean)\n\n### `GetRefund`\nRetrieve a refund by ID.\n- **Required:** `RefundId` (string)\n\n### `ListRefunds`\nList all refunds. Supports pagination.\n- **Optional:** `page` (integer), `results` (integer)\n\n---\n\n## Pickups\n\n### `CreatePickup`\nSchedule a carrier pickup.\n- **Required:** `carrier_account` (string), `location` (object with address and building info), `transactions` (array of transaction IDs), `requested_start_time` (string, ISO 8601), `requested_end_time` (string, ISO 8601)\n- **Optional:** `is_test` (boolean)\n\n---\n\n## Service Groups\n\n### `ListServiceGroups`\nList all service groups. No required parameters.\n\n### `CreateServiceGroup`\nCreate a new service group.\n- **Required:** `name` (string), `description` (string), `flat_rate` (string), `flat_rate_currency` (string), `service_levels` (array)\n\n### `UpdateServiceGroup`\nUpdate an existing service group.\n- **Required:** `object_id` (string, service group ID, in the request body)\n- **Optional:** Same fields as create\n\n### `DeleteServiceGroup`\nDelete a service group.\n- **Required:** `ServiceGroupId` (string)\n\n---\n\n## Webhooks\n\n### `createWebhook`\nCreate a new webhook subscription.\n- **Required:** `url` (string), `event` (string, e.g., `track_updated`, `transaction_created`, `batch_created`)\n- **Optional:** `is_test` (boolean), `active` (boolean)\n\n### `getWebhook`\nRetrieve a specific webhook.\n- **Required:** `webhookId` (string)\n\n### `listWebhooks`\nList all webhooks. No required parameters.\n\n### `updateWebhook`\nUpdate an existing webhook.\n- **Required:** `webhookId` (string)\n- **Optional:** `url` (string), `event` (string), `is_test` (boolean), `active` (boolean)\n\n### `deleteWebhook`\nDelete a webhook.\n- **Required:** `webhookId` (string)\n\n---\n\n## Shippo Accounts\n\n### `ListShippoAccounts`\nList all Shippo accounts. Supports pagination.\n- **Optional:** `page` (integer), `results` (integer)\n\n### `CreateShippoAccount`\nCreate a Shippo account.\n- **Required:** `email` (string), `first_name` (string), `last_name` (string), `company_name` (string)\n\n### `GetShippoAccount`\nRetrieve a Shippo account.\n- **Required:** `ShippoAccountId` (string)\n\n### `UpdateShippoAccount`\nUpdate a Shippo account.\n- **Required:** `ShippoAccountId` (string)\n- **Optional:** `email` (string), `first_name` (string), `last_name` (string), `company_name` (string)\n\n---\n\n## Async Tools Summary\n\nThese tools return immediately and require polling to get final results:\n\n| Tool | Initial Status | Poll With | Final Status |\n|---|---|---|---|\n| `CreateShipment` (async=true) | `QUEUED` | `GetShipment` | rates populated |\n| `CreateTransaction` | `QUEUED` | `GetTransaction` | `SUCCESS` or `ERROR` |\n| `CreateBatch` | `VALIDATING` | `GetBatch` | `VALID` or `INVALID` |\n| `PurchaseBatch` | `PURCHASING` | `GetBatch` | `PURCHASED` |\n| `CreateManifest` | `QUEUED` | `GetManifest` | `SUCCESS` or `ERROR` |\n| `CreateRefund` (async=true) | `QUEUED` | `GetRefund` | `SUCCESS` or `ERROR` |\n\nFile v1.4.4:skill-card.md\n\n## Description:\n\nA shipping and logistics skill for Shippo that helps agents get multi-carrier rates, buy domestic and international labels with customs, validate addresses, track packages, run CSV batches, analyze costs, route integrations, and support SDK upgrades through Shippo's hosted OAuth MCP.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[shippo](https://clawhub.ai/user/shippo)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nExternal users, developers, and shipping operations teams use this skill to compare carrier rates, validate addresses, purchase Shippo labels, prepare customs details, track shipments, process batch CSV shipments, and troubleshoot Shippo integrations through the hosted OAuth MCP.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill can guide live label purchases, batch purchases, refunds, webhook changes, and deletion actions that affect a real Shippo account.\n\nMitigation: Require explicit user approval for write actions and review carrier, service, cost, ETA, refund, webhook, or deletion details before proceeding.\n\nRisk: Shipment, contact, customs, and tracking details may be sent to Shippo and carriers or included in local analysis reports at the user's request.\n\nMitigation: Minimize personal information in generated reports and confirm that the user is comfortable connecting their Shippo account through OAuth before use.\n\n## Reference(s):\n\n- [Project homepage](https://github.com/goshippo/ai)\n- [Shippo hosted MCP](https://mcp.shippo.com)\n- [API Concepts](https://docs.goshippo.com/docs/api_concepts/apiversioning)\n- [Address Validation Guide](https://docs.goshippo.com/docs/addresses/address_validation)\n- [Customs Reference](https://docs.goshippo.com/docs/exporting/internationalshipments)\n- [Carrier Accounts](https://docs.goshippo.com/docs/shipping/carrieraccounts)\n- [Webhooks](https://docs.goshippo.com/docs/tracking/webhooks)\n- [API Changelog](https://docs.goshippo.com/changelog)\n- [Carrier Guide](references/carrier-guide.md)\n- [CSV Batch Format Specification](references/csv-format.md)\n- [Customs Declaration Guide](references/customs-guide.md)\n- [Shippo MCP Operation Reference](references/tool-reference.md)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Markdown, Code, Shell commands, Configuration, Guidance, API Calls, Files]\n\n**Output Format:** [Markdown guidance with JSON snippets, API-operation instructions, and optional CSV or Markdown report files]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May create local analysis reports and CSV files when the user asks for shipping analysis.]\n\n## Skill Version(s):\n\n1.4.4 (source: server release metadata and skill frontmatter)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.4.3: 7 files, 35586 bytes\n\nFiles: references/carrier-guide.md (5951b), references/csv-format.md (6234b), references/customs-guide.md (9582b), references/tool-reference.md (16243b), skill-card.md (3645b), SKILL.md (54586b), _meta.json (125b)\n\nFile v1.4.3:SKILL.md\n\n---\nname: shippo\ndescription: \"A shipping and logistics skill for Shippo. Get multi-carrier rates (USPS, UPS, FedEx, DHL, 30+), buy domestic and international labels with customs, validate addresses, track packages with webhooks, and run bulk CSV batches, plus cost analysis, integration routing, and SDK-upgrade help. Runs through Shippo's hosted MCP with per-user OAuth (sign in once, nothing to copy or store). Uses Shippo's discounted carrier rates.\"\nversion: 1.4.3\nlicense: MIT\nmetadata:\n  openclaw:\n    emoji: \"📦\"\n    homepage: https://github.com/goshippo/ai\n---\n\n# Shippo Shipping Skill\n\n## Setup\n\n**MCP server:** Shippo's hosted MCP at `https://mcp.shippo.com`, with per-user Shippo OAuth. You authorize once through Shippo on first use, with nothing to copy or configure, and the client refreshes the token automatically.\n\nPoint your MCP client at the hosted server:\n\n```json\n{\n  \"mcpServers\": {\n    \"shippo\": {\n      \"type\": \"http\",\n      \"url\": \"https://mcp.shippo.com\"\n    }\n  }\n}\n```\n\nOn first use, your client runs the Shippo OAuth sign-in (in OpenClaw, `openclaw mcp login shippo`; in Claude Code, `/mcp`). No local Node process and nothing to store.\n\n**Prerequisites:** A Shippo account and at least one carrier account (Shippo provides managed accounts for USPS, UPS, FedEx, DHL Express by default). See `references/tool-reference.md` for the full tool catalog.\n\n**Purchases are live:** label purchases charge the authorized Shippo account for real. Confirm carrier, service, and cost with the user before any purchase.\n\n**Response envelope:** The MCP wraps most API responses in a Speakeasy envelope shaped like `{\"ContentType\": \"application/json\", \"StatusCode\": <code>, \"RawResponse\": {}, \"<PayloadName>\": {...actual response...}}`. The payload field is named after the response schema on success (e.g. `ParsedAddress`, `AddressPaginatedList`, `AddressValidationResultV2`, `AddressWithMetadataResponse`, `Shipment`, `CarrierAccountPaginatedList`) and after the HTTP status code on some errors (e.g. `fourHundredAndNineApplicationJsonObject` for a 409, the body may be `{}`). To extract the payload, find the field whose key is not `ContentType`, `StatusCode`, or `RawResponse`, and branch on `StatusCode` for success vs error.\n\n**Non-envelope errors:** Some failures bypass the envelope entirely and surface as an MCP-level error instead, the tool response has `isError: true` with a single text block containing a plaintext message like `Unexpected API response status or content-type: Status 404 Content-Type application/json Body: {\"detail\":\"Not found.\"}`. Argument-validation failures come back as JSON-RPC error code `-32602`. Handle both paths when reporting errors to the user.\n\n---\n\n## Best Practices\n\nLatest Shippo API version: **2018-02-08**. Send via the `Shippo-API-Version` header.\n\n### Using the Shippo MCP\n\nThe hosted Shippo MCP at `https://mcp.shippo.com` exposes exactly **4 tools** (a meta-API), not the underlying operations directly:\n\n- `shippo_list_tools`: discover which operation you need.\n- `shippo_describe_tool`: get that operation's input schema.\n- `shippo_read_execute_tool`: run a read (lists, gets, lookups).\n- `shippo_write_execute_tool`: run a write or mutation (creates, purchases, voids).\n\nEvery operation name in this skill (`ValidateAddress`, `CreateShipment`, `CreateTransaction`, `GetTrack`, etc.) is invoked **through** these wrappers, never called as a tool on its own. Standard discovery pattern: `shippo_list_tools` to find the operation, then `shippo_describe_tool` for its schema, then `shippo_read_execute_tool` or `shippo_write_execute_tool` to run it. The read/write split lets approval policies gate mutations separately. In the Claude apps these 4 tools may be deferred (loaded on demand), so an initial \"tool has not been loaded yet\" is normal: discover via the wrappers rather than guessing operation names.\n\n### Integration routing\n\n| Building…                                          | Recommended primitive          | See                                                                                        |\n|----------------------------------------------------|--------------------------------|--------------------------------------------------------------------------------------------|\n| Checkout flow with live shipping rates             | Rates at Checkout              | Rate Shopping (+ `shippo/references/rate-shopping-guide.md`)                               |\n| Single label purchase                              | Shipments + Transactions       | Label Purchase                                                                              |\n| Bulk label generation from CSV                     | Batches + Manifests            | Batch Shipping (+ `shippo/references/csv-format.md`)                                        |\n| Track packages across carriers                     | Tracking + webhooks            | Tracking                                                                                    |\n| Validate user addresses before save                | Addresses v2                   | Address Validation (+ `shippo/references/address-formats.md`)                               |\n| Analyze shipping spend / optimize carriers         | Shipments + Transactions list  | Shipping Analysis                                                                           |\n| International shipments                            | Customs Items + Declarations   | Label Purchase (+ `shippo/references/customs-guide.md` + `shippo/references/international-shipping.md`) |\n\nRead the relevant skill or reference before answering integration questions or writing code.\n\n### Critical rules\n\n- **Always validate addresses before purchasing labels.** Most \"no rates\" / \"label failed\" errors trace back to unvalidated addresses.\n- **Label purchases charge your live Shippo account for real.** Always confirm carrier, service, and cost with the user before any purchase.\n- **Always confirm purchase before `CreateTransaction`.** Show carrier/service/cost/eta and require explicit user confirmation.\n- **Parcel dimensions and weight must be strings, not numbers.** Use `\"10\"`, never `10`.\n- **Label URLs are S3 signed URLs.** Always display the complete URL, truncating breaks the signature.\n- **Rates expire after 7 days.** Re-create the shipment for fresh rates.\n- **By-id parameter names are case-sensitive** (mostly PascalCase: `ShipmentId`, `TransactionId`, `OrderId`). Use the exact name from `shippo_describe_tool`; do not guess snake_case.\n- **Never retry a 403/404 tool error with the same arguments.** Ownership and not-found errors are permanent for those inputs; verify the ID via the matching `List*` operation first. The generic `An internal error occurred. Please retry later.` relay most often traces to an input issue too, so verify inputs before retrying, and retry the identical call at most once.\n\n### Response handling\n\nThe MCP wraps responses in a Speakeasy envelope. Some failures bypass the envelope. See `shippo/references/response-envelope.md` and `shippo/references/error-reference.md` for parsing logic and error-handling patterns.\n\n### Connecting\n\nThe hosted MCP at `https://mcp.shippo.com` uses per-user Shippo OAuth. You authorize once through Shippo (in Claude Code, run `/mcp` and sign in), and the session refreshes automatically. There is nothing to copy or configure. Once you are connected, the workflow guidance below is unchanged.\n\n- **Two 401 strings to recognize:**\n  - `\"Token does not exist\"`: the credential is invalid, revoked, or for a different account. Re-authorize the Shippo OAuth session.\n  - `\"Authentication credentials were not provided\"`: no credential reached Shippo. The OAuth session is not authorized yet, or it has expired. Re-authorize the Shippo OAuth session.\n\n### Purchases are live\n\nLabel and batch purchases charge the authorized Shippo account for real money. Before any `CreateTransaction` or `PurchaseBatch`, show the carrier, service level, cost, and ETA, and get explicit user confirmation. Do not proceed without it.\n\n### Key documentation\n\n- [API Concepts](https://docs.goshippo.com/docs/api_concepts/apiversioning): request shapes, versioning, auth\n- [Address Validation Guide](https://docs.goshippo.com/docs/addresses/address_validation): validation depth varies by country\n- [Customs Reference](https://docs.goshippo.com/docs/exporting/internationalshipments): incoterms, contents types, HS codes\n- [Carrier Accounts](https://docs.goshippo.com/docs/shipping/carrieraccounts): managed vs custom accounts\n- [Webhooks](https://docs.goshippo.com/docs/tracking/webhooks): event types, signature verification\n\n(Once Mintlify migration completes, `.md` URL suffixes will provide raw markdown access for AI agents.)\n\n---\n\n## Address Validation\n\n### Address Field Format\n\nThe Shippo API uses **v1 field names** for address components in most endpoints (including `CreateShipment`). Always use:\n\n| Field | Description | Example |\n|---|---|---|\n| `name` | Full name | `Jane Smith` |\n| `street1` | Street address line 1 | `731 Market St` |\n| `street2` | Street address line 2 (optional) | `Suite 200` |\n| `city` | City | `San Francisco` |\n| `state` | State or province | `CA` |\n| `zip` | Postal code | `94103` |\n| `country` | ISO 3166-1 alpha-2 country code | `US` |\n| `email` | Email (required for international senders) | `jane@example.com` |\n| `phone` | Phone (required for international senders) | `+1-555-123-4567` |\n\nNote: `CreateAddress` and `ValidateAddress` take the v2 field names (`address_line_1`, `city_locality`, `state_province`, `postal_code`), but when passing addresses inline to `CreateShipment`, you must use the v1 names above.\n\n---\n\n### Validate a Structured Address\n\n1. Collect at minimum: `street1`, `city`, `state`, `zip`, `country` (ISO 3166-1 alpha-2).\n2. Call `CreateAddress` with the address fields. This creates the address and returns an object ID.\n3. Call `ValidateAddress` with the address fields to get validation results. Note: this endpoint takes address fields as query parameters, not an object ID.\n4. Check `analysis.validation_result.value` in the response. Values: `\"valid\"`, `\"invalid\"`, or `\"partially_valid\"` (address found with corrections applied). Check `analysis.validation_result.reasons` for details.\n5. Report the standardized address back. Highlight any corrected fields (listed in `changed_attributes`). Note `analysis.address_type` (`\"residential\"`, `\"commercial\"`, or `\"unknown\"`) -- residential classification affects carrier surcharges.\n6. If invalid: relay the reason descriptions. If the API returns a `recommended_address`, present it to the user.\n7. If `partially_valid`: show what was corrected and ask the user to confirm the corrections are acceptable.\n\n---\n\n### Parse a Freeform Address\n\n1. Call `ParseAddress` with the raw string (e.g., \"123 Main St, Springfield IL 62704\").\n2. Review the structured output for completeness. The parse response uses v2 field names: `address_line_1`, `city_locality`, `state_province`, `postal_code`.\n3. Note: the parse response does not include `country`. You must ask the user for the country or infer it, then add it before proceeding.\n4. Validate the parsed result by passing the fields to `CreateAddress` then `ValidateAddress` (follow the structured address workflow above from step 2).\n\n---\n\n### International Addresses\n\n- Always require the `country` field. Do not guess.\n- Pass non-Latin characters as-is; the API handles encoding.\n- Validation depth varies by country. US, CA, GB, AU, and major EU countries have deep validation. Others may only confirm structural completeness. Inform the user of this limitation.\n\n---\n\n### Bulk Address Validation\n\nThere is no batch validation endpoint. Call `CreateAddress` per address. Track results (row number, valid/invalid, corrections, errors, residential classification) and report a summary when done. For 50+ addresses, set expectations about processing time and provide progress updates.\n\n---\n\n### Re-validate an Existing Address\n\nCall `ValidateAddress` with the address fields. This endpoint validates by address fields, not by object ID.\n\n---\n\n### Duplicate Addresses\n\nIf `CreateAddress` returns a \"Duplicate address\" error, the address already exists in the account. Retrieve it via `ListAddresses` or proceed directly to validation.\n\n---\n\n### Quick Reference\n\n**Validate an address:**\n`CreateAddress` (saves address) + `ValidateAddress` (validates with same fields)\n\n**Parse then validate:**\n`ParseAddress` -> add country -> `CreateAddress` + `ValidateAddress`\n\n---\n\n## Rate Shopping\n\n### Get Rates for a Shipment\n\n1. Collect: origin address, destination address, parcel (length, width, height, distance_unit, weight, mass_unit). All dimension and weight values must be **strings** (e.g., `\"10\"` not `10`).\n2. Optionally validate both addresses with `ValidateAddress` (see Address Validation).\n3. Call `CreateShipment` with `address_from`, `address_to` (as inline address objects using v1 field names -- `street1`, `city`, `state`, `zip`, `country` -- not object IDs), and `parcels`.\n4. The response `rates` array contains available options. Present a table: carrier, service level, price, estimated days.\n5. Note: the same carrier may return duplicate rates from multiple carrier accounts. Present the best rate per carrier/service combination.\n6. Each rate carries an `object_id`. To buy a label, pass the chosen rate's `object_id` to the purchase flow (see Label Purchase); you do not re-send the address or parcel.\n\n---\n\n### Rate Expiration\n\nRates expire after 7 days. If a user tries to purchase a rate that was retrieved more than 7 days ago, create a new shipment to get fresh rates.\n\n---\n\n### Filter by Speed\n\nMap user requests: \"overnight\" = estimated_days 1, \"2-day\" = estimated_days <= 2, \"within N days\" = estimated_days <= N. Filter the rates array accordingly. If nothing matches, show the fastest available option.\n\n---\n\n### International Rates\n\nSome carriers may return international rates without a customs declaration, but others will not. If no rates are returned, try attaching a customs declaration to the shipment. Some carriers also require a phone number on the destination address for international rate retrieval. Inform the user that customs will be required at label purchase time regardless. See `shippo/references/customs-guide.md` for customs details.\n\n---\n\n### Checkout Rates (Line Items)\n\nCall `CreateLiveRate` instead of `CreateShipment`. Accepts `address_from`, `address_to`, and `line_items` (each with title, quantity, total_price, currency, weight, weight_unit).\n\n---\n\n### Rates in a Specific Currency\n\nCall `ListShipmentRatesByCurrencyCode` with the preferred ISO currency code (USD, EUR, GBP, CAD, etc.).\n\n---\n\n### Recommendation\n\nIdentify the cheapest (lowest `amount`), fastest (lowest `estimated_days`), and best-value options from the rates array. These are not API fields -- compute them by sorting the rates array yourself. State the trade-off: \"Option A is $X cheaper but takes Y more days than Option B.\"\n\n---\n\n### Troubleshooting: No Rates\n\n- Verify both addresses passed validation (most common cause).\n- Confirm parcel dimensions are reasonable (not zero, not exceeding carrier limits).\n- Shippo provides managed carrier accounts by default for major carriers. If no rates are returned, the issue is more likely address validation, unsupported route, or parcel dimensions -- not missing carrier accounts. You can verify with `ListCarrierAccounts` if needed.\n- Rates expire after 7 days. If stale, create a new shipment to get fresh rates.\n\n---\n\n### Quick Reference\n\n**Get rates:**\n(optional) `ValidateAddress` (x2) -> `CreateShipment` (with inline addresses) -> read `rates` array\n\n---\n\n## Label Purchase\n\n### Purchases Are Live\n\nLabel purchases charge the authorized Shippo account for real. **Before purchasing, explicitly state \"this will charge your Shippo account\" with the carrier, service, and cost, and require the user to acknowledge.** Do not purchase without that confirmation.\n\n---\n\n### Purchase Confirmation Gate\n\nBefore every call to `CreateTransaction`, summarize the following and ask the user for explicit confirmation:\n- Carrier and service level\n- Estimated cost\n- Estimated delivery time\n- Origin and destination\n\n**Do not proceed without explicit user confirmation.**\n\n---\n\n### Domestic Label\n\n1. Optionally validate both addresses with `ValidateAddress` (see Address Validation).\n2. Call `CreateShipment` with `address_from`, `address_to` (as inline address objects using v1 field names -- `street1`, `city`, `state`, `zip`, `country`), `parcels`, and `async: false`.\n3. Present rates to the user. Let them choose.\n4. **Confirm purchase** (see Purchase Confirmation Gate above).\n5. Call `CreateTransaction` with: `rate` (selected rate object_id), `label_file_type` (default `PDF_4x6`), `async: false`.\n6. Check response `status`:\n   - `SUCCESS`: return `tracking_number`, `label_url` (display the COMPLETE URL -- S3 signed URLs break if truncated), and `tracking_url_provider`.\n   - `QUEUED`/`WAITING`: poll `GetTransaction` until resolved.\n   - `ERROR`: report messages from the `messages` array.\n\n---\n\n### International Label\n\nAll domestic steps apply, plus customs handling before shipment creation. See `shippo/references/customs-guide.md` for the full customs workflow.\n\n1. Optionally validate addresses with `ValidateAddress`. Sender must include `email` and `phone`. Ask if missing.\n2. Create customs items: call `CreateCustomsItem` per item (description, quantity, net_weight, mass_unit, value_amount, value_currency, origin_country, tariff_number). Alternatively, you can skip this step and pass inline item objects directly in the declaration (step 3).\n3. Create the customs declaration: call `CreateCustomsDeclaration` with contents_type, non_delivery_option, certify: true, certify_signer, and the items (either object_ids from step 2, or inline item objects). See `shippo/references/customs-guide.md` for field details.\n4. Call `CreateShipment` with all standard fields plus `customs_declaration` (the declaration object_id).\n5. Present rates, **confirm purchase** (see Purchase Confirmation Gate), then purchase label and return results as in the domestic flow.\n\n#### Contents Type Decision Tree\n\nUse this to determine the correct `contents_type` value:\n\n| Scenario | Value |\n|---|---|\n| Selling to the recipient (commercial sale) | `MERCHANDISE` |\n| Sending a free gift | `GIFT` |\n| Sending a product sample | `SAMPLE` |\n| Paper documents only | `DOCUMENTS` |\n| Customer returning a purchased item | `RETURN_MERCHANDISE` |\n| Charitable donation | `HUMANITARIAN_DONATION` |\n| None of the above | `OTHER` (requires `contents_explanation`) |\n\n#### Incoterms Decision Logic\n\nThe `incoterm` field on the customs declaration controls who pays duties and taxes:\n\n- **B2C / e-commerce (default):** Use `DDU` (Delivered Duty Unpaid) -- recipient pays duties at delivery.\n- **Seller prepays duties:** Use `DDP` (Delivered Duty Paid) -- seller covers all duties and taxes.\n- **FedEx/DHL only:** `FCA` (Free Carrier) is available for advanced trade scenarios.\n\nIf the user does not specify, default to `DDU` for standard e-commerce shipments.\n\n---\n\n### Return Labels\n\nTo generate a return label, swap `address_from` and `address_to` so the original recipient becomes the sender and the original sender becomes the recipient. All other steps (shipment creation, rate selection, label purchase) remain the same.\n\n---\n\n### Label Format Options\n\nDefault to `PDF_4x6` unless the user specifies otherwise. Supported formats: `PDF_4x6`, `PDF_4x8`, `PDF_A4`, `PDF_A5`, `PDF_A6`, `PDF`, `PDF_2.3x7.5`, `PNG`, `PNG_2.3x7.5`, `ZPLII`.\n\n---\n\n### Label Customization Options\n\nWhen purchasing a label via `CreateTransaction`, the following options may be set on the shipment or rate:\n\n- **Signature confirmation**: set `signature_confirmation` on the shipment's `extra` field. Values: `STANDARD`, `ADULT`, `CERTIFIED`, `INDIRECT`, `CARRIER_CONFIRMATION`.\n- **Insurance**: set `insurance` on the shipment's `extra` field with `amount`, `currency`, and `provider`.\n- **Saturday delivery**: set `saturday_delivery` to `true` in the shipment's `extra` field. Only supported by certain carriers and service levels.\n- **Reference fields**: pass `metadata` on the transaction for order numbers or internal references.\n\n---\n\n### Label from Existing Rate\n\nIf the user already has a rate object_id: optionally call `GetRate` to confirm details, then **confirm purchase** (see Purchase Confirmation Gate), then call `CreateTransaction` directly.\n\n---\n\n### Voiding a Label\n\nCall `CreateRefund` with the transaction object_id.\n\n**Refund limitations:** Void/refund eligibility depends on carrier and timing. Not all labels can be refunded after purchase. If `CreateRefund` fails, advise the user to contact Shippo support.\n\n---\n\n### Quick Reference\n\n**Domestic label:**\n(optional) `ValidateAddress` (x2) -> `CreateShipment` (with inline addresses) -> user picks rate -> confirm -> `CreateTransaction`\n\n**International label:**\n(optional) `ValidateAddress` (x2) -> `CreateCustomsItem` (per item) -> `CreateCustomsDeclaration` -> `CreateShipment` (with inline addresses + customs_declaration) -> user picks rate -> confirm -> `CreateTransaction`\n\n**Return label:**\nSame as domestic/international, but swap `address_from` and `address_to`.\n\n**Order-to-label:**\n`CreateOrder` -> `CreateShipment` (using order address/item data) -> user picks rate -> confirm -> `CreateTransaction` -> packing slip (REST fallback, see below)\n\n---\n\n### Orders and Packing Slips\n\nUse orders to represent e-commerce fulfillment requests. An order captures the shipping address, line items, and totals -- then feeds into the standard label purchase workflow.\n\n#### Tools\n\n- **`CreateOrder`**: Create an order with line items, shipping address, and order details.\n- **`GetOrder`**: Retrieve an order by its object_id.\n- **`ListOrders`**: List all orders.\n- **Packing slip (known gap):** Generate a packing slip PDF for an order. There is no packing-slip tool in the MCP catalog. The underlying REST endpoint exists at `GET /orders/{ORDER_ID}/packingslip/` (returns a 24-hour S3 PDF link). Fall back to a direct REST call, or advise the user to use the Shippo dashboard until the MCP gap is closed.\n\n#### Workflow\n\n1. Call `CreateOrder` with the shipping address, line items (title, quantity, sku, total_price, etc.), and order-level fields.\n2. Use the order's address and item data to call `CreateShipment`, then follow the standard label purchase flow (rate selection, confirmation, `CreateTransaction`).\n3. After purchasing the label, generate a packing slip via the REST fallback (see Tools above for the known MCP gap).\n\n---\n\n## Tracking\n\n### Track by Number\n\n1. Determine carrier and tracking number. Carrier must be a lowercase Shippo token (e.g., `usps`, `ups`, `fedex`, `dhl_express`). See `shippo/references/carrier-guide.md` for tracking number format hints per carrier. If uncertain, ask the user.\n2. Call `GetTrack` with `carrier` and `tracking_number`.\n3. Key response fields: `tracking_status` (status, status_details, status_date, location), `tracking_history`, `eta`.\n4. Each tracking event includes a `substatus` object with `code`, `text`, and `action_required` (boolean). Include substatus details when presenting tracking history -- these provide more specific information about what happened at each step.\n5. Present: current status, location, ETA, substatus details, and chronological event history (most recent first).\n\n---\n\n### Status Values\n\nSee `shippo/references/carrier-guide.md` for carrier-specific status nuances. Standard values:\n\n| Status | Meaning |\n|---|---|\n| PRE_TRANSIT | Label created, carrier has not received the package |\n| TRANSIT | Package is in transit |\n| DELIVERED | Delivered |\n| RETURNED | Being returned or returned to sender |\n| FAILURE | Delivery failed |\n| UNKNOWN | No tracking information from carrier |\n\nThe `eta` field is provided by most major carriers (USPS, UPS, FedEx, DHL Express) but availability is carrier-dependent, it may be `null` for regional carriers or for shipments before the carrier has finalized routing. Treat absence as informational, not as an error condition.\n\n---\n\n### Find Trackable Packages\n\nCall `ListTransactions`. Filter for `object_status: SUCCESS`. Each successful transaction has `tracking_number` and carrier info. Then call `GetTrack` for selected items.\n\n---\n\n### Register a Tracking Webhook\n\n1. Get the user's HTTPS webhook URL.\n2. Call `createWebhook` with `url` and `event: track_updated`.\n3. Optionally call `CreateTrack` with carrier and tracking number to register a specific shipment for push updates.\n\n---\n\n### Quick Reference\n\n**Track a package:**\n`GetTrack` with carrier + tracking number\n\n**Find past shipment tracking:**\n`ListTransactions` -> filter SUCCESS -> `GetTrack`\n\n---\n\n## Batch Shipping\n\n### Purchases Are Live\n\nBatch purchases charge the authorized Shippo account for real. Before `PurchaseBatch`, show the shipment count, carrier/service, and estimated total cost, and require explicit user confirmation.\n\n---\n\n### Purchase Confirmation Gate\n\nBefore every call to `PurchaseBatch`, summarize the following and ask the user for explicit confirmation:\n- Total number of shipments to be purchased\n- Carrier and service level (or selection rule if varied)\n- Estimated total cost\n- Number of domestic vs international shipments\n\n**Do not proceed without explicit user confirmation.**\n\n---\n\n### CSV Batch Processing\n\nSee `shippo/references/csv-format.md` for the column specification.\n\n1. Read and parse the CSV. Validate required columns are present. Report row count.\n2. Validate each row for non-empty required fields. Report invalid rows with reasons.\n3. Detect international rows (sender_country != recipient_country). Create customs declarations for those rows. See `shippo/references/customs-guide.md`. Use correct customs enum values: `RETURN_MERCHANDISE` (not `RETURN`) for returned goods, `HUMANITARIAN_DONATION` (not `HUMANITARIAN`) for charitable donations.\n4. Build the `batch_shipments` array with inline address and parcel objects per row.\n5. Call `CreateBatch` with the array.\n6. Poll `GetBatch` until status changes from `VALIDATING` to `VALID`. See Polling Intervals below.\n7. Review per-shipment validation results. Report failures before proceeding.\n8. **Confirm purchase** (see Purchase Confirmation Gate above).\n9. Call `PurchaseBatch` to buy labels for all valid shipments.\n10. Poll `GetBatch` until status changes from `PURCHASING` to `PURCHASED`. See Polling Intervals below.\n11. Report: total attempted, succeeded, failed. For successes: tracking_number and label_url (complete URL). For failures: error messages.\n\n#### Batch Size Guidance\n\nFor batches over 500 shipments, consider splitting into multiple batches. Large batches take longer to validate and purchase, and a single failure can be harder to diagnose.\n\n---\n\n### Polling Intervals\n\n- For batches under 100 shipments: poll every 3-5 seconds.\n- For batches with 100+ shipments: poll every 5-10 seconds.\n- Report progress to the user every 30 seconds.\n- Stop after 60 retries and suggest the user check back later using `GetBatch` with the batch object_id.\n\n---\n\n### Batch with Rate Shopping\n\n1. Call `CreateShipment` per shipment to get rate quotes (see Rate Shopping).\n2. Present rates. User picks a service level rule (e.g., \"cheapest for each\" or a specific carrier/service).\n3. Build `batch_shipments` with `servicelevel_token` per item.\n4. Create, validate, **confirm purchase**, purchase, report as above.\n\n---\n\n### Managing an Existing Batch\n\n- Add shipments: `AddShipmentsToBatch` (before purchase only). Note: adding an invalid shipment will change the entire batch status to `INVALID`. Check per-shipment statuses after adding.\n- Remove shipments: `RemoveShipmentsFromBatch` (before purchase only).\n\n---\n\n### End-of-Day Manifest\n\n1. Collect: `carrier_account` (object_id), `shipment_date` (YYYY-MM-DD, default today), `address_from` (pickup address).\n2. Optionally collect specific transaction object_ids to scope the manifest. You must pass specific transaction object_ids -- there is no auto-include for a date range.\n3. Call `CreateManifest`.\n4. Poll `GetManifest` until status is `SUCCESS` or `ERROR`.\n5. Return the manifest PDF URL(s) and shipment count.\n\n---\n\n### Quick Reference\n\n**CSV batch:**\nParse CSV -> `CreateCustomsDeclaration` (international rows) -> `CreateBatch` -> poll `GetBatch` -> confirm -> `PurchaseBatch` -> poll `GetBatch`\n\n**Manifest:**\n`CreateManifest` (with transaction object_ids) -> poll `GetManifest`\n\n---\n\n## Shipping Analysis\n\n### Geographic Cost Analysis\n\n1. Confirm origin address, destination list (or use representative cities), and parcel details.\n2. Call `ListCarrierAccounts` to see configured carriers.\n3. Call `CreateShipment` per destination to collect rates. Creating shipments is free; only `CreateTransaction` costs money.\n4. Write results to `analysis/` directory (markdown report + CSV). Columns: Route, Destination, Carrier, Service, Cost, Currency, EstimatedDays, Zone.\n\n---\n\n### Package Optimization\n\n1. Confirm the route.\n2. Define dimension profiles to test (or use user-provided ones).\n3. Check `ListCarrierParcelTemplates` and `ListUserParcelTemplates` for flat-rate and saved templates. See `shippo/references/rate-shopping-guide.md` for dimensional weight and flat-rate guidance.\n4. Call `CreateShipment` per profile on the same route.\n5. Compare: cheapest rate, carrier options, fastest option per profile. Note where flat-rate templates beat custom dimensions and where dimensional weight causes price jumps. See `shippo/references/carrier-guide.md` for carrier-specific weight limits and surcharges.\n\n---\n\n### Carrier Comparison\n\n1. Call `CreateShipment` for the route.\n2. Group the `rates` array by `provider`.\n3. Per carrier: cheapest service, fastest service, number of service levels, price range.\n\n---\n\n### Historical Cost Optimization\n\n1. Call `ListShipments` and `ListTransactions` to get past activity.\n2. Cross-reference: what the user paid vs. what alternatives were available.\n3. Identify patterns: carrier concentration, service-level mismatch, consistent overpayment.\n4. For a sample of shipments with tracking numbers, call `GetTrack` to check actual vs. estimated delivery times.\n5. If fewer than 5 successful transactions exist (not just shipments -- shipments are rate quotes, transactions represent actual spend), redirect to forward-looking analysis.\n\n---\n\n### Output Conventions\n\nWrite reports to the `analysis/` directory. Create it if it does not exist. Include both markdown and CSV. CSV must have a header row. Markdown must include a timestamp and input parameters.\n\n---\n\n### Quick Reference\n\n**Cost analysis:**\n`ListCarrierAccounts` -> `CreateShipment` (per destination) -> read `rates` arrays -> write report\n\n**Carrier comparison:**\n`CreateShipment` -> group `rates` by `provider` -> summarize\n\n**Historical review:**\n`ListShipments` + `ListTransactions` -> cross-reference -> `GetTrack` (sample) -> write report\n\n---\n\n## Upgrades\n\nThe Shippo MCP is hosted at `https://mcp.shippo.com`. It is OAuth-only and auto-updates server-side, so there is nothing to install or upgrade on your side. This skill covers what stays your responsibility: API version awareness, webhook payload versioning, and troubleshooting the hosted session.\n\n### API version handling\n\nThe current Shippo API version is **2018-02-08**. Shippo uses a single long-lived API version, and the hosted server manages it for you server-side. You do not set the `Shippo-API-Version` header yourself when going through the hosted MCP.\n\nWhat backward-compatibility means in practice:\n\n- Most changes are backward-compatible: new optional fields, new resources, additional webhook events. Existing calls keep working.\n- Breaking changes are rare and announced via release notes.\n- Because the server picks the version, you don't pin anything client-side. Your job is to handle new fields gracefully (see webhook versioning below) rather than to manage versions.\n\nShippo API changes are tracked in [the API changelog](https://docs.goshippo.com/changelog). As of 2026-06, no recent breaking changes affect the workflows covered by this skill set.\n\n### Webhook event versioning\n\nWebhook events can include new fields without bumping the API version. To handle them gracefully:\n\n- Default to ignoring unknown fields in your webhook handler, never fail-closed on a field you don't recognize.\n- Subscribe only to the specific event types you need (`track_updated`, `transaction_created`, `transaction_updated`, etc.).\n- Verify webhook signatures using the `Shippo-Signature` header per [webhook docs](https://docs.goshippo.com/docs/tracking/webhooks).\n\n### Troubleshooting the hosted MCP\n\n#### `401` or `403` errors\n\nThe OAuth session has expired or is not authorized. Re-authorize the Shippo OAuth session: in Claude Code, run `/mcp` and sign in again.\n\n#### Tools changed or missing after a server update\n\nThe hosted server auto-updates, so the tool catalog can shift without any action on your side. Re-list the current tools via `shippo_list_tools` to see what is available now.\n\n#### \"Not found\" errors for objects you expect to exist\n\nMost likely the object does not exist on the authorized account, or it belongs to a different account. Confirm you are signed in to the account that owns the object (re-authorize via `/mcp` if needed).\n\n### Auditing an existing integration\n\nBefore making a change to a production integration:\n\n1. Don't pin anything client-side. The hosted server manages the API version, so there's nothing to pin.\n2. Verify webhook handlers ignore unknown fields.\n3. Review the [API changelog](https://docs.goshippo.com/changelog) for any breaking changes.\n4. Re-list tools via `shippo_list_tools` after an update to catch renamed or added operations.\n\n---\n\n## Support Ticket Builder\n\nTurn a single shipment identifier into a complete, **classified**, well-structured\nsupport package for the Shippo support team. The agent classifies the issue,\ngathers every relevant fact from the Shippo MCP (running issue-type-specific\nlookups, not just the lost-package set), computes the triage timeline, and emits\ntwo things:\n\n1. A **human copy-paste block** for the ticket body.\n2. A **structured JSON block** tagged with a routing queue, so the ticket can be\n   piped into the ticketing system and land in the right pipeline without a\n   human re-classifying it.\n\nThis dual output is the point: completeness *and* correct routing are what kill\nthe back-and-forth.\n\nAudience: Shippo support agents. Output uses Shippo terminology, object IDs, and\nan internal routing tag. It is not customer-facing copy.\n\n### When to use\n\nUse this skill when someone wants to escalate or document a shipping problem and\nasks for a support ticket / message to Shippo support, e.g. \"package is stuck,\"\n\"label was charged but never shipped,\" \"why was I charged more than the rate I\nsaw,\" \"refund this label I never used,\" \"where is this delivery,\" \"the address\nlooks wrong,\" \"tracking updates aren't coming through,\" \"can't get rates from\nthis carrier.\" It produces **text + JSON to copy and paste**; it does not open a\nJira ticket or send Slack/email itself.\n\n### Step 1: Classify the issue (do this first)\n\nPick exactly one **canonical issue type** from the customer's description. The\nissue type drives both the routing tag and which extra lookups you run in Step 4.\nIf the wording is ambiguous, ask one clarifying question before building.\n\n| Issue type (canonical) | Triggers / signals | Routing tag |\n|---|---|---|\n| `lost_or_delayed` | stuck, late, no movement, \"where is my package\", lost | `queue:tracking-ops` |\n| `unused_label_refund` | \"never shipped\", \"refund this label\", bought-but-unused | `queue:billing-refunds` |\n| `billing_adjustment` | \"charged more than the rate\", surcharge, reweigh, dim-weight, address-correction fee | `queue:billing-adjustments` |\n| `address_exception` | undeliverable, returned to sender, bad/invalid address, address correction | `queue:address-exceptions` |\n| `customs_international` | customs hold, duties/taxes, missing HS code, commercial invoice, international | `queue:customs-intl` |\n| `carrier_account` | \"can't get rates from <carrier>\", connection failed, registration pending | `queue:carrier-onboarding` |\n| `tracking_webhook` | \"tracking updates aren't coming through\", webhook not firing | `queue:integrations` |\n| `other` | anything that doesn't fit above | `queue:general-triage` |\n\n> The routing tags above are a **stable, machine-parseable routing schema**;\n> the receiving team maps each `queue:*` tag to its own ticketing queue, so the\n> exact queue strings are configurable to match your support system.\n> The skill's value is producing a consistent, machine-parseable tag; the exact\n> strings should match your ticketing system.\n\n### Inputs accepted\n\nThe user may start from any **one** of these. Ask which one they have if it is\nambiguous; do not guess an ID type.\n\n| Input | What it anchors |\n|---|---|\n| **Tracking number + carrier** | Drives `GetTrack` directly. Best for delivery/lost-package issues. |\n| **Transaction (label) object ID** | Cleanest anchor: label creation time + tracking number + the rate/shipment link, all derivable. |\n| **Shipment object ID** | Gives from/to addresses, requested `shipment_date`, and rates; tracking number comes from the purchased transaction. |\n\n> **Resolving a tracking number to its label.** First detect the carrier and map\n> it to the Shippo carrier *token* (see the note below), then call `GetTrack`.\n> When the label was purchased through Shippo, the `GetTrack` response carries the\n> transaction `object_id`; use that with `GetTransaction` to pull the label and\n> billing facts. If the label was not bought through Shippo (no transaction comes\n> back), there is nothing to resolve: build the ticket from `GetTrack` plus\n> whatever the user supplied and mark the label fields \"Not available.\"\n> `ListTransactions` has no server-side `tracking_number` filter, so paging it to\n> match by hand is a rarely-useful last resort, not the primary path.\n\n> **Carrier token:** `GetTrack` expects a Shippo carrier *token*, not a display\n> name, e.g. `usps`, `ups`, `fedex`, `dhl_express`, `dhl_ecommerce`,\n> `canada_post`. If you only have a display name (often from a rate's\n> `provider`), map it to the token. If unsure, ask the user for the carrier.\n\n### Shippo MCP tools used\n\nDiscover/confirm with `shippo_list_tools` and `shippo_describe_tool`; execute\nread-only lookups with `shippo_read_execute_tool`. **Everything this skill needs\nis a `read` operation; never call a `write` tool (e.g. `CreateRefund`) from\nthis skill; the ticket only documents and recommends.**\n\nCore reads (all issue types):\n\n- `GetTransaction`: label creation time (`object_created`), `tracking_number`, `status`, `rate` reference, `eta`, `metadata` (order/internal reference)\n- `GetShipment`: `address_from`, `address_to`, requested `shipment_date`, `parcels`, `rates`, `customs_declaration`, `extra` (added services + references), `messages`\n- `GetTrack`: current `tracking_status`, full `tracking_history[]`, `eta`, and (for Shippo-purchased labels) the `transaction` object reference\n\nIssue-type-specific reads (Step 4):\n\n- `GetRate`: purchased `amount`, `currency`, `provider`, `servicelevel`, `estimated_days` (billing)\n- `GetParcel`: declared `length/width/height`, `distance_unit`, `weight`, `mass_unit` (billing)\n- `ListRefunds` / `GetRefund`: existing refund object + `status` (refund)\n- `ValidateAddress` / `ValidateAddressByID`: `is_valid`, `messages`, residential flag (address)\n- `GetCustomsDeclaration` / `GetCustomsItem`: `contents_type`, `incoterm`, `eel_pfc`, per-item `tariff_number` (HS code), `value_amount`, `origin_country` (customs)\n- `ListCarrierAccounts` / `GetCarrierAccount` / `GetCarrierRegistrationStatus`: `active`, registration status (carrier-account)\n- `listWebhooks` / `getWebhook`: `url`, `event`, `active` (webhook)\n\n### Step 2: Resolve the anchor object\n\nAlways work toward having the four core objects: **transaction**, **shipment**,\n**addresses**, and **tracking**. Stop early only when the issue genuinely needs\nnothing more (e.g. a pure tracking-status question with no label on file).\n\n- **Transaction ID** → `GetTransaction`. Read `object_created` (label creation\n  time), `tracking_number`, `tracking_url_provider`, `status`, and the `rate`\n  reference. Inspect for a `shipment` reference to get the shipment ID.\n- **Shipment ID** → `GetShipment`. Read `address_from`, `address_to`,\n  `shipment_date` (the **requested** ship date), `parcels`, `rates`,\n  `customs_declaration`. Find the purchased rate/transaction for the tracking #.\n- **Tracking number + carrier** → map the carrier to its token and call\n  `GetTrack`. For a Shippo-purchased label the response carries the transaction\n  `object_id`; follow it with `GetTransaction` to get the billing/label facts.\n\n### Step 3: Pull the core facts\n\nPull the shipment (`GetShipment`) for `address_from`, `address_to`,\n`shipment_date` if not already loaded, and tracking (`GetTrack` with carrier\ntoken + tracking number) for `tracking_status`, `tracking_history[]`, and `eta`.\n\n> From each address object capture only its `object_id` and coarse geography\n> (`city`, `state`, `zip`, `country`) for the ticket, **not** `name`,\n> `street1`, or `street2` (see PII minimization in guardrails).\n\n- **First carrier scan** = the earliest `tracking_history` event representing\n  physical acceptance by the carrier (the first `TRANSIT`/`DELIVERED`-class scan,\n  or the carrier's \"accepted/picked up\" event). Pre-transit / \"label created\" /\n  \"shipment info received\" pseudo-events do **not** count; call those out\n  separately if present.\n- **Added services and order reference (capture them):** surface the shipment's\n  `extra` block (added services such as `signature_confirmation`, `insurance`,\n  Saturday delivery, QR-code labels) and the customer's own order / internal\n  reference number. That reference can live in two places depending on the\n  integration: the transaction's `metadata` field (the documented home for order\n  numbers) and/or the shipment `extra` reference fields. Capture it from wherever\n  it actually appears, so the agent can tie the ticket back to the order without\n  searching on an order number. The `extra` schema is nuanced and\n  carrier/service-dependent, so **read the actual response fields rather than\n  assuming names**: the `label-purchase` skill documents the common added-service\n  options (signature, insurance, Saturday delivery) and\n  `shippo/references/carrier-guide.md` covers per-carrier availability. Surface\n  only what is actually present; omit the rest.\n- **`messages` noise:** a shipment's `messages` array often carries routine\n  \"carrier doesn't support option\" / \"out of service area\" entries. These are\n  informational. Only surface messages tied to a carrier that actually appears\n  in `rates`.\n- **Read the actual response fields:** do not assume names. If a field is\n  absent, record \"Not available\" rather than inventing a value.\n\n### Step 4: Run the issue-type branch\n\nAfter the core facts, run **only** the lookups for the classified issue type and\nfill the matching section of the output. Skip branches that don't apply.\n\n- **`lost_or_delayed`**: no extra reads; the core timeline carries it. Emphasize\n  \"last scan → now\" and \"overdue vs ETA.\"\n- **`unused_label_refund`**: Was the label ever scanned? Re-check `GetTrack`: if\n  there is a real carrier scan, the label is **used** (not eligible as an unused\n  refund). Say so. Compute **label age** from `object_created` to now. Call\n  `ListRefunds` (and `GetRefund`) to report any existing refund object + its\n  `status`. Do **not** assert a specific eligibility window from memory; state\n  the facts (used/unused, age, existing refund) and let the queue apply policy.\n- **`billing_adjustment`**: `GetRate` for the purchased `amount`/`currency`;\n  `GetParcel` (or shipment `parcels`) for **declared** dims/weight; compare the\n  transaction's charged amount to the quoted rate. Flag the likely cause:\n  dimensional-weight reweigh (declared vs billed dims), address-correction\n  surcharge, or service upgrade. Report declared-vs-billed as the core evidence.\n  Note: the reweigh/adjustment amount and the carrier's *billed* dims may not be\n  exposed by these read ops; if so, record \"Not available\" rather than inferring.\n- **`address_exception`**: run `ValidateAddress`/`ValidateAddressByID` on\n  `address_to`; report `is_valid`, any validation `messages`, and the\n  residential/commercial flag. Note whether validation was bypassed at purchase.\n- **`customs_international`**: pull `GetCustomsDeclaration` + each\n  `GetCustomsItem`. Check completeness: `contents_type`, `incoterm`,\n  `eel_pfc`/AES exemption, and per item a `tariff_number` (HS code),\n  `value_amount`, and `origin_country`. Flag missing HS codes / values, the\n  usual cause of customs holds.\n- **`carrier_account`**: `ListCarrierAccounts`, then `GetCarrierAccount` /\n  `GetCarrierRegistrationStatus` for the relevant carrier. Report `active` and\n  registration status; an incomplete registration is the usual \"no rates\" cause.\n- **`tracking_webhook`**: `listWebhooks` + `getWebhook`. Report whether an\n  `active` webhook exists for the relevant `track_updated`/tracking event and the\n  configured `url`.\n\n### Timeline to compute\n\nThese derived metrics pre-diagnose the issue so support doesn't have to:\n\n- **Label created → first carrier scan**: how long the label sat before entering\n  the network. A large gap is the classic \"bought but never shipped\" signature.\n- **Requested `shipment_date` → first carrier scan**: picked up on/near intent?\n- **First scan → last scan**: total time in transit so far.\n- **Last scan → now**: days of silence; a long gap signals a stalled/lost parcel.\n- **ETA vs. now**: is it overdue?\n\nState each as an absolute date/time **and** a duration (e.g. \"Label created\n2026-06-01 14:02 UTC; first scan 2026-06-05 09:11 UTC, a 3d 19h gap\"). Use UTC\nand label it. In the JSON block, also emit each gap in whole hours.\n\n### Output\n\nEmit **both** blocks below, each as its own fenced block. Replace every `<...>`\nplaceholder; use \"Not available\" for anything you could not retrieve; never\ninvent values.\n\n**Provenance (required).** Both blocks carry a generation stamp so support can\ntell at a glance that the ticket was machine-assembled, and so ticket quality\ncan be tracked over time. Stamp:\n\n- the **skill name** (`shippo-support-ticket`),\n- the **source** (`Shippo MCP`),\n- the **generation time in UTC** (ISO 8601).\n\nNever alter or omit the stamp, and never present an auto-generated ticket as if\nit were hand-written.\n\nAfter the blocks, add a short plain-language **triage summary**\n(1-3 sentences) naming the most likely problem based on the classification +\ntimeline, and list any data you could not retrieve.\n\n#### Block A: Human ticket (copy-paste)\n\n```\nSubject: [<issue_type>] <one-line summary>, tracking <tracking_number>\n\nROUTING\n  Issue type:      <canonical issue type>\n  Routing tag:     <queue:...>\n  Confidence:      <high | medium | low; note if classified from sparse info>\n\nISSUE\n  Reported by:     <customer name / email, if known>\n  Summary:         <2-3 sentence description in plain language>\n\nSHIPMENT\n  Shipment ID:     <shipment object_id>\n  Transaction ID:  <transaction object_id>\n  Carrier:         <carrier display name> (<carrier token>)\n  Service level:   <servicelevel name>\n  Tracking #:      <tracking_number>\n  Tracking URL:    <tracking_url_provider>\n  Parcel:          <declared dimensions + weight, if available>\n  References:      <order/internal ref from transaction metadata or shipment extra, else \"none\">\n  Added services:  <signature / insurance / QR code / etc. from extra, else \"none\">\n\nADDRESSES (no street-level PII; run GetAddress on an ID for full details)\n  From address ID: <address_from object_id>\n  From region:     <city> <state> <zip> <country>\n  To address ID:   <address\n\nArchive v1.4.2: 7 files, 34957 bytes\n\nFiles: references/carrier-guide.md (5951b), references/csv-format.md (6234b), references/customs-guide.md (9582b), references/tool-reference.md (15766b), skill-card.md (3395b), SKILL.md (54034b), _meta.json (125b)\n\nArchive v1.4.1: 6 files, 33374 bytes\n\nFiles: references/carrier-guide.md (5951b), references/csv-format.md (6234b), references/customs-guide.md (9582b), references/tool-reference.md (15766b), SKILL.md (54041b), _meta.json (125b)\n\nArchive v1.4.0: 7 files, 34783 bytes\n\nFiles: references/carrier-guide.md (5951b), references/csv-format.md (6234b), references/customs-guide.md (9582b), references/tool-reference.md (14843b), skill-card.md (3757b), SKILL.md (53909b), _meta.json (125b)\n\nArchive v1.3.4: 7 files, 27622 bytes\n\nFiles: references/carrier-guide.md (5951b), references/csv-format.md (6234b), references/customs-guide.md (9582b), references/tool-reference.md (14843b), skill-card.md (3583b), SKILL.md (34429b), _meta.json (125b)\n\nArchive v1.3.3: 7 files, 27468 bytes\n\nFiles: references/carrier-guide.md (5951b), references/csv-format.md (6234b), references/customs-guide.md (9582b), references/tool-reference.md (14843b), skill-card.md (3223b), SKILL.md (34416b), _meta.json (125b)\n\nArchive v1.3.2: 7 files, 27814 bytes\n\nFiles: references/carrier-guide.md (5951b), references/csv-format.md (6234b), references/customs-guide.md (9582b), references/tool-reference.md (14843b), skill-card.md (3243b), SKILL.md (35646b), _meta.json (125b)\n\nArchive v1.3.1: 7 files, 27852 bytes\n\nFiles: references/carrier-guide.md (5951b), references/csv-format.md (6234b), references/customs-guide.md (9582b), references/tool-reference.md (14843b), skill-card.md (3507b), SKILL.md (35382b), _meta.json (125b)\n\nArchive v1.1.3: 7 files, 29379 bytes\n\nFiles: references/carrier-guide.md (5954b), references/csv-format.md (6237b), references/customs-guide.md (9590b), references/tool-reference.md (15658b), skill-card.md (3148b), SKILL.md (40060b), _meta.json (125b)","readmeExcerpt":"Skill: Shippo Owner: shippo Summary: A shipping and logistics skill for Shippo. Get multi-carrier rates (USPS, UPS, FedEx, DHL, 30+), buy domestic and international labels with customs, validate... Tags: latest:1.4.4 Version history: v1.4.4 | 2026-07-15T21:42:15.167Z | user Release 1.4.4: hosted OAuth MCP (mcp.shippo.com), 4-tool meta-API with PascalCase operation names. v1.4.3 | 2026-07-15T17:24:21.774Z | user Relea","codeSnippets":[],"executableExamples":[{"language":"json","snippet":"{\n  \"mcpServers\": {\n    \"shippo\": {\n      \"type\": \"http\",\n      \"url\": \"https://mcp.shippo.com\"\n    }\n  }\n}"},{"language":"text","snippet":"Subject: [<issue_type>] <one-line summary>, tracking <tracking_number>\n\nROUTING\n  Issue type:      <canonical issue type>\n  Routing tag:     <queue:...>\n  Confidence:      <high | medium | low; note if classified from sparse info>\n\nISSUE\n  Reported by:     <customer name / email, if known>\n  Summary:         <2-3 sentence description in plain language>\n\nSHIPMENT\n  Shipment ID:     <shipment object_id>\n  Transaction ID:  <transaction object_id>\n  Carrier:         <carrier display name> (<carrier token>)\n  Service level:   <servicelevel name>\n  Tracking #:      <tracking_number>\n  Tracking URL:    <tracking_url_provider>\n  Parcel:          <declared dimensions + weight, if available>\n  References:      <order/internal ref from transaction metadata or shipment extra, else \"none\">\n  Added services:  <signature / insurance / QR code / etc. from extra, else \"none\">\n\nADDRESSES (no street-level PII; run GetAddress on an ID for full details)\n  From address ID: <address_from object_id>\n  From region:     <city> <state> <zip> <country>\n  To address ID:   <address_to object_id>\n  To region:       <city> <state> <zip> <country>\n\nTIMELINE (all times UTC)\n  Label created:           <object_created>\n  Requested ship date:     <shipment_date>\n  First carrier scan:      <status_date> @ <location>   (<status>)\n  Last/most recent scan:   <status_date> @ <location>   (<status>)\n  Curren\n\nFile v1.4.4:_meta.json\n\n{\n  \"ownerId\": \"kn770w02ykf4ca0bj09cfcw2n1835r4y\",\n  \"slug\": \"shippo\",\n  \"version\": \"1.4.4\",\n  \"publishedAt\": 1784151735167\n}\n\nFile v1.4.4:references/carrier-guide.md\n\n<!--\n  ⚠️  DO NOT EDIT, auto-generated from skills/shippo/references/carrier-guide.md by scripts/build-clawhub-bundle.js\n  Edits here will be overwritten on the next sync.\n  To change this content, edit the canonical source and re-run the sync script.\n-->\n\n# Carrier Guide\n\nPer-carrier nuances, requirements, and gotchas for the major carriers supported by Shippo.\n\n---\n\n## USPS\n\n### Setup\n- A **managed USPS account**"},{"language":"text","snippet":"Notes about the sample:\n- Row 1 (ORD-001): Domestic US shipment with minimal optional fields.\n- Row 2 (ORD-002): Domestic US shipment with recipient email and declared value.\n- Row 3 (ORD-003): International shipment (US to FR). The `state` field is empty for Paris, which is acceptable for countries that do not use state/province. This row triggers customs declaration creation.\n\n---\n\n## Parsing and Validation Rules\n\n### Column Matching\n- Match columns by header name (case-insensitive). Trim whitespace from headers.\n- Extra columns not in the spec should be ignored.\n- If required columns are missing from the header row, report the missing columns and stop processing.\n\n### Row Validation\n- Skip empty rows silently.\n- For each row, check that all required columns have non-empty values.\n- Collect invalid rows (with row number and the specific missing/invalid fields) and report them to the user before proceeding.\n- Do not include invalid rows in the batch.\n\n### Data Type Handling\n- `package_length`, `package_width`, `package_height`, and `package_weight` should be read as numbers from the CSV, then passed as **strings** to the Shippo API (e.g., CSV value `12` becomes API value `\"12\"`).\n- `country` fields must be 2-letter ISO codes. If a row has a full country name (e.g., \"United States\"), attempt to map it or flag the row for user correction.\n- `weight_unit` must be one of: `lb`, `kg`, `g`, `oz`.\n- `distance_unit` must be one of: `in`, `cm`, `ft`, `m`, `mm`, `yd`.\n\n### International Detection\n- Compare `sender_country` and `recipient_country` for each row.\n- If they differ, the row is international and requires a customs declaration.\n- For international rows, `sender_email` and `sender_phone` are mandatory (carriers require them for customs). Flag any international row missing these fields.\n\n### Encoding\n- Expect UTF-8 encoding. If parsing fails, ask the user to verify the file is UTF-8 encoded.\n- Handle common CSV dialects: comma-delimited, with or without quoted fields"},{"language":"text","snippet":"### HS / Tariff Codes\n\nHS (Harmonized System) codes classify goods for customs. They are typically 6 digits internationally, extended to 8-10 digits for country-specific tariff schedules (HTS in the US).\n\n**USPS requires 6-digit HS codes on ALL international commercial shipments (as of September 2025). FedEx and DHL strongly recommend them. Shipments without HS codes risk delays or rejection.**\n\n- If the user does not know the HS code, ask them to describe the product. Common codes:\n  - Clothing: 6109 (t-shirts), 6110 (sweaters), 6204 (women's suits/trousers)\n  - Electronics: 8471 (computers), 8517 (phones), 8528 (monitors)\n  - Books: 4901\n  - Toys: 9503\n  - Cosmetics: 3304\n- If unsure, the user should consult their country's tariff schedule or a customs broker.\n\n---\n\n## Step 2: Create the Customs Declaration\n\nCall `CreateCustomsDeclaration` with the item object_ids from step 1.\n\n### Required Fields\n\n| Field | Type | Description |\n|---|---|---|\n| `certify` | boolean | Must be `true`. Certifies the information is accurate. |\n| `certify_signer` | string | Full name of the person certifying the declaration. |\n| `contents_type` | string | Type of shipment contents. See values below. |\n| `non_delivery_option` | string | What to do if the package is undeliverable. See values below. |\n| `items` | array | Array of customs item object_ids from step 1. |\n\n### Optional Fields\n\n| Field | Type | Description |\n|---|---|---|\n| `contents_explanation` | string | Required if `contents_type` is `OTHER`. Free-text explanation. |\n| `exporter_reference` | string | Exporter reference number |\n| `importer_reference` | string | Importer reference number |\n| `invoice` | string | Invoice number |\n| `license` | string | Export license number |\n| `certificate` | string | Certificate number |\n| `notes` | string | Additional notes |\n| `eel_pfc` | string | Electronic Export License or Post Office Filing Citation. Values: `NOEEI_30_37_a`, `NOEEI_30_37_h`, `NOEEI_30_36`, `AES_ITN`. **Strongly recomm"},{"language":"text","snippet":"---\n\n### Commercial Invoice\n\nShippo automatically generates 3 copies of the commercial invoice for international shipments. The invoice URL is returned in the transaction response after label purchase.\n\n---\n\n## Step 3: Attach to Shipment\n\nWhen calling `CreateShipment`, include the `customs_declaration` field set to the object_id returned from step 2:"},{"language":"text","snippet":"---\n\n## Sender Address Requirements for International Shipments\n\nThe sender address (`address_from`) must include:\n- `email` -- required by carriers for customs processing\n- `phone` -- required by carriers for customs processing\n\nIf the user's sender address is missing these fields, ask before proceeding.\n\n---\n\n## Batch Processing with Customs\n\nWhen processing a CSV batch that includes international rows:\n\n1. Identify international rows by comparing `sender_country` and `recipient_country`.\n2. For each international row, create customs items from the row data (use `package_description` for the item description, `package_weight` for net_weight, `declared_value` for value_amount).\n3. Create a customs declaration per international row.\n4. Include the declaration object_id in the batch shipment object's `customs_declaration` field.\n5. Ask the user once for shared values: `contents_type`, `non_delivery_option`, and `certify_signer`. Reuse across all international rows unless the CSV provides per-row values.\n\n---\n\n## Common Issues\n\n### Missing HS/Tariff Code\nMost carriers require tariff_number for customs clearance. If omitted, the shipment may be delayed or rejected at customs. Always ask the user for this value.\n\n### Value Declaration\nUnder-declaring item values is illegal and can result in fines or seizure. Ensure `value_amount` reflects the actual market value of the goods.\n\n### EEL/PFC for US Exports\nFor shipments to Canada, use `NOEEI_30_36` regardless of value. For non-Canada destinations: shipments under $2,500 use `NOEEI_30_37_a` (most common exemption); shipments over $2,500 require an AES/ITN filing -- set `eel_pfc` to the ITN number.\n\n### Restricted and Prohibited Items\nThe Shippo API does not enforce import/export restrictions. The user is responsible for ensuring their goods are legal to ship to the destination country. If the user mentions shipping batteries, liquids, food, plants, or weapons, advise them to check destination country import regulations.\n\n--"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: shippo\ndescription: \"A shipping and logistics skill for Shippo. Get multi-carrier rates (USPS, UPS, FedEx, DHL, 30+), buy domestic and international labels with customs, validate addresses, track packages with webhooks, and run bulk CSV batches, plus cost analysis, integration routing, and SDK-upgrade help. Runs through Shippo's hosted MCP with per-user OAuth (sign in once, nothing to copy or store). Uses Shippo's discounted carrier rates.\"\nversion: 1.4.4\nlicense: MIT\nmetadata:\n  openclaw:\n    emoji: \"📦\"\n    homepage: https://github.com/goshippo/ai\n---\n\n# Shippo Shipping Skill\n\n## Setup\n\n**MCP server:** Shippo's hosted MCP at `https://mcp.shippo.com`, with per-user Shippo OAuth. You authorize once through Shippo on first use, with nothing to copy or configure, and the client refreshes the token automatically.\n\nPoint your MCP client at the hosted server:\n\n```json\n{\n  \"mcpServers\": {\n    \"shippo\": {\n      \"type\": \"http\",\n      \"url\": \"https://mcp.shippo.com\"\n    }\n  }\n}\n```\n\nOn first use, your client runs the Shippo OAuth sign-in (in OpenClaw, `openclaw mcp login shippo`; in Claude Code, `/mcp`). No local Node process and nothing to store.\n\n**Prerequisites:** A Shippo account and at least one carrier account (Shippo provides managed accounts for USPS, UPS, FedEx, DHL Express by default). See `references/tool-reference.md` for the full tool catalog.\n\n**Purchases are live:** label purchases charge the authorized Shippo account for real. Confirm carrier, service, and cost with the user before any purchase.\n\n**Response envelope:** The MCP wraps most API responses in a Speakeasy envelope shaped like `{\"ContentType\": \"application/json\", \"StatusCode\": <code>, \"RawResponse\": {}, \"<PayloadName>\": {...actual response...}}`. The payload field is named after the response schema on success (e.g. `ParsedAddress`, `AddressPaginatedList`, `AddressValidationResultV2`, `AddressWithMetadataResponse`, `Shipment`, `CarrierAccountPaginatedList`) and after the HTTP status code on some errors (e.g. `fourHundredAndNineApplicationJsonObject` for a 409, the body may be `{}`). To extract the payload, find the field whose key is not `ContentType`, `StatusCode`, or `RawResponse`, and branch on `StatusCode` for success vs error.\n\n**Non-envelope errors:** Some failures bypass the envelope entirely and surface as an MCP-level error instead, the tool response has `isError: true` with a single text block containing a plaintext message like `Unexpected API response status or content-type: Status 404 Content-Type application/json Body: {\"detail\":\"Not found.\"}`. Argument-validation failures come back as JSON-RPC error code `-32602`. Handle both paths when reporting errors to the user.\n\n---\n\n## Best Practices\n\nLatest Shippo API version: **2018-02-08**. Send via the `Shippo-API-Version` header.\n\n### Using the Shippo MCP\n\nThe hosted Shippo MCP at `https://mcp.shippo.com` exposes exactly **4 tools** (a meta-API), not the underlying operations directly:\n\n- `shippo_list_tools`: di"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn770w02ykf4ca0bj09cfcw2n1835r4y\",\n  \"slug\": \"shippo\",\n  \"version\": \"1.4.4\",\n  \"publishedAt\": 1784151735167\n}"},{"path":"references/carrier-guide.md","content":"<!--\n  ⚠️  DO NOT EDIT, auto-generated from skills/shippo/references/carrier-guide.md by scripts/build-clawhub-bundle.js\n  Edits here will be overwritten on the next sync.\n  To change this content, edit the canonical source and re-run the sync script.\n-->\n\n# Carrier Guide\n\nPer-carrier nuances, requirements, and gotchas for the major carriers supported by Shippo.\n\n---\n\n## USPS\n\n### Setup\n- A **managed USPS account** is available by default on all Shippo accounts. No additional configuration needed.\n- Carrier token: `usps`\n\n### Key Details\n- **HS codes required** for ALL international commercial shipments as of September 2025. Minimum 6 digits.\n- **No DDP support.** USPS always ships DDU (recipient pays duties/taxes). Do not set `incoterm` to `DDP` for USPS.\n- **Flat-rate options** available via parcel templates (e.g., `USPS_FlatRateEnvelope`, `USPS_SmallFlatRateBox`, `USPS_MediumFlatRateBox1`, `USPS_LargeFlatRateBox`). When using flat rate, parcel dimensions are ignored -- only weight matters for eligibility.\n- **EEL/PFC required** for international shipments. USPS will warn/reject if `eel_pfc` is missing on customs declarations.\n- **Tracking number format:** 20-22 digits, or starts with `9` followed by 20+ digits (e.g., `9400111899223100001234`).\n- **Max weight:** 70 lbs domestic, 66 lbs international (varies by destination).\n- **Signature confirmation:** Available via `extra.signature_confirmation` (`STANDARD`, `ADULT`). Not available on all service levels.\n\n### Common Service Levels\n| Token | Name |\n|---|---|\n| `usps_priority` | Priority Mail |\n| `usps_priority_express` | Priority Mail Express |\n| `usps_ground_advantage` | Ground Advantage |\n| `usps_first` | First-Class Mail |\n| `usps_media_mail` | Media Mail |\n\n---\n\n## UPS\n\n### Setup\n- **Requires Terms & Conditions acceptance** via the Shippo web app before API use. If the user gets auth errors for UPS, direct them to accept T&C in the Shippo dashboard.\n- Carrier token: `ups`\n\n### Key Details\n- **Supports DDP** (Delivered Duty Paid). Set `incoterm` to `DDP` on the customs declaration.\n- **Signature confirmation options:** `STANDARD`, `ADULT`, `CERTIFIED`, `INDIRECT`\n- **Tracking number format:** Starts with `1Z` followed by 16 alphanumeric characters (e.g., `1Z999AA10123456784`).\n- **Residential surcharge** applies automatically when the destination is residential. Shippo flags residential addresses during validation.\n- **Saturday delivery** available for select services via `extra.saturday_delivery = true`.\n- **Max weight:** 150 lbs per package.\n\n### Common Service Levels\n| Token | Name |\n|---|---|\n| `ups_ground` | UPS Ground |\n| `ups_next_day_air` | UPS Next Day Air |\n| `ups_2nd_day_air` | UPS 2nd Day Air |\n| `ups_3_day_select` | UPS 3 Day Select |\n| `ups_express` | UPS Worldwide Express |\n| `ups_expedited` | UPS Worldwide Expedited |\n\n---\n\n## FedEx\n\n### Setup\n- Carrier token: `fedex`\n- Requires a FedEx account connected via the Shippo dashboard.\n\n### Key Details\n- **Supports DDP** via `duti"},{"path":"references/csv-format.md","content":"<!--\n  ⚠️  DO NOT EDIT, auto-generated from skills/shippo/references/csv-format.md by scripts/build-clawhub-bundle.js\n  Edits here will be overwritten on the next sync.\n  To change this content, edit the canonical source and re-run the sync script.\n-->\n\n# CSV Batch Format Specification\n\nThis document defines the CSV column format for batch shipment processing.\n\n---\n\n## Required Columns\n\nEvery row must have non-empty values for all required columns. Rows missing required values should be skipped and reported to the user.\n\n| Column | Description | Example |\n|---|---|---|\n| `shipment_id` | Unique identifier for the row (user's reference) | `ORD-001` |\n| `sender_name` | Sender full name | `Jane Smith` |\n| `sender_street1` | Sender street address | `731 Market St` |\n| `sender_city` | Sender city | `San Francisco` |\n| `sender_state` | Sender state/province | `CA` |\n| `sender_zip` | Sender postal code | `94103` |\n| `sender_country` | Sender country (ISO 3166-1 alpha-2) | `US` |\n| `sender_email` | Sender email (column required; value may be empty for domestic rows but must be non-empty for international) | `jane@example.com` |\n| `sender_phone` | Sender phone (column required; value may be empty for domestic rows but must be non-empty for international) | `+1-555-123-4567` |\n| `recipient_name` | Recipient full name | `John Doe` |\n| `recipient_street1` | Recipient street address | `456 Oak Ave` |\n| `recipient_city` | Recipient city | `Portland` |\n| `recipient_state` | Recipient state/province | `OR` |\n| `recipient_zip` | Recipient postal code | `97201` |\n| `recipient_country` | Recipient country (ISO 3166-1 alpha-2) | `US` |\n| `package_length` | Parcel length (as a number) | `12` |\n| `package_width` | Parcel width (as a number) | `8` |\n| `package_height` | Parcel height (as a number) | `6` |\n| `package_weight` | Parcel weight (as a number) | `2.5` |\n| `weight_unit` | Mass unit: `lb`, `kg`, `g`, or `oz` | `lb` |\n| `distance_unit` | Dimension unit: `in`, `cm`, `ft`, `m`, `mm`, or `yd` | `in` |\n\n---\n\n## Optional Columns\n\nWhen these columns are absent or empty for a row, omit the corresponding fields from the API call. Do not send empty strings.\n\n| Column | Description | Example |\n|---|---|---|\n| `recipient_email` | Recipient email address | `john@example.com` |\n| `recipient_phone` | Recipient phone number | `+1-555-987-6543` |\n| `sender_street2` | Sender address line 2 (apt, suite) | `Suite 200` |\n| `recipient_street2` | Recipient address line 2 (apt, suite) | `Apt 4B` |\n| `package_description` | Item description (used for customs) | `Cotton t-shirts` |\n| `declared_value` | Declared value (used for customs/insurance) | `45.00` |\n| `customs_contents_type` | Customs contents type per row | `MERCHANDISE` |\n| `metadata` | Free-form reference or order number | `PO-20240115` |\n\n---\n\n## Sample CSV\n\n```csv\nshipment_id,sender_name,sender_street1,sender_city,sender_state,sender_zip,sender_country,sender_email,sender_phone,recipient_name,recipient_street1,recipient_city"},{"path":"references/customs-guide.md","content":"<!--\n  ⚠️  DO NOT EDIT, auto-generated from skills/shippo/references/customs-guide.md by scripts/build-clawhub-bundle.js\n  Edits here will be overwritten on the next sync.\n  To change this content, edit the canonical source and re-run the sync script.\n-->\n\n# Customs Declaration Guide\n\nThis document covers creating customs declarations for international shipments. A customs declaration is required whenever sender and recipient are in different countries.\n\n---\n\n## Overview: Two-Step Process\n\nInternational labels require customs documentation before the shipment can be created:\n\n1. **Create customs items** -- one per distinct product in the shipment.\n2. **Create the customs declaration** -- references the items and contains certifications.\n3. **Attach the declaration** to the shipment via the `customs_declaration` field.\n\n---\n\n## Step 1: Create Customs Items\n\nCall `CreateCustomsItem` once per distinct item type in the shipment.\n\n### Required Fields\n\n| Field | Type | Description |\n|---|---|---|\n| `description` | string | Plain-language description of the item (e.g., \"cotton t-shirt\") |\n| `quantity` | integer | Number of units |\n| `net_weight` | string | Weight per unit (as a string, e.g., \"0.5\") |\n| `mass_unit` | string | One of: `g`, `kg`, `lb`, `oz` |\n| `value_amount` | string | Declared monetary value per unit (as a string, e.g., \"25.00\") |\n| `value_currency` | string | ISO 4217 currency code (e.g., `USD`, `EUR`, `GBP`) |\n| `origin_country` | string | ISO 3166-1 alpha-2 country code where the item was manufactured (e.g., `US`, `CN`) |\n\n### Optional Fields\n\n| Field | Type | Description |\n|---|---|---|\n| `tariff_number` | string | HS/HTS harmonized tariff code (6-10 digits). Required by most carriers. Ask the user if not provided. |\n| `sku_code` | string | SKU or product code |\n| ` eccn_ear99` | string | Export Control Classification Number |\n| `metadata` | string | Free-form metadata |\n\n### Example\n\n```json\n{\n  \"description\": \"Cotton t-shirt, blue, size M\",\n  \"quantity\": 3,\n  \"net_weight\": \"0.3\",\n  \"mass_unit\": \"lb\",\n  \"value_amount\": \"15.00\",\n  \"value_currency\": \"USD\",\n  \"origin_country\": \"US\",\n  \"tariff_number\": \"6109100012\"\n}\n```\n\n### HS / Tariff Codes\n\nHS (Harmonized System) codes classify goods for customs. They are typically 6 digits internationally, extended to 8-10 digits for country-specific tariff schedules (HTS in the US).\n\n**USPS requires 6-digit HS codes on ALL international commercial shipments (as of September 2025). FedEx and DHL strongly recommend them. Shipments without HS codes risk delays or rejection.**\n\n- If the user does not know the HS code, ask them to describe the product. Common codes:\n  - Clothing: 6109 (t-shirts), 6110 (sweaters), 6204 (women's suits/trousers)\n  - Electronics: 8471 (computers), 8517 (phones), 8528 (monitors)\n  - Books: 4901\n  - Toys: 9503\n  - Cosmetics: 3304\n- If unsure, the user should consult their country's tariff schedule or a customs broker.\n\n---\n\n## Step 2: Create the Customs Declaration\n\nCall `Cr"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"A shipping and logistics skill for Shippo. Get multi-carrier rates (USPS, UPS, FedEx, DHL, 30+), buy domestic and international labels with customs, validate... Skill: Shippo Owner: shippo Summary: A shipping and logistics skill for Shippo. Get multi-carrier rates (USPS, UPS, FedEx, DHL, 30+), buy domestic and international labels with customs, validate... Tags: latest:1.4.4 Version history: v1.4.4 | 2026-07-15T21:42:15.167Z | user Release 1.4.4: hosted OAuth MCP (mcp.shippo.com), 4-tool meta-API with PascalCase operation names. v1.4.3 | 2026-07-15T17:24:21.774Z | user Relea","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1799,"uniquenessScore":46,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-09T17:24:00.916Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-09T17:24:00.916Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-09T21:52:52.907Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}