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

Pipedrive Laravel Package

devio/pipedrive

A Laravel/PHP client for the Pipedrive CRM API, offering an expressive, framework-friendly way to work with deals, leads, persons, organizations, activities, and other Pipedrive resources.

View on GitHub
Deep Wiki
Context7

Getting Started

Install (pin 2.17.0+ if you're on PHP 8.3/8.4):

composer require devio/pipedrive

2.17.0 only clears deprecation warnings — no API changes — but it's the release you want if your logs are full of E_DEPRECATED noise from the package's signatures. Anything below it still works; it just complains on modern PHP.

Laravel (5.5+ auto-discovers the provider/facade). Publish config:

php artisan vendor:publish --provider="Devio\Pipedrive\PipedriveServiceProvider"
PIPEDRIVE_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Standalone PHP:

use Devio\Pipedrive\Pipedrive;

$pipedrive = Pipedrive::make(env('PIPEDRIVE_TOKEN'));

First call — the 30-second validation loop:

$response = $pipedrive->deals->all(['status' => 'open', 'limit' => 10]);

if ($response->isSuccess()) {
    foreach ($response->getData() as $deal) {
        echo $deal->id . ' — ' . $deal->title . PHP_EOL;
    }
}

Where to look first (reading the source beats reading the README):

  1. src/Pipedrive.php — the factory and the list of supported resources (deals, persons, organizations, leads, activities, notes, products, pipelines, stages, users, files, webhooks, filters, *Fields, goals, roles, activityTypes). If a resource is listed here, the rest is trivial.
  2. src/Resources/Resource.php — every resource is a thin wrapper. all(), find($id), add($data), update($id, $data), delete($id) are the contract; anything past that (search, deals->products, deals->followers) is resource-specific.
  3. src/Http/Response.php (or PipedriveResponse) — how to read getData(), getStatusCode(), and the raw additional_data block. You will use this class more than any other.
  4. src/Resources/*.php — skim the few you actually need. There are no hidden abstractions; each method is ~5 lines mapping to a URL.

First real use case: a lead-intake job that creates a Person, an Organization, then a Deal linked via person_id/org_id, plus a Note. That sequence exercises the four resources you'll touch 90% of the time.

Implementation Patterns

Resource-as-property (or facade method) style. Resources are resolved lazily through __get, so $pipedrive->deals->add([...]) and Pipedrive::deals()->add([...]) are equivalent. Wrap the client once and inject it — don't call Pipedrive::make() in controllers.

Service layer / Laravel container binding:

// AppServiceProvider
$this->app->singleton(\Devio\Pipedrive\Pipedrive::class, fn () =>
    \Devio\Pipedrive\Pipedrive::make(config('pipedrive.token'))
);

Then type-hint Pipedrive in a CrmService and mock it in tests.

Create-or-update via find + update. There is no upsert. The common pattern:

$existing = $pipedrive->persons->find($id);
$pipedrive->persons->update($id, ['email' => $email]);
// vs
$pipedrive->persons->add(['name' => $name, 'email' => $email]);

Wrap it in your own repository method so idempotent syncs are possible — the API otherwise happily creates duplicates.

Custom fields. Pipedrive stores custom data under hashed keys (abc123..._value). Fetch the mapping once and cache it:

$map = collect($pipedrive->dealFields->all()->getData())
    ->keyBy('name')            // or 'field_code'
    ->map->key;                // the hash you send

$pipedrive->deals->add([
    $map['Lead Source'] => 'Website',
]);

Cache $map for minutes, not per-request — it's an extra API call every time.

Manual pagination. all() returns one page. Loop using additional_data.pagination:

$start = 0;
do {
    $res = $pipedrive->deals->all(['start' => $start, 'limit' => 100]);
    $deals = array_merge($deals, $res->getData() ?? []);
    $pagination = $res->getData()['additional_data']['pagination'] ?? null;
    $start = $pagination['next_start'] ?? null;
} while ($pagination && ($pagination['more_items_in_collection'] ?? false));

(Adjust: on this package getData() typically returns only the resource array; the wrapper exposes the full payload via its getContent()/additional-data accessor — check src/Http/Response.php in your pinned version.)

Webhook receiver as the integration point. Register with $pipedrive->webhooks->add(['subscription_url' => ..., 'event_action' => '*', 'event_object' => 'deal']), and in your controller always return response('', 200) before queuing work; Pipedrive retries on non-2xx.

Queue everything outbound. A CreateDealJob per write, and a scheduled SyncDealsCommand per read. This package has no retry/backoff, so jobs are your rate-limit strategy.

Batch writes. No bulk endpoint in the wrapper. Chunk your payloads (e.g., collect($rows)->chunk(50)) and usleep(250_000) between calls to stay under the 80-requests-per-2-seconds ceiling.

Gotchas and Tips

  • Deprecation warnings (2.17.0). The release removes them; there were no public API or signature changes, so upgrading is a composer update and nothing else. If you'd been filtering E_DEPRECATED for this package in phpunit.xml, a custom error handler, or error_reporting(), you can drop that workaround now — and if you were suppressing deprecations globally to hide this package's noise, 2.17.0 lets you tighten error reporting again and catch your own deprecations.
  • Rate limits are on you. Pipedrive enforces ~80 req/2s per token. This client throws on 429 (Guzzle exception) and does not retry. Add a Guzzle middleware or a Retry-style job wrapper if you scrape or bulk-import.
  • isSuccess() is mandatory. getData() returns null on a 4xx/5xx rather than throwing at the resource layer. Any foreach ($res->getData() as ...) without a success check will emit a warning or throw on null.
  • API version drift. The package targets Pipedrive v1. Legacy search endpoints (/deals/search, /persons/search) are deprecated in favor of /items/search and the v2 search APIs, which this client does not wrap. Test any search() call against your account before relying on it.
  • Company-domain base URL. Default is api.pipedrive.com; some accounts/regions need a custom base (Pipedrive::make($token, $baseUrl)). If you get cryptic 404s on every call, check the base URL and token — a revoked token returns 401 with a valid-looking JSON body.
  • OAuth is not supported. The client is token-only. For marketplace/OAuth apps you must either swap tokens into Pipedrive::make() per request or use a different SDK.
  • files->upload() is multipart. It does not go through the JSON path; it expects a local file path/CURLFile-style structure. Don't pass a URL and expect it to fetch.
  • delete returns a boolean-ish payload, not the deleted entity — don't try to ->getData()->id afterward.
  • Numeric IDs come back as integers but are sometimes strings depending on the endpoint. Cast explicitly when matching against your DB.
  • Custom field hashes differ per account. Never hard-code them across environments; always resolve via *Fields at startup.
  • all() default limits. Without ['limit' => 100] you'll silently under-read on large accounts, and without pagination you'll silently drop the tail.
  • No exception type for "Pipedrive is down." Guzzle's ConnectException bubbles up. Catch \GuzzleHttp\Exception\GuzzleException at the service-layer boundary and translate to your own domain exception.
  • Testing. Since Pipedrive::make() accepts a token and the resource layer only uses Guzzle, you can point it at a local mock server (e.g., https://localhost:8080) or bind a fake client. There's no built-in HTTP fake — build one in your test base class rather than mocking every resource individually.
  • Extension point. If a resource method is missing, don't fork the package: create a small class with the same Guzzle client, or call $pipedrive->getClient()->request(...) if that accessor is exposed in your pinned version. New Pipedrive endpoints appear faster than this repo's release cadence (2.17.0 is a maintenance release, so don't expect v2 API coverage here).
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource