# pingpoint

**Category:** 💻 Developer Tools  
**Repository:** https://github.com/sudzikcoin/pingpoint-freight-mcp  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/pingpoint-2

## Description
Live truck GPS tracking: create loads, read live position, ETA and trip stats, cancel a load.

## Claude Desktop Quick Installation
Heuristic fallback — verify the package name and runner against the repository README before running it. Uses `npx` (confidence: low):

```json
"mcpServers": {
  "pingpoint": {
    "command": "npx",
    "args": ["-y","pingpoint-2"]
  }
}
```

## Documentation & README

# PingPoint — freight tracking MCP server and SDK

Real-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.

| Package | npm | What it is |
|---|---|---|
| [`@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 |
| [`@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 |

Full API documentation: **https://pingpoint.suverse.io/docs** · OpenAPI 3.1 spec: [`/docs/openapi.json`](https://pingpoint.suverse.io/docs/openapi.json)

## The problem

Most 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.

That 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.

## How it works

### 1. A load is created over the API

`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.

The 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:

- `customerRef` doubles as a **dedup key** — re-sending the same reference returns the existing load (`deduplicated: true`) instead of creating a duplicate;
- an `Idempotency-Key` header makes retries after a network failure safe — the balance is debited and the load created at most once.

### 2. The driver connects from an SMS link

PingPoint 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.

### 3. Position flows in over two independent channels

- **The driver's phone** — background geolocation from the app.
- **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.

The 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.

### 4. Statuses advance from geofences — never from a keyboard

Every 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.

On 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.

External 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`.

### 5. Reading it back

`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/)).

```text
 SMS link          +---------------------+
 (sent by  ------> |  Driver phone app   |--- background GPS ---+
  PingPoint)       +---------------------+                      |
                                                                v
                   +---------------------+   1 Hz frames   +--------------------+
                   |  ELD dongle on the  |---------------->| ingest (thinning)  |
                   |  diagnostic port,   |   via the app   +--------------------+
                   |  BLE (IOSiX, PT30)  |                      |
                   +---------------------+                      v
                                                       +-----------------+
                                                       |  position store |
                                                       +-----------------+
                                                            |        |
                                     geofence engine <------+        |
                                            |                        |
        PLANNED -> AT_PICKUP -> IN_TRANSIT -> AT_DELIVERY -> DELIVERED
                                            |                        |
                                            v                        v
                  webhooks -> your endpoint      GET /v1/agent/loads/{n}   (position, ETA)
                                                 GET .../trip-stats        (post-trip summary)
```

## Quick start

### Try it without signing up

A sandbox key is published in the docs and works on the same endpoints — no account, no balance, no live driver:

```
sup_agent_8Q3d7hfKjT2JOCQTmSBVVvXCjYK9Qcrk
```

Create 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`):

| `scenario` | What happens | Trip length |
|---|---|---|
| `normal` | Arrives inside the delivery window | ≈ 20 min |
| `late` | Arrives after the window — `onTime: false` | ≈ 26 min |
| `signal_loss` | Pings stop mid-trip for about 4½ minutes, then resume | ≈ 20 min |

What 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`.

Everything else is the production system: real geofences, real ETA math, real trip stats, real cancellation.

### Get your own key

1. Sign up at [pingpoint.suverse.io](https://pingpoint.suverse.io) (e-mail or Google/GitHub).
2. In the cabinet open **Integrations → Agent API** and press **Issue key**.
3. 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.

### First call

```bash
curl -X POST https://api.suverse.io/v1/agent/loads \
  -H "Authorization: Bearer sup_agent_…" \
  -H "Content-Type: application/json" \
  -d '{
    "driverPhone": "+15551234567",
    "pickups":    [{ "address": "6492 Tower Lane", "city": "Claremore", "state": "OK", "zip": "74017" }],
    "deliveries": [{ "address": "6499 Caldwell Park Dr", "city": "Charlotte", "state": "NC", "zip": "28269" }],
    "customerRef": "PO-483920"
  }'
