PHPackages                             chrisjohnleah/velocity-fleet-api - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. chrisjohnleah/velocity-fleet-api

ActiveLibrary[API Development](/categories/api)

chrisjohnleah/velocity-fleet-api
================================

Framework-agnostic PHP SDK for the Radius Velocity Fleet Telematics API (customers + live device positions), built on Saloon.

v0.1.0(today)001MITPHPPHP ^8.3CI passing

Since Jun 9Pushed todayCompare

[ Source](https://github.com/chrisjohnleah/velocity-fleet-api)[ Packagist](https://packagist.org/packages/chrisjohnleah/velocity-fleet-api)[ Docs](https://github.com/chrisjohnleah/velocity-fleet-api)[ RSS](/packages/chrisjohnleah-velocity-fleet-api/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (6)Versions (2)Used By (1)

Velocity Fleet API — PHP SDK
============================

[](#velocity-fleet-api--php-sdk)

[![CI](https://github.com/chrisjohnleah/velocity-fleet-api/actions/workflows/ci.yml/badge.svg)](https://github.com/chrisjohnleah/velocity-fleet-api/actions/workflows/ci.yml)[![Packagist Version](https://camo.githubusercontent.com/9969da94a4598ec7d57c2791903797f761e73ebd19d071cbebf0bdb1577e3563/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f63687269736a6f686e6c6561682f76656c6f636974792d666c6565742d6170692e737667)](https://packagist.org/packages/chrisjohnleah/velocity-fleet-api)[![Total Downloads](https://camo.githubusercontent.com/de677def6308806163a2d9e6b72e9de50daf51b465c82a8d910b1cc29596881b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f63687269736a6f686e6c6561682f76656c6f636974792d666c6565742d6170692e737667)](https://packagist.org/packages/chrisjohnleah/velocity-fleet-api)[![PHP Version](https://camo.githubusercontent.com/e815b9c340bf3989da13b813c4749580a0ec8f49b9705c4f8e0522d0279640b3/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f63687269736a6f686e6c6561682f76656c6f636974792d666c6565742d6170692e737667)](https://packagist.org/packages/chrisjohnleah/velocity-fleet-api)[![License: MIT](https://camo.githubusercontent.com/7013272bd27ece47364536a221edb554cd69683b68a46fc0ee96881174c4214c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667)](LICENSE)

A modern, framework-agnostic PHP SDK for the [Radius Velocity Fleet](https://www.velocityfleet.com) Telematics API, built on [Saloon](https://docs.saloon.dev). Bearer authentication, an optional OAuth2 refresh-token flow, typed responses, and transient-error backoff — all baked in.

> **Using Laravel?** Reach for the companion bridge [`chrisjohnleah/velocity-fleet-api-laravel`](https://github.com/chrisjohnleah/velocity-fleet-api-laravel)for a service provider, config, and a persistent token store.

What it covers
--------------

[](#what-it-covers)

The Velocity Telematics API exposes a small, focused surface — and this SDK wraps all of it:

EndpointSDKList the customers linked to your user`$velocity->customers()->list()`List live device (vehicle) positions for a customer`$velocity->devicePositions()->forCustomer($id)`Requirements
------------

[](#requirements)

- PHP 8.3+
- A Velocity Fleet API token (generated in the UI), **or** a customer-issued refresh token if you're a third-party integration.

Installation
------------

[](#installation)

```
composer require chrisjohnleah/velocity-fleet-api
```

Quick start
-----------

[](#quick-start)

### With an API token (existing customers)

[](#with-an-api-token-existing-customers)

Generate a token in the Velocity UI under **Account → Account Settings → API Integrations → Create API Token**, then:

```
use ChrisJohnLeah\VelocityFleet\VelocityFleet;

$velocity = VelocityFleet::withToken(getenv('VELOCITY_API_TOKEN'));

// Every customer linked to your user — typed.
foreach ($velocity->customers()->list() as $customer) {
    printf("%s (#%s) — %s\n", $customer->name, $customer->number, $customer->product);
}
```

### With a refresh token (third-party integrations)

[](#with-a-refresh-token-third-party-integrations)

Your customer supplies a **Refresh Token**. The SDK exchanges it for a short-lived access token on first use (standard OAuth2 `refresh_token` grant), and refreshes again whenever a call comes back unauthorised:

```
use ChrisJohnLeah\VelocityFleet\VelocityFleet;

$velocity = VelocityFleet::withRefreshToken(
    refreshToken: getenv('VELOCITY_REFRESH_TOKEN'),
    clientId: getenv('VELOCITY_CLIENT_ID'),         // if your OAuth client requires it
    clientSecret: getenv('VELOCITY_CLIENT_SECRET'),
);
```

Reading device positions
------------------------

[](#reading-device-positions)

```
$positions = $velocity->devicePositions()->forCustomer($customer->id);

echo "{$positions->deviceCount} devices\n";

foreach ($positions->devices as $device) {
    printf(
        "%s @ %.5f,%.5f — %d %s, ignition %s, seen %s\n",
        $device->vehicleRegistration,
        $device->lat ?? 0.0,
        $device->lon ?? 0.0,
        $device->speed ?? 0,
        $device->speedMeasureText ?? '',
        $device->ignitionOn() ? 'on' : 'off',
        $device->occurredAt()?->format('H:i') ?? 'n/a',
    );
}

// The same devices are also grouped:
foreach ($positions->deviceGroups as $group) {
    echo "{$group->name}: ".count($group->devices)." devices\n";
}
```

> **Use the right id.** The customers response is keyed by each customer's **unique id** — exposed as `Customer::$id`. Pass *that* to `forCustomer()`, not the human-facing `Customer::$number`.

Persisting tokens
-----------------

[](#persisting-tokens)

When you use the refresh-token flow, implement [`Contracts\TokenStore`](src/Contracts/TokenStore.php) to keep the rotated token between requests (the in-memory `ArrayTokenStore` only lives for the current process). The token endpoint may rotate the refresh token, so your `put()` must always overwrite the previous record:

```
use ChrisJohnLeah\VelocityFleet\Auth\StoredToken;
use ChrisJohnLeah\VelocityFleet\Contracts\TokenStore;
use ChrisJohnLeah\VelocityFleet\VelocityFleet;
use ChrisJohnLeah\VelocityFleet\VelocityFleetConnector;

final class MyTokenStore implements TokenStore
{
    public function get(): ?StoredToken { /* load access/refresh/expiresAt */ }
    public function put(StoredToken $token): void { /* overwrite */ }
    public function forget(): void { /* delete */ }
}

$velocity = new VelocityFleet(
    new VelocityFleetConnector(clientId: '…', clientSecret: '…'),
    new MyTokenStore(),
);
```

Errors
------

[](#errors)

Failures surface as typed exceptions, all extending `Exceptions\VelocityFleetException`:

ExceptionWhen`NotConnectedException`No token available (and none could be obtained)`AuthenticationException``401` / `403` after a refresh attempt — re-authorise`ApiException`Any other API error or transport failure (carries `->status`, `->body`, `->headers`, `header()`, and `retryAfter()`)```
use ChrisJohnLeah\VelocityFleet\Exceptions\ApiException;

try {
    $velocity->devicePositions()->forCustomer($id);
} catch (ApiException $e) {
    report("Velocity API {$e->status}: {$e->getMessage()}");
}
```

A note on authentication details
--------------------------------

[](#a-note-on-authentication-details)

The Velocity API is a Django REST Framework service using Bearer (SimpleJWT) access tokens, with token issuance via an OAuth2 endpoint (django-oauth-toolkit). The third-party refresh-token exchange isn't part of the public reference, so the SDK targets the standard OAuth2 `refresh_token` grant at `https://www.velocityfleet.com/o/token/` by default. If your integration documents a different token endpoint or client-authentication requirement, pass it through `VelocityFleetConnector` (`tokenEndpoint`, `clientId`, `clientSecret`) — no code changes needed.

Sending raw requests
--------------------

[](#sending-raw-requests)

Anything not yet wrapped in a resource can be sent through the client, which still applies auth, refresh-on-401, and typed error handling:

```
use ChrisJohnLeah\VelocityFleet\Requests\Customers\GetCustomers;

$customers = $velocity->send(new GetCustomers())->dto();
```

Testing
-------

[](#testing)

```
composer test      # Pest
composer analyse   # PHPStan (max)
composer lint      # Pint --test
composer check     # all three
```

Tests never hit the network — every request is faked with Saloon's `MockClient`.

Contributing
------------

[](#contributing)

Issues and PRs welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). Please report security issues privately per [SECURITY.md](SECURITY.md).

Licence
-------

[](#licence)

MIT © [Chris John Leah](https://github.com/chrisjohnleah). See [LICENSE](LICENSE).

> Not affiliated with or endorsed by Radius or Velocity Fleet. "Radius", "Velocity" and "Kinesis" are trademarks of their respective owners.

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance100

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity38

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

0d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/ec2ce54c81968f6fc293587599bbaaa4221e303eec07cd2a50de1d99a6f0f2a4?d=identicon)[chrisjohnleah](/maintainers/chrisjohnleah)

---

Top Contributors

[![chrisjohnleah](https://avatars.githubusercontent.com/u/959104?v=4)](https://github.com/chrisjohnleah "chrisjohnleah (4 commits)")

---

Tags

apisdksaloonKinesisradiusfleetvelocitytelematicsvelocity-fleet

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/chrisjohnleah-velocity-fleet-api/health.svg)

```
[![Health](https://phpackages.com/badges/chrisjohnleah-velocity-fleet-api/health.svg)](https://phpackages.com/packages/chrisjohnleah-velocity-fleet-api)
```

###  Alternatives

[saloonphp/saloon

Build beautiful API integrations and SDKs with Saloon

2.4k11.0M641](/packages/saloonphp-saloon)[saloonphp/laravel-plugin

The official Laravel plugin for Saloon

806.6M184](/packages/saloonphp-laravel-plugin)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
