stitch-digital/simpro-php-sdk
A PHP SDK for the Simpro field service management API. Gives Laravel and PHP apps fluent, typed access to jobs, quotes, customers, sites and invoices, handling authentication and HTTP requests so you can integrate Simpro without boilerplate.
Install the package and let Laravel auto-discovery register the service provider and Simpro facade:
composer require stitch-digital/simpro-php-sdk
Publish the config (and the token migration if you're using the database token store):
php artisan vendor:publish --tag="simpro-config"
php artisan vendor:publish --tag="simpro-migrations"
php artisan migrate
Set the tenant-specific credentials in .env — Simpro base URLs are per-company, so there is no shared host:
SIMPRO_BASE_URL=https://yourcompany.simprosuite.com
SIMPRO_CLIENT_ID=...
SIMPRO_CLIENT_SECRET=...
Authenticate once (OAuth2) and let the SDK persist and refresh the token from there. Your first real use case is almost always a read against a known entity to confirm auth, base URL, and casing all line up:
use StitchDigital\Simpro\Facades\Simpro;
$customer = Simpro::customers()->find(123);
$sites = Simpro::sites()->forCustomer(123)->all();
Look first at config/simpro.php for connection/token settings, then the Simpro facade for the list of available resources, then the individual resource classes for supported filters and terminal methods.
The SDK is resource-first: you grab an accessor, chain filters, then call a terminal method.
Simpro::jobs()->find($id);
Simpro::jobs()->forCustomer($customerId)->all();
Simpro::quotes()->paginate(page: 1, perPage: 100);
Simpro::invoices()->create($payload);
Filtering — pass Simpro's own query parameters straight through (Customer, SiteID, DateModified, Search). The SDK is a thin wrapper; the Simpro API reference is the source of truth for what's available.
Column selection — use the column/display options to trim payloads on list screens. Fetching full records for a table view is the most common performance mistake.
Pagination — prefer explicit paginate() over all() on anything sizeable. Simpro caps page size (250), so "just fetch everything" loops are slow and rate-limit prone.
Incremental syncs — for nightly jobs, persist the last run timestamp and filter with DateModified. This is the standard pattern for mirroring customers/sites/jobs into a local read model.
Writes — use create()/update() with Simpro's exact field names. Since 1.1.23, quotes correctly read and request LinkedJobID using Simpro's casing, so a quoted job link can be set directly:
Simpro::quotes()->create([
'Customer' => $customerId,
'SiteID' => $siteId,
'LinkedJobID' => $jobId, // PascalCase, matching Simpro
]);
Queues — wrap syncs in jobs and keep them idempotent. Simpro writes are not transactional across resources, so a failed job should be safe to re-run.
Multi-tenant — the SDK reads config at runtime. Either config()->set() per request or build a dedicated connector instance per tenant, and scope the token store accordingly.
Testing — bind a fake connector / mocked HTTP client in the container rather than hitting the API. Avoid the facade in unit tests when you can inject the connector.
LinkedJobID, not LinkedJobId or linked_job_id). Release 1.1.23 fixes this specifically for LinkedJobID on quotes. If you carried a manual mapping workaround for linked jobs, remove it — the SDK now handles both the read and the request side.Y-m-d, others a full timestamp. Don't assume Carbon's default serialization is accepted; format explicitly per field.all() loops are the usual trigger.How can I help you explore Laravel packages today?