benhawker/pipedrive
PHP client for the Pipedrive CRM API. Install via Composer and use a simple fluent interface to manage persons, notes, deals, and activities, with building blocks to cover more of the API including file uploads.
Installation:
composer require benhawker/pipedrive
Add the package to your composer.json and run composer update.
Authentication:
$pipedrive = new \Benhawker\Pipedrive\Pipedrive('your_api_key_here');
First Use Case: Fetch a list of persons (contacts) to verify connectivity:
$persons = $pipedrive->persons()->get();
dd($persons['data']); // Inspect the response structure
persons, deals, notes) has CRUD methods (get(), add(), update(), delete()).get() with start and limit params for large datasets:
$pipedrive->persons()->get(['start' => 0, 'limit' => 50]);
try {
$deal = $pipedrive->deals()->get(['id' => 123]);
} catch (\Benhawker\Pipedrive\Exceptions\ApiException $e) {
Log::error($e->getMessage());
}
Data Synchronization:
get() to fetch records, then loop/update locally:
$remotePersons = $pipedrive->persons()->get();
foreach ($remotePersons['data'] as $person) {
Person::updateOrCreate(
['local_id' => $person['id']],
['name' => $person['name']]
);
}
Deal Pipeline Automation:
$openDeals = $pipedrive->deals()->get(['status' => 'won']);
foreach ($openDeals['data'] as $deal) {
Mail::to($deal['person_id'])->send(new DealWonEmail($deal));
}
Activity Tracking:
due_date:
$activity = [
'subject' => 'Follow-up Call',
'type' => 'call',
'person_id' => $personId,
'due_date' => now()->addDays(7)->format('Y-m-d'),
];
$pipedrive->activities()->add($activity);
Laravel Service Container:
Bind the client in AppServiceProvider for dependency injection:
$this->app->singleton(\Benhawker\Pipedrive\Pipedrive::class, function ($app) {
return new \Benhawker\Pipedrive\Pipedrive(config('services.pipedrive.key'));
});
Then inject via constructor:
public function __construct(private Pipedrive $pipedrive) {}
Queued Jobs: Offload API calls to queues to avoid timeouts:
SyncPipedriveData::dispatch($pipedrive)->onQueue('pipedrive');
Webhooks:
Use Pipedrive’s webhook system to push updates to your app (e.g., via Laravel’s HandleIncomingWebhook middleware).
Rate Limiting:
throttle middleware:
Route::middleware(['throttle:60,1'])->group(...);
ID Mismatches:
person_id in your DB) ≠ Pipedrive IDs. Always validate:
$pipedrivePerson = $pipedrive->persons()->get(['id' => $localPerson->pipedrive_id]);
if (empty($pipedrivePerson['data'])) {
throw new \Exception("Pipedrive ID {$localPerson->pipedrive_id} not found");
}
File Uploads:
$file = [
'file' => base64_encode(file_get_contents('path/to/file.pdf')),
'name' => 'contract.pdf',
'deal_id' => 123,
];
$pipedrive->files()->add($file);
Storage facade to handle encoding:
$file['file'] = base64_encode(Storage::disk('local')->get('file.pdf'));
Soft Deletes:
deleted flag (not Laravel’s deleted_at). Filter queries:
$activePersons = $pipedrive->persons()->get(['deleted' => 'false']);
Enable Debugging:
Set the debug flag in the constructor to log raw API responses:
$pipedrive = new \Benhawker\Pipedrive\Pipedrive('api_key', true);
Check Laravel logs for client/server response details.
Common Errors:
person_id for notes).Custom Endpoints:
class CustomPipedrive extends \Benhawker\Pipedrive\Pipedrive {
public function customDeals() {
return $this->request('GET', '/deals', ['custom_field' => 'value']);
}
}
Middleware:
$pipedrive->getMiddleware()->push(function ($request) {
$request->query->set('expand', ['person', 'user']);
});
Testing:
$mock = Mockery::mock(\Benhawker\Pipedrive\Pipedrive::class);
$mock->shouldReceive('persons->get')->andReturn(['data' => []]);
$this->app->instance(\Benhawker\Pipedrive\Pipedrive::class, $mock);
How can I help you explore Laravel packages today?