```

```json
{
  "success": true,
  "loadId": "3b9f6a2e-1c47-4d8a-9e02-7f5b1c8d4a63",
  "loadNumber": "LD-2026-042317",
  "trackingLink": "https://pingpoint.suverse.io/track/trk_…",
  "driverWebLink": "https://pingpoint.suverse.io/driver/drv_…",
  "driverAppLink": "pingpoint://driver/drv_…",
  "driverResolution": "none"
}
```

The driver link is already on its way to `+15551234567` by SMS. From here, `GET /v1/agent/loads/LD-2026-042317` reads the live position.

### Connect the MCP server

Claude Code, one line:

```bash
claude mcp add pingpoint --env PINGPOINT_AGENT_KEY=sup_agent_… -- npx -y @suverselabs/pingpoint-mcp
```

Claude Desktop (`claude_desktop_config.json`) or any MCP-capable agent:

```json
{
  "mcpServers": {
    "pingpoint": {
      "command": "npx",
      "args": ["-y", "@suverselabs/pingpoint-mcp"],
      "env": {
        "PINGPOINT_AGENT_KEY": "sup_agent_…"
      }
    }
  }
}
```

Restart the agent and the tools appear.

## MCP tools

Detailed per-tool reference with full request/response examples: [docs/tools/](https://github.com/sudzikcoin/pingpoint-freight-mcp/blob/HEAD/docs/tools/).

| Tool | What it does | Parameters | Returns | Price |
|---|---|---|---|---|
| `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 |
| `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 |
| `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 |
| `cancel_load` | Cancels a load: status CANCELLED, tracking stops, nothing deleted, no refund (idempotent) | `loadNumber` | `{ ok, loadNumber, previousStatus, status, cancelledAt, trackingEndedAt }` | free |
| `update_load_status` | **Not part of the API** — statuses are GPS-verified | `loadNumber`, `status` | always HTTP 501 `OPERATION_NOT_AVAILABLE` | — |
| `get_pricing` | Current USD price list | — | `{ currency, prices }` | free |
| `get_balance` | Prepaid balance | — | `{ currency, balanceUsd }` | free |

Tool 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).

## SDK

```bash
npm install @suverselabs/pingpoint-sdk
```

```ts
import { PingPointAgent, InsufficientFundsError, DeliveryNotReadyError } from "@suverselabs/pingpoint-sdk";

const pp = new PingPointAgent({ apiKey: process.env.PINGPOINT_AGENT_KEY! });

// $0.65 — driver gets the app link by SMS
const load = await pp.createLoad(
  {
    driverPhone: "+15551234567",
    pickups: [{ address: "6492 Tower Lane", city: "Claremore", state: "OK", zip: "74017" }],
    deliveries: [{ address: "6499 Caldwell Park Dr", city: "Charlotte", state: "NC", zip: "28269" }],
    customerRef: "PO-483920",
  },
  { idempotencyKey: "PO-483920" },
);

const pos = await pp.getPosition(load.loadNumber);   // $0.02
const trip = await pp.getTripStats(load.loadNumber); // $0.02, best after DELIVERED
await pp.cancelLoad(load.loadNumber);                 // free, idempotent, no refund
```

Methods: `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](https://github.com/sudzikcoin/pingpoint-freight-mcp/blob/HEAD/docs/sdk.md).

Every non-2xx answer throws a typed subclass of `PingPointAgentError` carrying `.status` and the raw `.body`:

```ts
try {
  await pp.createLoad(input);
} catch (err) {
  if (err instanceof InsufficientFundsError) {
    console.log(`balance $${err.balanceUsd}, need $${err.priceUsd} — nothing was charged`);
  } else if (err instanceof DeliveryNotReadyError) {
    // driver hasn't arrived yet — do NOT retry; the load completes automatically when the truck departs the delivery zone
  }
}
```

Node ≥ 18 (uses global `fetch`), ESM + CJS, zero runtime dependencies.

## Data model

### Position (`get_load_position` / `getPosition`)

| Field | Unit / format | Meaning |
|---|---|---|
| `status` | enum | `PLANNED`, `AT_PICKUP`, `IN_TRANSIT`, `AT_DELIVERY`, `DELIVERED`, `CANCELLED` — advanced automatically from GPS and geofence events |
| `gpsTrack[]` | — | Up to the 500 most recent points, oldest first |
| `gpsTrack[].lat` / `lng` | degrees | Position fix |
| `gpsTrack[].speed` | mph, 1 decimal | Ground speed; `null` when the fix carries none |
| `gpsTrack[].heading` | degrees 0–359, 0 = north | `null` when unknown |
| `gpsTrack[].ts` | ISO 8601 UTC | Fix timestamp |
| `distanceMiles` | miles | Haversine over the **full** track (not just the 500 returned points); `null` until ≥ 2 pings |
| `stops[].arrivedAt` / `departedAt` | ISO 8601 UTC | Set by geofence arrival/departure |
| `stops[].windowFrom` / `windowTo` | ISO 8601 UTC | Planned windows, `null` when not set |
| `onTime` | boolean | Delivered within the delivery window (15 min grace); `null` until delivered or without a window |
| `delayMinutes`, `pickupDwellMinutes`, `deliveryDwellMinutes` | minutes | `null` when not yet known |
| `pingCount` | count | Total pings recorded for the load |
| `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 |

