← Ship24 Tracking APICONTENT HISTORY

Update to Ship24 Tracking API

Snapshot Sep 30, 2026 · 23:16 UTC · version 1.0.0

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "name": "ship24-integration",
  "description": "Integrate the Ship24 Tracking API into a codebase end to end. Use when asked to \"add Ship24\", \"integrate Ship24\", \"integrate package, parcel or shipment tracking\", create trackers, choose between webhooks and polling, or call the per-call tracking endpoint, in any language or framework. Detects the project's stack, confirms the Ship24 plan type and update mechanism, then guides implementation and verification against the OpenAPI spec. For webhook receivers, courier codes, statuses, errors or the Node SDK, the sibling ship24-* skills go deeper.",
  "included_files": [
    {
      "relative_path": "agents/openai.yaml",
      "size_in_bytes": 309
    },
    {
      "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/errors.md",
      "size_in_bytes": 4322
    },
    {
      "relative_path": "references/integration-patterns.md",
      "size_in_bytes": 4702
    },
    {
      "relative_path": "references/rate-limits.md",
      "size_in_bytes": 1896
    },
    {
      "relative_path": "references/schemas.md",
      "size_in_bytes": 12633
    }
  ],
  "skill_md_contents": "---\nname: ship24-integration\ndescription: Integrate the Ship24 Tracking API into a codebase end to end. Use when asked to \"add Ship24\", \"integrate Ship24\", \"integrate package, parcel or shipment tracking\", create trackers, choose between webhooks and polling, or call the per-call tracking endpoint, in any language or framework. Detects the project's stack, confirms the Ship24 plan type and update mechanism, then guides implementation and verification against the OpenAPI spec. For webhook receivers, courier codes, statuses, errors or the Node SDK, the sibling ship24-* skills go deeper.\nlicense: MIT\n---\n\n# Ship24 Tracking API integration\n\nOpinionated wizard: detect the project, clarify the plan, guide the implementation, verify the result.\n\nFacts in this skill come from [docs.ship24.com](https://docs.ship24.com) and the OpenAPI 3.1 spec at\n[docs.ship24.com/assets/openapi/ship24-tracking-api.yaml](https://docs.ship24.com/assets/openapi/ship24-tracking-api.yaml).\nThe bundled `references/endpoints.md` and `schemas.md` are generated from that spec, `errors.md` and\n`rate-limits.md` from the docs pages. When a detail matters (a field name, a status\ncode, a limit), read the reference file rather than guessing:\n\n| Need | Read |\n| --- | --- |\n| Every endpoint, parameters, responses, rate limit | `references/endpoints.md` |\n| Field tables for tracker, tracking, shipment, event, request bodies | `references/schemas.md` |\n| HTTP statuses and error codes | `references/errors.md` |\n| Rate limits and rate-limit headers | `references/rate-limits.md` |\n| Webhook vs polling trade-offs, matching records, idempotent creation | `references/integration-patterns.md` |\n\nSibling skills: `ship24-webhooks` (build the receiver), `ship24-couriers` (courier codes and required fields),\n`ship24-tracking-statuses` (map statuses to business logic), `ship24-troubleshooting` (errors), `ship24-node-sdk`\n(the official `ship24` npm package).\n\n## Phase 1: Detect\n\nScan before asking. Use the host tool's search if there is no shell.\n\n```bash\ngrep -ri \"ship24\" . --include=*.{js,ts,py,rb,php,go,java,cs,env,json,yaml,yml} 2>/dev/null | head -20\ngrep -ri \"SHIP24\" .env .env.local .env.example 2>/dev/null\ngrep -rE \"express|fastify|nest|fastapi|django|flask|rails|laravel|symfony|gin|spring|aspnet\" \\\n  package.json requirements.txt pyproject.toml Gemfile composer.json go.mod pom.xml *.csproj 2>/dev/null | head\ngrep -rn \"webhook\" . --include=*.{js,ts,py,rb,php,go,java,cs} 2>/dev/null | head\ngrep -rE \"bull|bullmq|celery|sidekiq|rabbitmq|sqs|pubsub|kafka\" \\\n  package.json requirements.txt pyproject.toml Gemfile composer.json go.mod 2>/dev/null | head -5\n```\n\nNote: existing Ship24 usage (trackers created? webhook route present?), language and framework, HTTP client in\nuse, queue or async infrastructure, an existing public webhook route that can be extended.\n\nIf the project is Node.js or TypeScript, prefer the official SDK (`npm install ship24`) over hand-written HTTP\ncalls and switch to the `ship24-node-sdk` skill for the implementation details.\n\n## Phase 2: Clarify\n\nAsk only what detection could not determine, one question at a time.\n\n1. **Account and key.** Does the user have a Ship24 account and API key? A free plan exists for integration and\n   testing: [dashboard.ship24.com/onboarding](https://dashboard.ship24.com/onboarding). Keys live under\n   Integrations → API Keys (up to 20 active keys per account).\n2. **Plan type.** Per-shipment (the standard product: trackers, webhooks, polling) or per-call (a separate\n   subscription, one synchronous endpoint, no trackers)? If unsure: per-shipment is the default choice; Ship24\n   documents it as offering more features, faster fetching and lower overall cost.\n3. **Update mechanism** (per-shipment only). Webhooks (Ship24 pushes updates, recommended) or polling (the app\n   calls the API)? Webhooks need a public HTTPS endpoint. At thousands of trackers, polling stops being\n   practical.\n4. **Constraints.** New project or existing app? Serverless? No public URL? Multi-tenant?\n\nDo not write code until plan type, key handling (env var) and update mechanism are settled.\n\n## Phase 3: Guide\n\n### Authentication and base URL (all plans)\n\n- Base URL: `https://api.ship24.com/public/v1`\n- Header on every request: `Authorization: Bearer <api key>` (keys look like `apik_...`).\n- Read the key from the `SHIP24_API_KEY` environment variable. Never hardcode it, commit it, or ship it to a\n  browser or mobile client: the key grants full access to the account's tracking data and quota.\n\n```bash\n# .env (never committed)\nSHIP24_API_KEY=apik_your_key_here\n```\n\n### Route by plan\n\n| Plan | Endpoints | Docs |\n| --- | --- | --- |\n| Per-shipment | `POST /trackers`, `POST /trackers/bulk`, `POST /trackers/track`, `GET /trackers/{trackerId}/results`, webhooks | [Trackers](https://docs.ship24.com/trackers), [Webhooks](https://docs.ship24.com/webhooks/overview) |\n| Per-call | `POST /tracking/search` only | [Per-call API](https://docs.ship24.com/per-call-api) |\n\n### Per-shipment plan\n\n**Webhook flow (recommended)**\n\n1. Build a `POST` endpoint that answers `2xx` quickly and processes asynchronously (see `ship24-webhooks`).\n2. Register its URL in the dashboard: Integrations → Webhooks. There is no API for this.\n3. Create a tracker per shipment with `POST /trackers` (or up to 100 at once with `POST /trackers/bulk`).\n   Store the returned `trackerId`.\n4. Ship24 pushes every new event to the endpoint. A webhook `events[]` array contains only the events\n   discovered since the last push; API responses contain the full history.\n\n**Polling flow**\n\n1. `POST /trackers`, store `trackerId`.\n2. Do not poll immediately: results are not available the instant a tracker is created.\n3. `GET /trackers/{trackerId}/results` on an interval the app chooses. Ship24 does not mandate a cadence.\n   Longer intervals for slower shipments.\n4. Stop when the tracker's `isTracked` is `false`: Ship24 has stopped tracking that shipment.\n\n**Synchronous one-shot flow**\n\n`POST /trackers/track` creates the tracker and returns results in the same call. The first call can take up to\none minute because Ship24 queries couriers synchronously; some couriers do not support it and return results on\nlater calls. Calling it again with the same payload returns the same tracker with fresh results.\n\n**Idempotency** (verified in Ship24's implementation, see the plugin's CONTRIBUTING). `POST /trackers` and\n`POST /trackers/track` look up the account's trackers with the same `clientTrackerId` when one is sent, otherwise\nwith the same `trackingNumber`, and reuse a candidate without consuming quota when `shippingDate` (day\nprecision), `originCountryCode`, `destinationCountryCode`, `destinationPostCode`, `shipmentReference`,\n`courierName`, `trackingUrl`, `settings.restrictTrackingToCourierCode` and the `courierCode` list all match.\nOtherwise a new tracker is created. `tracker_conflict` comes back when the `clientTrackerId` belongs to an active\ntracker with a different payload, and on `POST /trackers/track` when the `courierCode` list overlaps an existing\ntracker's list without matching it; differentiate with `shippingDate` or `destinationCountryCode`, or resend the\nsame payload.\n\n**Identify shipments.** Tracking numbers are not unique across couriers or time. Store Ship24's `trackerId`.\nAlternatively set `clientTrackerId` to the app's own unique id: Ship24 validates its uniqueness across\nsubscribed trackers and most endpoints accept `?searchBy=clientTrackerId`. `shipmentReference` is free text,\nnot validated for uniqueness, and returned in responses and webhooks.\n\n**Tracker creation fields** (full table in `references/schemas.md`, `tracker-create-request`):\n\n| Field | Notes |\n| --- | --- |\n| `trackingNumber` | Required. 5 to 50 chars, `A-Z a-z 0-9 - _ / .`; returned uppercased, compare case-insensitively. Dummy values such as `123456789` are rejected. |\n| `courierCode` | Optional string or array (max 3 per request). Improves detection; see `ship24-couriers`. |\n| `clientTrackerId` | Optional, max 100 chars, unique across subscribed trackers. |\n| `shipmentReference` | Optional, max 100 chars, not unique. |\n| `originCountryCode`, `destinationCountryCode` | ISO 3166-1 alpha-2 or alpha-3 accepted; alpha-2 returned. Some couriers require the destination. |\n| `destinationPostCode` | 1 to 32 chars. Some couriers require it. |\n| `shippingDate` | ISO 8601. Rejected with `shipping_date_outdated` when older than 180 days. |\n| `recipient.email`, `recipient.name` | Used for email notifications; `name` also feeds courier-restricted tracking data. Max 254 and 100 chars. |\n| `settings.restrictTrackingToCourierCode` | Track only with the given courier codes. |\n\n**Bulk creation.** `POST /trackers/bulk` accepts 1 to 100 items and answers `201` (the docs also list `200`),\n`207` (partial) or an error. Its body is `{ status, summary, data, error }` with `status` in\n`success | partial | error`, unlike the `{ data }` envelope of the other tracker endpoints. Rate limit 3 requests\nper second.\n\n### Per-call plan\n\n`POST /tracking/search` with the tracking number (plus optional courier and destination hints) fetches results\nsynchronously from couriers. Response time can reach one minute. No tracker is created, nothing to store, no\nwebhooks. Requires an active per-call subscription; without one the API answers `no_active_subscription`.\n\n### Data handling rules that bite\n\n- Add-on fields (for example `shipment.delivery.aiPredictiveDeliveryDate`) are **absent**, not `null`, when\n  the account lacks the option or Ship24 has no value. Check presence before reading.\n- Event `occurrenceDatetime` is a `logistic-date-time`: local time, local time with offset, UTC, or date only.\n  Keep it as a string; use the event `order` field to sort events that lack a time.\n- `datetime`, `utcOffset`, `hasNoTime` (events) and `signedBy` (delivery) are deprecated. Do not build on them.\n- Rate limits are per endpoint per second (most are 10 req/s, bulk 3, couriers and resend 1). Honor\n  `Retry-After` on `429`. Details in `references/rate-limits.md`.\n- Error bodies are `{ \"errors\": [{ \"code\", \"message\" }], \"data\": null }` (`message` optional), except the bulk\n  endpoint's `{ status, summary, data, error }`. Branch on `code`, not on the message text. Codes in\n  `references/errors.md`.\n\n## Phase 4: Verify\n\n- [ ] API key read from `SHIP24_API_KEY`; not in source, not in client-side code.\n- [ ] `trackerId` (or a validated `clientTrackerId`) persisted for every created tracker.\n- [ ] Webhook endpoint answers `2xx` before processing and matches payloads on `trackerId` or `clientTrackerId`.\n- [ ] Duplicate deliveries handled: deduplicate on `events[].eventId`.\n- [ ] Polling does not start immediately, uses an interval the team chose deliberately, and stops on\n      `isTracked: false`.\n- [ ] `401`, `403`, `422`, `429` (with `Retry-After`) and `5xx` handled; error `code` logged.\n- [ ] Add-on fields read only when present.\n- [ ] Tested end to end with a Ship24 sample tracking number such as `SHIP24_SAMPLE_DELIVERED_000`\n      (see `ship24-tracking-statuses` for the full list).\n\n## Key links\n\n| Resource | URL |\n| --- | --- |\n| Dashboard, API keys, webhook URL | https://dashboard.ship24.com |\n| Documentation | https://docs.ship24.com |\n| API reference | https://docs.ship24.com/tracking-api-reference/ |\n| OpenAPI 3.1 spec | https://docs.ship24.com/assets/openapi/ship24-tracking-api.yaml |\n| Node SDK | https://github.com/ship24/ship24-node |\n| Pricing | https://www.ship24.com/pricing |\n"
}

SHA-256: ee5281bcb275102245b767f1df354e9e6181b7d529402eb349daaeb3db05a15d