← Ship24 Tracking APICONTENT HISTORYWHAT CHANGED · RULE-BASED ANALYSIS
Update to Ship24 Tracking API
Snapshot Sep 30, 2026 · 23:16 UTC · version 1.0.0
Collection source: not recorded for this historical snapshot.
First saved snapshot
No earlier snapshot is available to establish a change.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"description": "Use the official Ship24 npm package in TypeScript or Node.js. Triggers include \"Ship24 Node SDK\", \"ship24 npm package\", \"use ship24\", \"ship24.trackers.create\", \"import ship24\", \"new Ship24\", or asking to replace hand-written fetch or axios calls to Ship24 in Node/TypeScript. Routes deeper Node SDK questions away from generic integration guidance. Covers installation, constructor configuration, all 12 endpoint methods, error handling, per-call vs per-shipment differences, pagination, and when retries are needed.",
"included_files": [
{
"relative_path": "agents/openai.yaml",
"size_in_bytes": 279
},
{
"relative_path": "assets/icon-large.png",
"size_in_bytes": 11655
},
{
"relative_path": "assets/icon-small.png",
"size_in_bytes": 3297
},
{
"relative_path": "references/endpoints.md",
"size_in_bytes": 25240
},
{
"relative_path": "references/sdk-methods.md",
"size_in_bytes": 3193
}
],
"name": "ship24-node-sdk",
"skill_md_contents": "---\nname: ship24-node-sdk\ndescription: Use the official Ship24 npm package in TypeScript or Node.js. Triggers include \"Ship24 Node SDK\", \"ship24 npm package\", \"use ship24\", \"ship24.trackers.create\", \"import ship24\", \"new Ship24\", or asking to replace hand-written fetch or axios calls to Ship24 in Node/TypeScript. Routes deeper Node SDK questions away from generic integration guidance. Covers installation, constructor configuration, all 12 endpoint methods, error handling, per-call vs per-shipment differences, pagination, and when retries are needed.\nlicense: MIT\n---\n\n# Ship24 Node SDK\n\nOfficial SDK for the Ship24 Tracking API. Full TypeScript support, zero runtime dependencies, native `fetch`.\n\n## Install\n\n```bash\nnpm install ship24\n```\n\nThe SDK README lists Node 18+, Bun, Deno, Cloudflare Workers and modern browsers as supported runtimes; CI tests Node 20/22/24, Bun and Deno. Never ship a production API key to a browser: keep the SDK on the server. It ships ESM and CommonJS:\n\n```ts\n// ESM\nimport { Ship24 } from 'ship24';\n// CommonJS\nconst { Ship24 } = require('ship24');\n```\n\n## Quickstart\n\n```ts\nimport { Ship24 } from 'ship24';\n\nconst apiKey = process.env.SHIP24_API_KEY;\nif (!apiKey) throw new Error('SHIP24_API_KEY is not set');\n\nconst ship24 = new Ship24({ apiKey });\nconst tracker = await ship24.trackers.create({ trackingNumber: '1234567890' });\nconsole.log(tracker.trackerId);\n```\n\n## Configuration\n\nPass a `Ship24Config` object to the constructor. Only `apiKey` is required:\n\n```ts\ntype Ship24Config = {\n apiKey: string; // required; the SDK does not read the environment itself\n baseUrl?: string; // default 'https://api.ship24.com'; the SDK appends /public/v1\n timeoutMs?: number; // see Timeouts\n fetch?: typeof fetch; // custom fetch implementation\n headers?: Record<string, string>; // extra default headers; Authorization is always overwritten\n};\n```\n\n### Timeouts\n\nDefaults are 10 seconds per request and 60 seconds for the synchronous `ship24.trackers.track` and `ship24.perCall.track` (Ship24 queries couriers synchronously, up to about a minute). In SDK 1.0.0 the constructor's `timeoutMs` is stored but never read by the transport, so set the timeout per call:\n\n```ts\nconst result = await ship24.trackers.track(\n { trackingNumber: '1234567890' },\n { timeoutMs: 120_000 }\n);\n```\n\nPer-call options are accepted by every method: `{ timeoutMs?, signal?, headers? }`. Tracker-id methods also accept `{ searchBy: 'trackerId' | 'clientTrackerId' }` to resolve the id as your own client id.\n\n## Methods\n\nSee `references/sdk-methods.md` for the full table of 12 methods, their endpoints, signatures and when each is used, and `references/endpoints.md` for the endpoint paths, response codes and rate limits generated from the OpenAPI spec. Highlights:\n\n### Per-shipment (default, recommended)\n\nCreate trackers, receive updates via webhooks or polling, manage courier codes.\n\n```ts\nawait ship24.trackers.create({ trackingNumber, courierCode?, clientTrackerId? });\nawait ship24.trackers.track({ trackingNumber }); // sync, ~60s\nawait ship24.trackers.bulkCreate([{ trackingNumber }, ...]); // up to 100\nawait ship24.trackers.list({ page?, limit?, sort? });\nawait ship24.trackers.get(trackerId);\nawait ship24.trackers.getResults(trackerId);\nawait ship24.trackers.update(trackerId, { isSubscribed? });\nawait ship24.trackers.resendWebhooks(trackerId);\nawait ship24.couriers.list(); // rate limited 1 req/s\n```\n\n### Per-call (separate subscription)\n\nSynchronous one-off lookups; no persistent tracker.\n\n```ts\nawait ship24.perCall.track({ trackingNumber, destinationCountryCode? });\n```\n\nReturns `PerCallTracking[]`. Note there is **no `.tracker`** field, unlike `trackers.track`.\n\n## Error handling\n\nAll errors extend `Ship24Error`. Catch by class, then optionally branch on `code`:\n\n```ts\nimport {\n Ship24Error,\n NotFoundError,\n RateLimitError,\n SubscriptionError,\n AuthenticationError,\n ValidationError,\n ConflictError,\n QuotaError,\n ServerError,\n Ship24ConnectionError,\n Ship24TimeoutError,\n} from 'ship24';\n\ntry {\n const tracker = await ship24.trackers.get('unknown-id');\n} catch (err) {\n if (err instanceof NotFoundError) {\n console.error(`Tracker not found: ${err.code}`);\n // Handle 404\n } else if (err instanceof RateLimitError) {\n console.error(`Rate limited; wait ${err.retryAfter}s`, err.rateLimit);\n // err.rateLimit has { limit, remaining, reset }\n } else if (err instanceof SubscriptionError) {\n console.error(err.message); // Actionable: per-call subscription required?\n } else if (err instanceof ValidationError) {\n console.error(`Invalid request: ${err.code}`);\n // Handle 400 validation\n } else if (err instanceof ConflictError) {\n console.error(`Conflict: ${err.code}`); // tracker_conflict or request_conflict\n } else if (err instanceof QuotaError) {\n console.error(`Quota exceeded: ${err.code}`);\n } else if (err instanceof AuthenticationError) {\n console.error(`Auth failed: ${err.code}`); // Missing/invalid key or header\n } else if (err instanceof ServerError) {\n console.error(`Server error (5xx): ${err.httpStatus}`);\n } else if (err instanceof Ship24TimeoutError) {\n console.error(`Timed out after ${err.timeoutMs}ms`);\n } else if (err instanceof Ship24ConnectionError) {\n console.error(`Network error: ${err.message}`);\n } else if (err instanceof Ship24Error) {\n console.error(`Unexpected error: ${err.message}`);\n } else {\n throw err; // Not a Ship24 error\n }\n}\n```\n\n`ValidationError` is an alias of `InvalidRequestError`, the name that appears in `err.name`. All `Ship24APIError` subclasses carry `httpStatus`, `code`, `errors[]`, `requestId` (from the `x-request-id` header), and `body` (raw response). Log the `requestId` when contacting support.\n\n## Per-call vs per-shipment\n\nShip24 sells two products. The SDK keeps them explicit:\n\n| Aspect | Per-shipment | Per-call |\n| --- | --- | --- |\n| Namespace | `ship24.trackers.*` | `ship24.perCall.*` |\n| Usage counted | Shipments (trackers) | API calls |\n| Tracker persists | Yes | No |\n| Return type | `Tracking` (has `.tracker`) | `PerCallTracking` (no `.tracker`) |\n| Subscription | Standard per-shipment plan | Separate \"Per-call\" subscription |\n\nCalling `ship24.perCall.track` without an active per-call subscription throws `SubscriptionError` (HTTP 422) with a message pointing to the dashboard.\n\n## Bulk creation\n\n`trackers.bulkCreate` accepts 1 to 100 items and **never throws on the envelope**: HTTP 201/207/400/403 all resolve to a `BulkCreateResult`. Inspect the result's `status` and per-item `errors`:\n\n```ts\nconst result = await ship24.trackers.bulkCreate([\n { trackingNumber: 'A' },\n { trackingNumber: 'B' },\n]);\n\nif (result.status === 'success') {\n console.log('All created');\n} else if (result.status === 'partial') {\n // Some failed; iterate result.data to find which\n for (const item of result.data ?? []) {\n if (item.itemStatus === 'error') {\n console.error(item.inputData.trackingNumber, item.errors);\n }\n }\n}\n```\n\nThe SDK documents that HTTP 201, 207, 400 and 403 resolve to a `BulkCreateResult`; other failures throw the usual error classes.\n\n## Honesty: what the SDK does not do\n\n**No automatic retries or backoff.** The SDK raises on rate limit (429). You must handle it:\n\n```ts\nimport { RateLimitError } from 'ship24';\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\nasync function retryOnRateLimit<T>(fn: () => Promise<T>, attempts = 3): Promise<T> {\n try {\n return await fn();\n } catch (err) {\n if (err instanceof RateLimitError && attempts > 1) {\n await sleep((err.retryAfter ?? 1) * 1000);\n return retryOnRateLimit(fn, attempts - 1);\n }\n throw err;\n }\n}\n```\n\n**No pagination helper.** `trackers.list` returns a flat array with no total count. Pagination is manual:\n\n```ts\nimport type { Tracker } from 'ship24';\n\nconst allTrackers: Tracker[] = [];\nlet page = 1;\nwhile (true) {\n const batch = await ship24.trackers.list({ page, limit: 100 });\n if (batch.length === 0) break;\n allTrackers.push(...batch);\n if (batch.length < 100) break;\n page++;\n}\n```\n\n**No webhook helper.** Webhook verification and parsing are your responsibility. See the `ship24-webhooks` skill or `docs.ship24.com/webhooks/overview`. You must verify the `Authorization: Bearer <webhook-secret>` header yourself using constant-time comparison.\n\n**Statuses are typed as `string`, not literal unions.** Compare against the documented snake_case values such as `\"delivered\"` or `\"delivery_delivered\"` (see `ship24-tracking-statuses`); the compiler will not catch a typo.\n\n## Exported types\n\nFrom `src/index.ts` and `src/types/*.ts`:\n\n```ts\nimport type {\n // Domain\n Tracker, Shipment, ShipmentDelivery, ShipmentRecipient, TrackingEvent, Statistics,\n Courier, CourierRequiredField, WebhookMetadata, Tracking, PerCallTracking,\n LogisticDateTime, IsoDateTime,\n // Requests\n CreateTrackerRequest, UpdateTrackerRequest, ListTrackersParams, PerCallTrackRequest,\n TrackerRecipientInput, TrackerSettingsInput,\n // Responses\n ApiErrorItem, BulkCreateItem, BulkCreateResult, ResendWebhooksResult, WebhookHistoryEntry, WebhookHistory,\n // Config and errors\n Ship24Config, RequestOptions, TrackerLookupOptions, RateLimitInfo, Ship24APIErrorInit,\n} from \"ship24\";\n```\n\nValue exports: `Ship24`, `TrackersResource`, `CouriersResource`, `PerCallResource`, `VERSION` and the error classes,\nincluding `Ship24APIError` and `InvalidRequestError`.\n\nThe API object `Event` is exported as `TrackingEvent` to avoid shadowing the DOM global. `LogisticDateTime` stays a\nplain string on purpose: coercing it to `Date` would lose the \"no time\" and offset information.\n\n## Schema drift and weekly checks\n\nThe `ship24-node` repository runs a weekly workflow that re-downloads the official OpenAPI spec from `docs.ship24.com/assets/openapi/ship24-tracking-api.yaml` and opens a PR when the vendored copy changed. Reconciling the hand-written types with the new spec is a manual step in that PR's checklist, so a spec change is noticed weekly but the types are not diffed automatically.\n\n## See also\n\nFor full integration patterns (when to use webhooks vs polling, idempotent creation, handling add-on fields), see the `ship24-integration` skill. For webhooks specifically, see `ship24-webhooks`.\n"
}SHA-256 of public snapshot: dfc4871dc4365f1833b7ffc31f45b4eac47fbd4f0e6815a26dc4ec470e77eb48