### Trip stats (`get_trip_stats` / `getTripStats`)

| Field | Unit | Meaning |
|---|---|---|
| `dataPoints` | count | GPS pings recorded for the load |
| `durationSeconds` | s | `lastAt − firstAt` |
| `estimatedDistanceMiles` | miles | Haversine over the full recorded track |
| `avgSpeedMph` | mph | Over the whole span, stops included |
| `maxSpeedMph` | mph | Maximum recorded ground speed |
| `hardAccelCount` | count | Speed gain > +15 mph/min while moving > 20 mph |
| `hardBrakeCount` | count | Speed drop < −20 mph/min while moving > 20 mph |
| `cityMilesPct` | % 0–100 | Share of miles at 5–45 mph |
| `highwayMilesPct` | % 0–100 | Share of miles above 45 mph |
| `parkedTimePct` | % 0–100 | Share of pings at ≤ 5 mph |
| `nightPct` | % 0–100 | Share of pings between 23:00–07:00 UTC |
| `coveragePct` | % ≤ 100 | Pings vs. a one-per-minute expectation over the span |
| `firstAt` / `lastAt` | ISO 8601 UTC | First/last recorded ping; `null` when no pings |

### Error codes

| Code | Meaning |
|---|---|
| `400 TOO_MANY_STOPS` | More than 2 pickups or 3 deliveries; the body carries `limits` and `received`. Nothing created, nothing charged. |
| `400 INVALID_STOP_WINDOW` | A stop's `dateTo` doesn't parse or ends before its `date`; the body names the field. |
| `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. |
| `401` | Missing or invalid key. |
| `402 INSUFFICIENT_FUNDS` | Prepaid balance can't cover the operation. **Nothing was charged and nothing was created.** Body carries `balanceUsd`, `priceUsd`, `billingUrl`. |
| `403` | The load belongs to another account. |
| `404` | No such load. |
| `409 LOAD_ALREADY_DELIVERED` | Cancel on a delivered load. Delivered is final — don't retry. |
| `422 UNKNOWN_BROKER` | The key's account is not registered on PingPoint. |
| `429 SANDBOX_RATE_LIMITED` | Sandbox key only: over 30 loads in the last hour. The cap is shared by everyone using the published key. |
| `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. |
| `503 BILLING_UNAVAILABLE` | Billing backend temporarily unreachable — nothing was charged, retry later. |

## Billing

Prepaid balance, per-call pricing, no subscription. Details: [docs/billing.md](https://github.com/sudzikcoin/pingpoint-freight-mcp/blob/HEAD/docs/billing.md).

| Operation | Price |
|---|---|
| Create a load | $0.65 |
| Read load position | $0.02 per request |
| Trip summary stats | $0.02 per request |
| Cancel a load | free |
| Pricing, balance | free |

- Top up in the cabinet under **Billing**. Free operations work at zero balance.
- A `402` means the call was rejected **before** anything happened: nothing created, nothing charged.
- `createLoad` retries are safe with the same `Idempotency-Key` — the debit happens at most once; `customerRef` deduplicates at the business level.
- Prices are served live by `GET /v1/agent/pricing` — treat that as the source of truth, never hardcode them.

## What this is not

- **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.
- **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.
- **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.

## How this compares

Enterprise 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)**.

## Links

- API documentation: https://pingpoint.suverse.io/docs
- OpenAPI 3.1 spec: https://pingpoint.suverse.io/docs/openapi.json
- Comparison with alternatives: https://pingpoint.suverse.io/compare
- MCP server on npm: https://www.npmjs.com/package/@suverselabs/pingpoint-mcp
- SDK on npm: https://www.npmjs.com/package/@suverselabs/pingpoint-sdk
- In-repo docs: [architecture](https://github.com/sudzikcoin/pingpoint-freight-mcp/blob/HEAD/docs/architecture.md) · [billing](https://github.com/sudzikcoin/pingpoint-freight-mcp/blob/HEAD/docs/billing.md) · [SDK reference](https://github.com/sudzikcoin/pingpoint-freight-mcp/blob/HEAD/docs/sdk.md) · [MCP tools](https://github.com/sudzikcoin/pingpoint-freight-mcp/blob/HEAD/docs/tools/)
- Contact: info@suverse.io

## License

[MIT](https://github.com/sudzikcoin/pingpoint-freight-mcp/blob/HEAD/LICENSE) © 2026 Sudzik Group Inc.

