Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Simpro Php Sdk Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

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.

Implementation Patterns

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.

Gotchas and Tips

  • Field casing is the big one. Simpro uses PascalCase, and it's not always consistent (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.
  • Token refresh races. Tokens are short-lived and refresh automatically, but multiple queue workers hitting an expired token at once can race. Serialize refreshes with a cache lock or a single dedicated refresh job.
  • Base URL per tenant. Don't hardcode a shared host or reuse credentials across tenants. Sandbox and production URLs are different — a misconfigured base URL usually surfaces as a confusing 404 rather than an auth error.
  • Date formats. Simpro is picky: some fields want Y-m-d, others a full timestamp. Don't assume Carbon's default serialization is accepted; format explicitly per field.
  • Rate limits. Handle 429s with backoff and retries at the HTTP layer rather than retrying the whole job. Long all() loops are the usual trigger.
  • Error shape. Non-2xx responses throw. Catch at your service boundary and map to domain exceptions, but log the response body — Simpro's error payload carries the actual validation detail you need for writes.
  • Debugging casing issues. Enable HTTP logging/debug middleware in local and staging. When a field "disappears" or a write silently ignores a value, the first thing to check is the exact casing in the outgoing payload versus the API docs.
  • Extension points. Add bespoke endpoints by extending the connector and registering your own request classes — don't patch vendor code, or you'll lose the fix stream (1.1.23 is a good example of a fix you want to inherit cleanly).
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment