{
  "markdown": "# PingPoint — freight tracking MCP server and SDK\n\nReal-time freight tracking and load visibility for logistics software and AI agents: an MCP server and a TypeScript SDK that give any agent live driver GPS position for a truckload shipment in US trucking — create a load over the API, the driver connects from an SMS link in about a minute, and from then on position, ETA, stop timeline and post-trip stats are one call away. No ELD provider integration, no corporate contract, no sales call.\n\n| Package | npm | What it is |\n|---|---|---|\n| [`@suverselabs/pingpoint-mcp`](https://www.npmjs.com/package/@suverselabs/pingpoint-mcp) | `npm i @suverselabs/pingpoint-mcp` | MCP server — 7 tools over stdio, for Claude and any MCP-capable agent |\n| [`@suverselabs/pingpoint-sdk`](https://www.npmjs.com/package/@suverselabs/pingpoint-sdk) | `npm i @suverselabs/pingpoint-sdk` | Typed API client — zero dependencies, typed errors, idempotent retries |\n\nFull API documentation: **https://pingpoint.suverse.io/docs** · OpenAPI 3.1 spec: [`/docs/openapi.json`](https://pingpoint.suverse.io/docs/openapi.json)\n\n## The problem\n\nMost carriers in US trucking are one- or two-truck companies. They have no corporate telematics stack, no visibility contract, and no IT department — the truck is the company. When a broker needs to know where a load is, the only reliable instrument is a phone call to the driver.\n\nThat is why \"AI track & trace\" from most vendors today means a robot that calls a human and asks. The position data itself never becomes machine-readable — it lives in one driver's head, one call at a time. PingPoint makes the position itself available over an API: the driver installs one app from an SMS link, and from that moment any software — or any AI agent through MCP — reads live GPS instead of asking someone to dial.\n\n## How it works\n\n### 1. A load is created over the API\n\n`POST /v1/agent/loads` with the driver's phone and the stops. Required: `driverPhone` (E.164 — the driver link is texted to this number) and the `pickups` / `deliveries` arrays; every stop needs `address`, `city`, `state`, `zip`. Multi-stop loads are supported — several pickups and several deliveries, in array order.\n\nThe response carries the `loadNumber` (used in every later call), a public `trackingLink` for the customer, and the driver web/app links. Two safety nets against double-charging:\n\n- `customerRef` doubles as a **dedup key** — re-sending the same reference returns the existing load (`deduplicated: true`) instead of creating a duplicate;\n- an `Idempotency-Key` header makes retries after a network failure safe — the balance is debited and the load created at most once.\n\n### 2. The driver connects from an SMS link\n\nPingPoint texts the driver a link automatically. The link opens onboarding: install the app, tap through consent, done — about a minute of the driver's time, once. Under the hood the link carries a one-time load token which the app exchanges for a persistent device token, so the next load to the same phone number binds without any new setup.\n\n### 3. Position flows in over two independent channels\n\n- **The driver's phone** — background geolocation from the app.\n- **An ELD dongle on the truck's diagnostic port** — streams vehicle data over Bluetooth to the app, which relays it. Tested with IOSiX and Pacific Track PT30 hardware. The dongle emits frames at 1 Hz; the app thins them before upload so the stored track stays dense enough for geofencing without drowning the pipeline.\n\nThe phone stays the gateway for both channels — the dongle talks to the app, not to the network. The point of two sources is that they fail differently: the dongle keeps positions coming for as long as the engine runs even when the phone's GPS can't get a fix or the OS has throttled background geolocation. Dongle frames also carry their own timestamps, taken from the frame itself rather than the moment of upload — so when a buffered backlog is flushed after an offline stretch, the recorded times are the real ones.\n\n### 4. Statuses advance from geofences — never from a keyboard\n\nEvery pickup and delivery stop gets a geofence. Entering the pickup zone moves the load to `AT_PICKUP`, leaving it moves to `IN_TRANSIT`, entering the delivery zone to `AT_DELIVERY` — and `DELIVERED` is set when the truck **departs** the final delivery zone, not on arrival. Stop `arrivedAt` / `departedAt` timestamps come from the same geofence events.\n\nOn a multi-stop load only the ends move the status: departing the **first** stop sets `IN_TRANSIT`, arriving at the **last** sets `AT_DELIVERY` and departing it sets `DELIVERED` — whatever the type of that stop. Middle stops record their own timestamps and leave the load's status alone.\n\nExternal status writes **don't exist**: `PATCH …/status` answers `501 OPERATION_NOT_AVAILABLE`. This is a data-integrity guarantee, not a missing feature — a status you read was never hand-set by anyone; there is recorded position behind it. Delivery confirmation is likewise not a vendor operation: it belongs to the carrier flow, where the carrier files the BOL over Telegram. A load you no longer need is stopped with `cancel_load`.\n\n### 5. Reading it back\n\n`GET /v1/agent/loads/{loadNumber}` returns the live state: status, the GPS track (up to the 500 most recent points), the stop timeline with arrival/departure timestamps, distance covered, dwell times, on-time flag and an ETA block computed from the stored route geometry and the latest position. After the trip, `GET …/trip-stats` returns an aggregated summary computed over every recorded ping. Webhooks can push load events to your endpoint as they happen (see the [docs](https://pingpoint.suverse.io/docs/)).\n\n```text\n SMS link          +---------------------+\n (sent by  ------> |  Driver phone app   |--- background GPS ---+\n  PingPoint)       +---------------------+                      |\n                                                                v\n                   +---------------------+   1 Hz frames   +--------------------+\n                   |  ELD dongle on the  |---------------->| ingest (thinning)  |\n                   |  diagnostic port,   |   via the app   +--------------------+\n                   |  BLE (IOSiX, PT30)  |                      |\n                   +---------------------+                      v\n                                                       +-----------------+\n                                                       |  position store |\n                                                       +-----------------+\n                                                            |        |\n                                     geofence engine <------+        |\n                                            |                        |\n        PLANNED -> AT_PICKUP -> IN_TRANSIT -> AT_DELIVERY -> DELIVERED\n                                            |                        |\n                                            v                        v\n                  webhooks -> your endpoint      GET /v1/agent/loads/{n}   (position, ETA)\n                                                 GET .../trip-stats        (post-trip summary)\n```\n\n## Quick start\n\n### Try it without signing up\n\nA sandbox key is published in the docs and works on the same endpoints — no account, no balance, no live driver:\n\n```\nsup_agent_8Q3d7hfKjT2JOCQTmSBVVvXCjYK9Qcrk\n```\n\nCreate a load with it and add a `scenario` field. A simulated truck then walks the real route through the same geofence engine a live truck uses, so statuses advance on their own (`PLANNED → AT_PICKUP → IN_TRANSIT → AT_DELIVERY → DELIVERED`):\n\n| `scenario` | What happens | Trip length |\n|---|---|---|\n| `normal` | Arrives inside the delivery window | ≈ 20 min |\n| `late` | Arrives after the window — `onTime: false` | ≈ 26 min |\n| `signal_loss` | Pings stop mid-trip for about 4½ minutes, then resume | ≈ 20 min |\n\nWhat it is not: there is no real driver, no real phone and no ELD dongle — SMS is never sent, though the `driverLink` in the response is returned and opens. The key is shared, so every sandbox load is visible to everyone holding it: don't put real addresses or phone numbers in one, and either leave `customerRef` out or make it unique (it is the dedup key — a collision returns someone else's load). Sandbox loads live at least an hour and are then removed by the hourly cleanup. The cap is 30 loads per hour **shared by all users of the key**; over it the create call answers `429 SANDBOX_RATE_LIMITED`.\n\nEverything else is the production system: real geofences, real ETA math, real trip stats, real cancellation.\n\n### Get your own key\n\n1. Sign up at [pingpoint.suverse.io](https://pingpoint.suverse.io) (e-mail or Google/GitHub).\n2. In the cabinet open **Integrations → Agent API** and press **Issue key**.\n3. The `sup_agent_…` key arrives by e-mail. PingPoint never stores the secret — if it's lost, re-issue a new one from the same page.\n\n### First call\n\n```bash\ncurl -X POST https://api.suverse.io/v1/agent/loads \\\n  -H \"Authorization: Bearer sup_agent_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"driverPhone\": \"+15551234567\",\n    \"pickups\":    [{ \"address\": \"6492 Tower Lane\", \"city\": \"Claremore\", \"state\": \"OK\", \"zip\": \"74017\" }],\n    \"deliveries\": [{ \"address\": \"6499 Caldwell Park Dr\", \"city\": \"Charlotte\", \"state\": \"NC\", \"zip\": \"28269\" }],\n    \"customerRef\": \"PO-483920\"\n  }'\n```\n\n```json\n{\n  \"success\": true,\n  \"loadId\": \"3b9f6a2e-1c47-4d8a-9e02-7f5b1c8d4a63\",\n  \"loadNumber\": \"LD-2026-042317\",\n  \"trackingLink\": \"https://pingpoint.suverse.io/track/trk_…\",\n  \"driverWebLink\": \"https://pingpoint.suverse.io/driver/drv_…\",\n  \"driverAppLink\": \"pingpoint://driver/drv_…\",\n  \"driverResolution\": \"none\"\n}\n```\n\nThe driver link is already on its way to `+15551234567` by SMS. From here, `GET /v1/agent/loads/LD-2026-042317` reads the live position.\n\n### Connect the MCP server\n\nClaude Code, one line:\n\n```bash\nclaude mcp add pingpoint --env PINGPOINT_AGENT_KEY=sup_agent_… -- npx -y @suverselabs/pingpoint-mcp\n```\n\nClaude Desktop (`claude_desktop_config.json`) or any MCP-capable agent:\n\n```json\n{\n  \"mcpServers\": {\n    \"pingpoint\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@suverselabs/pingpoint-mcp\"],\n      \"env\": {\n        \"PINGPOINT_AGENT_KEY\": \"sup_agent_…\"\n      }\n    }\n  }\n}\n```\n\nRestart the agent and the tools appear.\n\n## MCP tools\n\nDetailed per-tool reference with full request/response examples: [docs/tools/](docs/tools/).\n\n| Tool | What it does | Parameters | Returns | Price |\n|---|---|---|---|---|\n| `create_load` | Creates a freight load; PingPoint texts the driver link to `driverPhone` | `driverPhone`, `pickups[]` (1–2), `deliveries[]` (1–3) required — each stop takes `date`/`dateTo` for its window; `shipperName`, `carrierName`, `equipmentType`, `customerRef`, `rate`, `miles`, `weight`, `truckNumber`, `idempotencyKey` (optional) | `loadNumber`, public `trackingLink`, driver web/app links, `driverResolution`, dedup flag | $0.65 |\n| `get_load_position` | Live state of a load | `loadNumber` | status, GPS track (last 500 points), stops with arrive/depart timestamps, distance, on-time flag, dwell times, ETA block | $0.02 |\n| `get_trip_stats` | Aggregated summary of the whole GPS trip (meant for a DELIVERED load; mid-trip returns the trip so far) | `loadNumber` | `stats`: distance, duration, avg/max speed, hard accel/brake counts, city/highway/parked/night shares, GPS coverage, first/last ping | $0.02 |\n| `cancel_load` | Cancels a load: status CANCELLED, tracking stops, nothing deleted, no refund (idempotent) | `loadNumber` | `{ ok, loadNumber, previousStatus, status, cancelledAt, trackingEndedAt }` | free |\n| `update_load_status` | **Not part of the API** — statuses are GPS-verified | `loadNumber`, `status` | always HTTP 501 `OPERATION_NOT_AVAILABLE` | — |\n| `get_pricing` | Current USD price list | — | `{ currency, prices }` | free |\n| `get_balance` | Prepaid balance | — | `{ currency, balanceUsd }` | free |\n\nTool descriptions are written for the calling model: each one states what it costs, when to use it and when not to (e.g. `get_load_position` answers \"where is the truck **now**\", `get_trip_stats` answers \"how did the **finished** trip go\", and both warn against polling in a loop because every call is billed).\n\n## SDK\n\n```bash\nnpm install @suverselabs/pingpoint-sdk\n```\n\n```ts\nimport { PingPointAgent, InsufficientFundsError, DeliveryNotReadyError } from \"@suverselabs/pingpoint-sdk\";\n\nconst pp = new PingPointAgent({ apiKey: process.env.PINGPOINT_AGENT_KEY! });\n\n// $0.65 — driver gets the app link by SMS\nconst load = await pp.createLoad(\n  {\n    driverPhone: \"+15551234567\",\n    pickups: [{ address: \"6492 Tower Lane\", city: \"Claremore\", state: \"OK\", zip: \"74017\" }],\n    deliveries: [{ address: \"6499 Caldwell Park Dr\", city: \"Charlotte\", state: \"NC\", zip: \"28269\" }],\n    customerRef: \"PO-483920\",\n  },\n  { idempotencyKey: \"PO-483920\" },\n);\n\nconst pos = await pp.getPosition(load.loadNumber);   // $0.02\nconst trip = await pp.getTripStats(load.loadNumber); // $0.02, best after DELIVERED\nawait pp.cancelLoad(load.loadNumber);                 // free, idempotent, no refund\n```\n\nMethods: `createLoad(input, { idempotencyKey? })`, `getPosition(loadNumber)`, `getTripStats(loadNumber)`, `cancelLoad(loadNumber)`, `updateStatus(loadNumber, status)` (kept only to throw a typed 501), `getPricing()`, `getBalance()`. Full reference: [docs/sdk.md](docs/sdk.md).\n\nEvery non-2xx answer throws a typed subclass of `PingPointAgentError` carrying `.status` and the raw `.body`:\n\n```ts\ntry {\n  await pp.createLoad(input);\n} catch (err) {\n  if (err instanceof InsufficientFundsError) {\n    console.log(`balance $${err.balanceUsd}, need $${err.priceUsd} — nothing was charged`);\n  } else if (err instanceof DeliveryNotReadyError) {\n    // driver hasn't arrived yet — do NOT retry; the load completes automatically when the truck departs the delivery zone\n  }\n}\n```\n\nNode ≥ 18 (uses global `fetch`), ESM + CJS, zero runtime dependencies.\n\n## Data model\n\n### Position (`get_load_position` / `getPosition`)\n\n| Field | Unit / format | Meaning |\n|---|---|---|\n| `status` | enum | `PLANNED`, `AT_PICKUP`, `IN_TRANSIT`, `AT_DELIVERY`, `DELIVERED`, `CANCELLED` — advanced automatically from GPS and geofence events |\n| `gpsTrack[]` | — | Up to the 500 most recent points, oldest first |\n| `gpsTrack[].lat` / `lng` | degrees | Position fix |\n| `gpsTrack[].speed` | mph, 1 decimal | Ground speed; `null` when the fix carries none |\n| `gpsTrack[].heading` | degrees 0–359, 0 = north | `null` when unknown |\n| `gpsTrack[].ts` | ISO 8601 UTC | Fix timestamp |\n| `distanceMiles` | miles | Haversine over the **full** track (not just the 500 returned points); `null` until ≥ 2 pings |\n| `stops[].arrivedAt` / `departedAt` | ISO 8601 UTC | Set by geofence arrival/departure |\n| `stops[].windowFrom` / `windowTo` | ISO 8601 UTC | Planned windows, `null` when not set |\n| `onTime` | boolean | Delivered within the delivery window (15 min grace); `null` until delivered or without a window |\n| `delayMinutes`, `pickupDwellMinutes`, `deliveryDwellMinutes` | minutes | `null` when not yet known |\n| `pingCount` | count | Total pings recorded for the load |\n| `eta` | object | Next stop, distance to it (mi), drive time (h), moving flag, ETA window; fail-soft — degrades to a reason-only object when there is not enough data |\n\n### Trip stats (`get_trip_stats` / `getTripStats`)\n\n| Field | Unit | Meaning |\n|---|---|---|\n| `dataPoints` | count | GPS pings recorded for the load |\n| `durationSeconds` | s | `lastAt − firstAt` |\n| `estimatedDistanceMiles` | miles | Haversine over the full recorded track |\n| `avgSpeedMph` | mph | Over the whole span, stops included |\n| `maxSpeedMph` | mph | Maximum recorded ground speed |\n| `hardAccelCount` | count | Speed gain > +15 mph/min while moving > 20 mph |\n| `hardBrakeCount` | count | Speed drop < −20 mph/min while moving > 20 mph |\n| `cityMilesPct` | % 0–100 | Share of miles at 5–45 mph |\n| `highwayMilesPct` | % 0–100 | Share of miles above 45 mph |\n| `parkedTimePct` | % 0–100 | Share of pings at ≤ 5 mph |\n| `nightPct` | % 0–100 | Share of pings between 23:00–07:00 UTC |\n| `coveragePct` | % ≤ 100 | Pings vs. a one-per-minute expectation over the span |\n| `firstAt` / `lastAt` | ISO 8601 UTC | First/last recorded ping; `null` when no pings |\n\n### Error codes\n\n| Code | Meaning |\n|---|---|\n| `400 TOO_MANY_STOPS` | More than 2 pickups or 3 deliveries; the body carries `limits` and `received`. Nothing created, nothing charged. |\n| `400 INVALID_STOP_WINDOW` | A stop's `dateTo` doesn't parse or ends before its `date`; the body names the field. |\n| `400 MISSING_FIELDS` | Required fields absent — the body lists them in `fields[]` (dotted paths, e.g. `pickups.0.zip`). Also `400 INVALID_DRIVER_PHONE` when the phone is not E.164. |\n| `401` | Missing or invalid key. |\n| `402 INSUFFICIENT_FUNDS` | Prepaid balance can't cover the operation. **Nothing was charged and nothing was created.** Body carries `balanceUsd`, `priceUsd`, `billingUrl`. |\n| `403` | The load belongs to another account. |\n| `404` | No such load. |\n| `409 LOAD_ALREADY_DELIVERED` | Cancel on a delivered load. Delivered is final — don't retry. |\n| `422 UNKNOWN_BROKER` | The key's account is not registered on PingPoint. |\n| `429 SANDBOX_RATE_LIMITED` | Sandbox key only: over 30 loads in the last hour. The cap is shared by everyone using the published key. |\n| `501 OPERATION_NOT_AVAILABLE` | The route isn't part of the API (status writes, delivery confirmation). The body lists what is. Nothing charged; don't retry. |\n| `503 BILLING_UNAVAILABLE` | Billing backend temporarily unreachable — nothing was charged, retry later. |\n\n## Billing\n\nPrepaid balance, per-call pricing, no subscription. Details: [docs/billing.md](docs/billing.md).\n\n| Operation | Price |\n|---|---|\n| Create a load | $0.65 |\n| Read load position | $0.02 per request |\n| Trip summary stats | $0.02 per request |\n| Cancel a load | free |\n| Pricing, balance | free |\n\n- Top up in the cabinet under **Billing**. Free operations work at zero balance.\n- A `402` means the call was rejected **before** anything happened: nothing created, nothing charged.\n- `createLoad` retries are safe with the same `Idempotency-Key` — the debit happens at most once; `customerRef` deduplicates at the business level.\n- Prices are served live by `GET /v1/agent/pricing` — treat that as the source of truth, never hardcode them.\n\n## What this is not\n\n- **Not a certified ELD.** PingPoint reads GPS (and, through the dongle, engine-bus data) for visibility. It is not an FMCSA-registered ELD and does not produce HOS/RODS compliance records.\n- **Not carrier vetting.** A live position tells you where the truck is, not whether the carrier is safe, insured or real. Keep whatever onboarding checks you run today.\n- **The driver has to install the app.** One SMS link, one install, about a minute — but it is a real step that requires the driver's cooperation. A load with no connected phone and no dongle produces no positions.\n\n## How this compares\n\nEnterprise visibility platforms assume the carrier already has telematics and the broker already has a contract; call-based tracking vendors put a phone call (human or robotic) in the loop for every check. PingPoint's trade is different: one driver-side install in exchange for a per-call API with published prices and no minimums. A factual, cell-by-cell comparison with both groups — key issuance, public pricing, API surface, MCP/SDK availability — is maintained at **[pingpoint.suverse.io/compare](https://pingpoint.suverse.io/compare)**.\n\n## Links\n\n- API documentation: https://pingpoint.suverse.io/docs\n- OpenAPI 3.1 spec: https://pingpoint.suverse.io/docs/openapi.json\n- Comparison with alternatives: https://pingpoint.suverse.io/compare\n- MCP server on npm: https://www.npmjs.com/package/@suverselabs/pingpoint-mcp\n- SDK on npm: https://www.npmjs.com/package/@suverselabs/pingpoint-sdk\n- In-repo docs: [architecture](docs/architecture.md) · [billing](docs/billing.md) · [SDK reference](docs/sdk.md) · [MCP tools](docs/tools/)\n- Contact: info@suverse.io\n\n## License\n\n[MIT](LICENSE) © 2026 Sudzik Group Inc.\n",
  "bytes": 20188,
  "sha": "63fd0a2866518e6a34061a7a5d7c3f5fa4f6ed5f123d6c2f71e518cb4be80356",
  "repo_slug": "sudzikcoin/pingpoint-freight-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_sudzikcoin_pingpoint_c2a7462c/readme"
}