PHPackages                             stitch-digital/uptick-php-sdk - 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. stitch-digital/uptick-php-sdk

ActiveLibrary[API Development](/categories/api)

stitch-digital/uptick-php-sdk
=============================

An SDK to easily work with the Uptick API

0.0.6(2mo ago)010MITPHPPHP ^8.2CI passing

Since Jan 20Pushed 2mo agoCompare

[ Source](https://github.com/stitch-digital/uptick-php-sdk)[ Packagist](https://packagist.org/packages/stitch-digital/uptick-php-sdk)[ Docs](https://github.com/stitch-digital/uptick-php-sdk)[ RSS](/packages/stitch-digital-uptick-php-sdk/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (5)Dependencies (6)Versions (10)Used By (0)

Uptick PHP SDK
==============

[](#uptick-php-sdk)

[![Latest Version on Packagist](https://camo.githubusercontent.com/54fd5b030514f685011e74bca248047369e200b9cf289fe9bdadc1414bce0aed/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7374697463682d6469676974616c2f75707469636b2d7068702d73646b2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/stitch-digital/uptick-php-sdk)[![Total Downloads](https://camo.githubusercontent.com/a084e9f179cb36bc648212a4da3e906583a31138caf89ea6824c7c6680825075/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f7374697463682d6469676974616c2f75707469636b2d7068702d73646b2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/stitch-digital/uptick-php-sdk)

This package is an unofficial PHP SDK for the Uptick API, built with [Saloon](https://docs.saloon.dev/) v3.

⚠️ **Active Development Notice**

This package is under active development. The API, features, and internal structure may change at any time.

A stable, production-ready release has **not** yet been tagged. Until a `v1.0.0` (or similar) release is published, this SDK should be considered **experimental** and used with caution in production environments.

 [ ![Uptick Logo](uptick-logo.webp) ](https://www.uptickhq.com/)

About Uptick
------------

[](#about-uptick)

[Uptick](https://www.uptickhq.com/) is a cloud-based field service and compliance platform designed specifically for fire protection, security, HVAC and related maintenance businesses, helping them manage inspections, scheduling, asset maintenance and reporting in one place. It replaces paper and manual processes with a mobile-friendly system that lets technicians capture data, photos and defect details on site while office teams handle job scheduling, quoting, invoicing and customer communication more efficiently, all backed by built-in standards and integrations with tools like accounting software.

- **Website:**
- **GitHub:**

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

[](#installation)

```
composer require stitch-digital/uptick-php-sdk
```

Quick Start
-----------

[](#quick-start)

```
use Uptick\PhpSdk\Uptick\Uptick;

// Create an SDK instance
$uptick = Uptick::make(
    baseUrl: 'https://your-company.onuptick.com/api/v2/',
    username: 'your_username',
    password: 'your_password',
    clientId: 'your_client_id',
    clientSecret: 'your_client_secret'
);

// List clients (paginated)
$paginator = $uptick->clients()->list();

// Iterate over all clients
foreach ($paginator->items() as $client) {
    $clientId = $client->id;
    $clientName = $client->attributes->name;
}

// Or collect all items into an array
$allClients = $paginator->collect()->all();
```

Behind the scenes, the SDK uses [Saloon](https://docs.saloon.dev) to make the HTTP requests.

Usage
-----

[](#usage)

### Authentication

[](#authentication)

The SDK handles OAuth2 authentication automatically. You provide your credentials when creating the SDK instance:

```
$uptick = Uptick::make(
    baseUrl: 'https://your-company.onuptick.com/api/v2/',
    username: 'your_username',
    password: 'your_password',
    clientId: 'your_client_id',
    clientSecret: 'your_client_secret'
);
```

The SDK will automatically:

- Obtain an access token using the password grant
- Refresh the token when it expires
- Include the token in all API requests

### Setting a timeout

[](#setting-a-timeout)

By default, the SDK waits 10 seconds for a response. Override via the constructor:

```
$uptick = new Uptick(
    baseUrl: 'https://your-company.onuptick.com/api/v2/',
    username: 'your_username',
    password: 'your_password',
    clientId: 'your_client_id',
    clientSecret: 'your_client_secret',
    requestTimeout: 30
);
```

### Handling errors

[](#handling-errors)

The SDK uses Saloon's `AlwaysThrowOnErrors` trait on the connector, which means exceptions will automatically be thrown whenever a request fails (4xx or 5xx response status codes). You don't need to manually check if a request failed or call `throw()` on responses - exceptions are thrown automatically.

Saloon's built-in exceptions are used for most errors, with a custom exception for validation errors (422 status codes).

#### Exception Hierarchy

[](#exception-hierarchy)

The SDK uses Saloon's exception hierarchy:

```
SaloonException
├── FatalRequestException (Connection Errors)
└── RequestException (Request Errors)
    ├── ServerException (5xx)
    │   ├── InternalServerErrorException (500)
    │   ├── ServiceUnavailableException (503)
    │   └── GatewayTimeoutException (504)
    └── ClientException (4xx)
        ├── UnauthorizedException (401)
        ├── PaymentRequiredException (402)
        ├── ForbiddenException (403)
        ├── NotFoundException (404)
        ├── MethodNotAllowedException (405)
        ├── RequestTimeOutException (408)
        ├── UnprocessableEntityException (422)
        │   └── ValidationException (422 - Custom)
        └── TooManyRequestsException (429)

```

#### Validation Errors

[](#validation-errors)

For validation errors (422 status code), the SDK throws a custom `ValidationException` which extends Saloon's `UnprocessableEntityException`. This exception provides additional methods to access validation error details:

```
use Saloon\Exceptions\Request\ClientException;
use Saloon\Exceptions\Request\ServerException;
use Uptick\PhpSdk\Uptick\Exceptions\ValidationException;

try {
    $paginator = $uptick->clients()->list();
} catch (ValidationException $exception) {
    // Get a string describing all errors
    $message = $exception->getMessage();

    // Get all validation errors as an array
    $errors = $exception->getErrors();
    // ['field_name' => ['Error message 1', 'Error message 2']]

    // Get errors for a specific field
    $fieldErrors = $exception->getErrorsForField('field_name');

    // Check if a specific field has errors
    $hasErrors = $exception->hasErrorsForField('field_name');

    // Get all error messages as a flat array
    $allMessages = $exception->getAllErrorMessages();

    // Access the Saloon Response object for debugging
    $response = $exception->getResponse();
} catch (ClientException $exception) {
    // Handle 4xx errors (401, 403, 404, etc.)
    $message = $exception->getMessage();
    $response = $exception->getResponse();
} catch (ServerException $exception) {
    // Handle 5xx errors
    $message = $exception->getMessage();
    $response = $exception->getResponse();
}
```

#### Connection Errors

[](#connection-errors)

If Saloon cannot connect to the API, it will throw a `FatalRequestException`:

```
use Saloon\Exceptions\Request\FatalRequestException;

try {
    $paginator = $uptick->clients()->list();
} catch (FatalRequestException $exception) {
    // Handle connection errors (network issues, DNS failures, etc.)
    $message = $exception->getMessage();
}
```

Resources
---------

[](#resources)

The SDK provides resource-based APIs for working with different entities. Each resource is accessed through a method on the main SDK instance.

### Client Resource

[](#client-resource)

The client resource provides methods for working with clients. Access it through the `clients()` method:

```
$uptick = Uptick::make(...);
$clients = $uptick->clients();
```

Methods that return lists of clients (like `list()`, `findById()`, etc.) return a `UptickPaginator` instance. See the [Pagination](#pagination) section for details on working with paginated results. Future methods like `create()`, `update()`, and `delete()` will return different response types.

#### Listing Clients

[](#listing-clients)

The following methods return a `UptickPaginator` instance:

**List all clients:**

```
$paginator = $uptick->clients()->list();
```

**List active clients:**

```
$paginator = $uptick->clients()->listActive();
```

**List inactive clients:**

```
$paginator = $uptick->clients()->listInactive();
```

#### Finding Clients

[](#finding-clients)

**Find by ID:**

```
$paginator = $uptick->clients()->findById(123);
```

**Find by name:**

```
// Exact match
$paginator = $uptick->clients()->findByName('Acme Corp', strict: true);

// Contains match (default)
$paginator = $uptick->clients()->findByName('Acme');
```

**Search clients:**

```
// Search across multiple fields
$paginator = $uptick->clients()->search('construction');
```

#### Filtering Clients

[](#filtering-clients)

The client resource provides many filtering methods. All filtering methods return a `UptickPaginator` instance. You can chain multiple filters together, or pass filters directly to the `list()` method for better performance.

**Filtering by Account Manager:**

```
// Single or multiple account manager IDs
$paginator = $uptick->clients()->whereAccountManager(5);
$paginator = $uptick->clients()->whereAccountManager([5, 10, 15]);

// Exclude account managers
$paginator = $uptick->clients()->whereNotAccountManager(5);
```

**Filtering by Billing Card:**

```
$paginator = $uptick->clients()->whereBillingCard(3);
$paginator = $uptick->clients()->whereBillingCard([3, 7]);
$paginator = $uptick->clients()->whereNotBillingCard(3);
```

**Filtering by Date:**

```
// Created dates
$paginator = $uptick->clients()->createdBefore('2024-01-01T00:00:00Z');
$paginator = $uptick->clients()->createdAfter('2024-01-01T00:00:00Z');

// Updated dates
$paginator = $uptick->clients()->updatedBefore('2024-01-01T00:00:00Z');
$paginator = $uptick->clients()->updatedAfter('2024-01-01T00:00:00Z');
$paginator = $uptick->clients()->updatedSince('2024-01-01T00:00:00Z'); // alias
```

**Filtering by Sector:**

```
use Uptick\PhpSdk\Uptick\Data\Clients\Sector;

// Using Sector enum
$paginator = $uptick->clients()->whereSector(Sector::Construction);
$paginator = $uptick->clients()->whereSector([Sector::Construction, Sector::RetailTrade]);

// Using string (backward compatibility)
$paginator = $uptick->clients()->whereSector('Construction');

// Exclude sectors
$paginator = $uptick->clients()->whereNotSector(Sector::Construction);
```

**Filtering by Price Tier:**

```
$paginator = $uptick->clients()->wherePriceTier(2);
$paginator = $uptick->clients()->wherePriceTier([2, 4, 6]);
$paginator = $uptick->clients()->whereNotPriceTier(2);
```

**Filtering by Active Status:**

```
$paginator = $uptick->clients()->whereIsActive(true);
$paginator = $uptick->clients()->whereIsActive(false);
```

**Filtering by Report Settings:**

```
$paginator = $uptick->clients()->whereReportWhitelabel(true);
$paginator = $uptick->clients()->whereReportManual(true);
```

**Filtering by Billing Settings:**

```
$paginator = $uptick->clients()->whereBillingManual(true);
$paginator = $uptick->clients()->whereBillingFixedPrice(true);
```

**Filtering by Quoting Settings:**

```
$paginator = $uptick->clients()->whereQuotingAutoRemindersEnabled(true);
```

**Filtering by Properties:**

```
$paginator = $uptick->clients()->whereHasProperties(true);
$paginator = $uptick->clients()->whereHasActiveProperty(true);
```

**Filtering by Duplicate Status:**

```
$paginator = $uptick->clients()->whereIsDuplicated(true);
```

**Filtering by Client Group:**

```
$paginator = $uptick->clients()->whereParentClientGroup(10);
$paginator = $uptick->clients()->whereClientGroup(10);
$paginator = $uptick->clients()->whereNotClientGroup(10);
```

**Filtering by Branch:**

```
$paginator = $uptick->clients()->whereBranch(5);
$paginator = $uptick->clients()->whereBranch([5, 10]);
$paginator = $uptick->clients()->whereNotBranch(5);
```

**Filtering by Account:**

```
$paginator = $uptick->clients()->whereHasAccount(true);
```

**Filtering by Tags:**

```
$paginator = $uptick->clients()->whereTags(1);
$paginator = $uptick->clients()->whereTags([1, 2, 3]);
$paginator = $uptick->clients()->whereNotTags(1);
```

**Filtering by Business Hours:**

```
$paginator = $uptick->clients()->whereHasBusinessHours(true);
```

**Filtering by Phone Number:**

```
$paginator = $uptick->clients()->wherePhoneNumberContains('555');
```

**Filtering by Extra Fields:**

```
$paginator = $uptick->clients()->whereExtraFields([
    'custom_field_1' => 'value1',
    'custom_field_2' => 'value2',
]);
```

**Custom Filters:**

You can pass custom filters directly to the `list()` method for better performance when using multiple filters:

```
$paginator = $uptick->clients()->list([
    'is_active' => true,
    'sector' => Sector::Construction->value,
    'account_manager' => [5, 10],
    'created_after' => '2024-01-01T00:00:00Z',
]);
```

**Chaining Filters:**

You can also chain filter methods together, though using `list()` with an array is more efficient:

```
$paginator = $uptick->clients()
    ->whereIsActive(true)
    ->whereSector(Sector::Construction)
    ->whereAccountManager(5)
    ->createdAfter('2024-01-01T00:00:00Z');
```

#### Client Object Structure

[](#client-object-structure)

Each client returned from listing methods is a `Client` DTO with the following structure:

```
$client->id;                    // string - Client ID
$client->type;                  // string - Always "Client"
$client->attributes->name;       // string|null - Client name
$client->attributes->isActive;   // bool|null - Active status
$client->attributes->sector;     // Sector|null - Sector enum
$client->attributes->created;    // DateTimeImmutable|null - Creation date
$client->attributes->updated;    // DateTimeImmutable|null - Last update date
$client->attributes->ref;        // string|null - Reference number
$client->attributes->contactName; // string|null - Contact name
$client->attributes->contactEmail; // string|null - Contact email
// ... and many more properties (see ClientAttributes class)
$client->relationships;         // array - Related resources
```

Pagination
----------

[](#pagination)

Many resource methods return a `UptickPaginator` instance for working with paginated API responses. The SDK uses Saloon's pagination plugin to handle pagination automatically. The Uptick API uses limit/offset pagination, where results are divided into pages. The SDK's paginator handles all of this for you, allowing you to iterate through every result across every page in one loop.

The paginator is a custom PHP iterator, meaning it can be used in foreach loops. It's also memory efficient - it only keeps one page in memory at a time, so you can iterate through thousands of pages and millions of results without running out of memory.

### Iterating Over Items

[](#iterating-over-items)

The simplest way to use the paginator is to iterate over items using the `items()` method. This will give you each item across multiple pages:

```
$paginator = $uptick->clients()->list();

foreach ($paginator->items() as $client) {
    $clientId = $client->id;
    $clientName = $client->attributes->name;
    $isActive = $client->attributes->isActive;
    $sector = $client->attributes->sector?->value;
    $createdAt = $client->attributes->created?->format('Y-m-d H:i:s');
}
```

### Using Laravel Collections

[](#using-laravel-collections)

If you're using Laravel (or have `illuminate/collections` installed), you can use the `collect()` method to get a `LazyCollection`. This allows you to use powerful collection methods like `filter()`, `map()`, `sort()`, and more while keeping memory consumption low:

```
use Uptick\PhpSdk\Uptick\Data\Clients\Sector;

$paginator = $uptick->clients()->list();
$collection = $paginator->collect();

$activeConstructionClients = $collection
    ->filter(function ($client) {
        return $client->attributes->isActive === true
            && $client->attributes->sector === Sector::Construction;
    })
    ->map(function ($client) {
        return [
            'id' => $client->id,
            'name' => $client->attributes->name,
            'sector' => $client->attributes->sector?->value,
        ];
    })
    ->sortBy('name');

foreach ($activeConstructionClients as $client) {
    $clientName = $client['name'];
}
```

### Collecting All Items

[](#collecting-all-items)

You can collect all items into an array if you need to work with the complete dataset:

```
$paginator = $uptick->clients()->list();
$allClients = $paginator->collect()->all();

// Now you have an array of all clients
$clientCount = count($allClients);
$firstClient = $allClients[0];
```

### Accessing Pagination Metadata

[](#accessing-pagination-metadata)

The paginator provides methods to access pagination information:

```
$paginator = $uptick->clients()->list();

// Get the total number of pages
$totalPages = $paginator->getTotalPages();

// Get the first item without iterating
$firstClient = $paginator->items()->current();
```

### Controlling Page Size

[](#controlling-page-size)

By default, 50 items are fetched per page. You can control pagination by setting the per-page limit:

```
$paginator = $uptick->clients()->list();
$paginator->setPerPageLimit(100); // Fetch 100 items per page

foreach ($paginator->items() as $client) {
    $clientName = $client->attributes->name;
}
```

The paginator will automatically handle fetching subsequent pages as you iterate through the results. You don't need to worry about managing page numbers or offsets - just iterate and the SDK handles the rest.

Using Saloon requests directly
------------------------------

[](#using-saloon-requests-directly)

You can use the request classes directly for full control:

```
use Uptick\PhpSdk\Uptick\Uptick;
use Uptick\PhpSdk\Uptick\Requests\Clients\ListClientsRequest;

$uptick = Uptick::make(...);
$request = new ListClientsRequest();

$response = $uptick->send($request)->dto();
```

Security
--------

[](#security)

If you discover any security related issues, please email  instead of using the issue tracker.

Credits
-------

[](#credits)

- [Stitch Digital](https://www.stitch-digital.com)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE) for more information.

###  Health Score

36

—

LowBetter than 82% of packages

Maintenance84

Actively maintained with recent releases

Popularity5

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity42

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 89.5% 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

Every ~6 days

Total

6

Last Release

80d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/1afc3eb4b0906148c94893fc2c76a6832d3c5f6499f6f9ab223ec6d945ee95da?d=identicon)[johntrickett](/maintainers/johntrickett)

---

Top Contributors

[![johntrickett86](https://avatars.githubusercontent.com/u/149476912?v=4)](https://github.com/johntrickett86 "johntrickett86 (17 commits)")[![claude](https://avatars.githubusercontent.com/u/81847?v=4)](https://github.com/claude "claude (2 commits)")

---

Tags

apisdksaloonuptick

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/stitch-digital-uptick-php-sdk/health.svg)

```
[![Health](https://phpackages.com/badges/stitch-digital-uptick-php-sdk/health.svg)](https://phpackages.com/packages/stitch-digital-uptick-php-sdk)
```

###  Alternatives

[saloonphp/saloon

Build beautiful API integrations and SDKs with Saloon

2.4k9.6M468](/packages/saloonphp-saloon)[saloonphp/laravel-plugin

The official Laravel plugin for Saloon

765.7M125](/packages/saloonphp-laravel-plugin)[ohdearapp/ohdear-php-sdk

An SDK to easily work with the Oh Dear API

742.6M13](/packages/ohdearapp-ohdear-php-sdk)

PHPackages © 2026

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