ramiroestrella / sendgrid-campaigns-sdk
A PHP SDK for managing SendGrid single send campaigns, including creation, scheduling, and analytics.
Package info
github.com/balance3840/sendgrid-campaigns-php-sdk
pkg:composer/ramiroestrella/sendgrid-campaigns-sdk
Requires
- guzzlehttp/guzzle: ^7.10
This package is auto-updated.
Last update: 2026-03-12 19:21:05 UTC
README
A comprehensive PHP SDK for interacting with SendGrid's Marketing Campaigns API. This library provides an intuitive, object-oriented interface for managing contacts, lists, designs, single sends, and more.
Table of Contents
- Installation
- Getting Started
- Core Concepts
- Supported Entities
- Complete Workflow Example
- Error Handling
- Contributing
- License
Installation
Install the package via Composer:
composer require ramiroestrella/sendgrid-campaigns-sdk
Getting Started
Basic Setup
<?php require_once __DIR__ . '/vendor/autoload.php'; use SendgridCampaign\Entities\Contact\Contact; $apiKey = 'YOUR_SENDGRID_API_KEY'; $contact = new Contact(apiKey: $apiKey);
Using SendgridClient for Multiple Entities
If you're working with multiple entities, you can create a shared SendgridClient instance:
use SendgridCampaign\Clients\SendgridClient; use SendgridCampaign\Entities\Contact\Contact; use SendgridCampaign\Entities\ContactList\ContactList; $sendgridClient = SendgridClient::create( apiKey: 'YOUR_SENDGRID_API_KEY', onBehalfOf: 'subuser-name' // Optional: for subuser operations ); $contact = new Contact(sendgridClient: $sendgridClient); $contactList = new ContactList(sendgridClient: $sendgridClient);
Core Concepts
DTOs (Data Transfer Objects)
All data is represented using DTOs that extend BaseDTO. These provide type-safe data structures with automatic serialization/deserialization:
use SendgridCampaign\Entities\Contact\DTO\ContactDTO; $contact = new ContactDTO( email: 'john@example.com', first_name: 'John', last_name: 'Doe' ); // Convert to array $array = $contact->toArray(excludeNullValues: true); // Create from array $contact = ContactDTO::fromArray([ 'email' => 'john@example.com', 'first_name' => 'John' ]);
Error Handling
All methods return either the expected DTO or a BaseErrorDTO:
$result = $contact->getById('contact-id'); if ($result instanceof BaseErrorDTO) { foreach ($result->errors as $error) { echo "Error: {$error->message}\n"; } } else { echo "Contact email: {$result->email}\n"; }
Supported Entities
Contacts
The Contact entity manages contact records in SendGrid.
Get Sample Contacts
Retrieve a sample of contacts from your account:
$contact = new Contact(apiKey: $apiKey); $sample = $contact->getSample(); if (!$sample instanceof BaseErrorDTO) { foreach ($sample->result as $contactDTO) { echo "Email: {$contactDTO->email}\n"; } }
Get Contact by ID
$contact = $contact->getById('contact-id-here'); if (!$contact instanceof BaseErrorDTO) { echo "Name: {$contact->first_name} {$contact->last_name}\n"; echo "Email: {$contact->email}\n"; }
Search Contacts by Email
$results = $contact->getByEmail( emails: ['john@example.com', 'jane@example.com'], phone_number_id: null, external_id: null, anonymous_id: null ); if (!$results instanceof BaseErrorDTO) { foreach ($results as $contactDTO) { echo "Found: {$contactDTO->email}\n"; } }
Create or Update Contacts
use SendgridCampaign\Entities\Contact\DTO\ContactDTO; $contacts = [ new ContactDTO( email: 'john@example.com', first_name: 'John', last_name: 'Doe', custom_fields: [ 'company' => 'Acme Inc', 'employee_id' => 12345 ] ), new ContactDTO( email: 'jane@example.com', first_name: 'Jane', last_name: 'Smith' ) ]; $job = $contact->createOrUpdate( contacts: $contacts, listIds: ['list-id-1', 'list-id-2'] // Optional ); if (!$job instanceof BaseErrorDTO) { echo "Job ID: {$job->job_id}\n"; }
Search Contacts with Query
Use SendGrid's query language to search contacts:
$results = $contact->search( query: 'email LIKE "%@example.com" AND last_clicked >= "2024-01-01"' ); if (!$results instanceof BaseErrorDTO) { echo "Found {$results->contact_count} contacts\n"; foreach ($results->result as $contactDTO) { echo "- {$contactDTO->email}\n"; } }
Import Contacts from CSV
You can import contacts from CSV content or a file:
// From CSV string $csvContent = <<<CSV email,first_name,last_name,company john@example.com,John,Doe,Acme Inc jane@example.com,Jane,Smith,Tech Corp CSV; $import = $contact->import( fileContent: $csvContent, fileType: ContactImportFileType::CSV, listIds: ['list-id'] ); // From local file $import = $contact->importFromFile( filePath: 'contacts.csv', listIds: ['list-id'] ); // From URL $import = $contact->importFromFile( filePath: 'https://example.com/contacts.csv', listIds: ['list-id'] ); if (!$import instanceof BaseErrorDTO) { echo "Job ID: {$import->job_id}\n"; }
Check Import Status
$status = $contact->getImportStatus('job-id'); if (!$status instanceof BaseErrorDTO) { echo "Status: {$status->status}\n"; echo "Created: {$status->results?->created_count}\n"; echo "Updated: {$status->results?->updated_count}\n"; }
Get Contact Count
$count = $contact->getCount(); if (!$count instanceof BaseErrorDTO) { echo "Total contacts: {$count->contact_count}\n"; echo "Billable contacts: {$count->billable_count}\n"; }
Export Contacts
$job = $contact->export( listIds: ['list-id-1'], sendEmailNotification: true, fileType: ContactImportFileType::CSV, maxFileSizeInMb: 5000 ); if (!$job instanceof BaseErrorDTO) { echo "Export Job ID: {$job->job_id}\n"; }
Check Export Status
$status = $contact->exportStatus('job-id'); if (!$status instanceof BaseErrorDTO) { echo "Status: {$status->status->value}\n"; if ($status->urls) { foreach ($status->urls as $url) { echo "Download: {$url}\n"; } } }
Delete Contacts
// Delete specific contacts $job = $contact->delete( contactIds: ['contact-id-1', 'contact-id-2'] ); // Delete all contacts (use with caution!) $job = $contact->delete(deleteAll: true);
Delete Contact Identifier
Remove a specific identifier from a contact:
use SendgridCampaign\Entities\Contact\Enums\ContactIdentifierType; $job = $contact->deleteIndentifier( contactId: 'contact-id', identifierType: ContactIdentifierType::EXTERNALID, identifierValue: 'external-123' );
Contact Lists
The ContactList entity manages contact list operations.
Get All Lists
use SendgridCampaign\Entities\ContactList\ContactList; $contactList = new ContactList(apiKey: $apiKey); $lists = $contactList->getAll(); if (!$lists instanceof BaseErrorDTO) { foreach ($lists->result as $list) { echo "List: {$list->name} ({$list->contact_count} contacts)\n"; } }
Get List by ID
$list = $contactList->getById( listId: 'list-id', includeContactSample: true // Optional: include sample contacts ); if (!$list instanceof BaseErrorDTO) { echo "List: {$list->name}\n"; echo "Contacts: {$list->contact_count}\n"; foreach ($list->contact_sample as $contact) { echo "- {$contact->email}\n"; } }
Create List
$newList = $contactList->create('My New List'); if (!$newList instanceof BaseErrorDTO) { echo "Created list: {$newList->id}\n"; }
Update List Name
$updatedList = $contactList->update( listId: 'list-id', newName: 'Updated List Name' );
Get Contact Count in List
$count = $contactList->getContactsCount('list-id'); if (!$count instanceof BaseErrorDTO) { echo "Contacts: {$count->contact_count}\n"; echo "Billable: {$count->billable_count}\n"; }
Remove Contacts from List
$job = $contactList->removeContacts( listId: 'list-id', contactIds: ['contact-id-1', 'contact-id-2'] );
Delete List
// Delete list only $contactList->delete( listId: 'list-id', deleteContactsFromList: false ); // Delete list and its contacts $contactList->delete( listId: 'list-id', deleteContactsFromList: true );
Custom Fields
The CustomField entity manages custom field definitions.
Get All Custom Fields
use SendgridCampaign\Entities\CustomField\CustomField; $customField = new CustomField(apiKey: $apiKey); $fields = $customField->getAll(); if (!$fields instanceof BaseErrorDTO) { foreach ($fields->custom_fields as $field) { echo "Field: {$field->name} ({$field->field_type->value})\n"; } // Reserved fields are also included foreach ($fields->reserved_fields as $field) { echo "Reserved: {$field->name}\n"; } }
Create Custom Field
use SendgridCampaign\Entities\CustomField\Enums\CustomFieldType; $field = $customField->create( name: 'employee_id', fieldType: CustomFieldType::NUMBER ); if (!$field instanceof BaseErrorDTO) { echo "Created field ID: {$field->id}\n"; } // Available types: TEXT, NUMBER, DATE
Update Custom Field
$updated = $customField->update( id: 'field-id', name: 'new_field_name', fieldType: CustomFieldType::TEXT // Optional );
Delete Custom Field
$customField->delete('field-id');
Note: Custom field names must:
- Begin with a letter (A-Z) or underscore (_)
- Contain only alphanumeric characters and underscores
- Not match reserved field names (email, first_name, etc.)
Designs
The Design entity manages email design templates.
Get All Designs
use SendgridCampaign\Entities\Design\Design; $design = new Design(apiKey: $apiKey); $designs = $design->getAll( pageSize: 100, pageToken: null, summary: true ); if (!$designs instanceof BaseErrorDTO) { foreach ($designs->result as $designDTO) { echo "Design: {$designDTO->name}\n"; echo "Editor: {$designDTO->editor->value}\n"; } }
Get Design by ID
$designDetail = $design->getById('design-id'); if (!$designDetail instanceof BaseErrorDTO) { echo "Name: {$designDetail->name}\n"; echo "HTML: {$designDetail->html_content}\n"; }
Create Design
use SendgridCampaign\Entities\Design\Enums\EditorType; $newDesign = $design->create( htmlContent: '<html><body><h1>Hello {{first_name}}!</h1></body></html>', name: 'My Email Template', subject: 'Welcome Email', editor: EditorType::CODE, // or EditorType::DESIGN plainContent: 'Hello {{first_name}}!' ); if (!$newDesign instanceof BaseErrorDTO) { echo "Created design ID: {$newDesign->id}\n"; }
Duplicate Design
$duplicated = $design->duplicate( designId: 'design-id', name: 'Copy of Design', editor: EditorType::DESIGN );
Update Design
$updated = $design->update( designId: 'design-id', name: 'Updated Design Name', htmlContent: '<html>...</html>', plainContent: 'Plain text version', generatePlainContent: false, subject: 'New Subject', categories: ['newsletter', 'promotional'] );
Delete Design
$design->delete('design-id');
Single Sends
The SingleSend entity manages one-time email campaigns.
Get All Single Sends
use SendgridCampaign\Entities\SingleSend\SingleSend; $singleSend = new SingleSend(apiKey: $apiKey); $sends = $singleSend->getAll( pageSize: 50, pageToken: null ); if (!$sends instanceof BaseErrorDTO) { foreach ($sends->result as $send) { echo "Campaign: {$send->name} - Status: {$send->status->value}\n"; } }
Get Single Send by ID
$send = $singleSend->getById('single-send-id'); if (!$send instanceof BaseErrorDTO) { echo "Name: {$send->name}\n"; echo "Status: {$send->status->value}\n"; echo "Send At: {$send->send_at}\n"; }
Create Single Send
use SendgridCampaign\Entities\SingleSend\DTO\{SingleSendDTO, SendToDTO, EmailConfigDTO}; $newSend = $singleSend->create( new SingleSendDTO( name: 'Monthly Newsletter', send_to: new SendToDTO( list_ids: ['list-id-1', 'list-id-2'] // Or use: segment_ids: ['segment-id'] // Or use: all: true (send to all contacts) ), email_config: new EmailConfigDTO( subject: 'Welcome to Our Newsletter', design_id: 'design-id', sender_id: 123456, suppression_group_id: 789 ), categories: ['newsletter', 'monthly'] ) ); if (!$newSend instanceof BaseErrorDTO) { echo "Created single send ID: {$newSend->id}\n"; }
Schedule Single Send
// Schedule for specific time (ISO 8601 format) $scheduled = $singleSend->schedule( singleSendId: 'single-send-id', sendAt: '2024-12-25T10:00:00Z' ); // Send immediately $scheduled = $singleSend->schedule( singleSendId: 'single-send-id', sendAt: 'now' );
Search Single Sends
use SendgridCampaign\Entities\SingleSend\Enums\StatusType; $results = $singleSend->search( name: 'Newsletter', status: [StatusType::DRAFT, StatusType::SCHEDULED], categories: ['monthly'], pageSize: 50 );
Get All Categories
$categories = $singleSend->getAllCategories(); if (!$categories instanceof BaseErrorDTO) { foreach ($categories as $category) { echo "Category: {$category}\n"; } }
Update Single Send
$updated = $singleSend->update( singleSendId: 'single-send-id', singleSendDTO: new SingleSendDTO( name: 'Updated Campaign Name' ) );
Duplicate Single Send
$duplicate = $singleSend->duplicate( singleSendId: 'single-send-id', name: 'Copy of Campaign' );
Delete Single Send
// Delete single campaign $singleSend->delete('single-send-id'); // Bulk delete (max 50 at once) $singleSend->bulkDelete([ 'single-send-id-1', 'single-send-id-2', 'single-send-id-3' ]);
Delete Schedule
Unschedule a single send without deleting it:
$unscheduled = $singleSend->deleteSchedule('single-send-id');
Senders
The Sender entity manages sender identities.
Get All Senders
use SendgridCampaign\Entities\Sender\Sender; $sender = new Sender(apiKey: $apiKey); $senders = $sender->getAll(); if (!$senders instanceof BaseErrorDTO) { foreach ($senders as $senderDTO) { echo "Sender: {$senderDTO->nickname}\n"; echo "From: {$senderDTO->from_email}\n"; } }
Get All Verified Senders
$verified = $sender->getAllVerified( limit: 50, lastSeenID: null, id: null );
Get Sender by ID
$senderDetail = $sender->getById(123456); if (!$senderDetail instanceof BaseErrorDTO) { echo "Nickname: {$senderDetail->nickname}\n"; echo "Verified: " . ($senderDetail->verified ? 'Yes' : 'No') . "\n"; }
Unsubscribe Groups
The UnsubscribeGroup entity manages suppression groups.
Get All Unsubscribe Groups
use SendgridCampaign\Entities\UnsubscripeGroup\UnsubscribeGroup; $unsubscribeGroup = new UnsubscribeGroup(apiKey: $apiKey); $groups = $unsubscribeGroup->getAll(); if (!$groups instanceof BaseErrorDTO) { foreach ($groups as $group) { echo "Group: {$group->name}\n"; echo "Unsubscribes: {$group->unsubscribes}\n"; } }
Get Unsubscribe Group by ID
$group = $unsubscribeGroup->getById(12345); if (!$group instanceof BaseErrorDTO) { echo "Name: {$group->name}\n"; echo "Description: {$group->description}\n"; }
Create Unsubscribe Group
$newGroup = $unsubscribeGroup->create( name: 'Marketing Emails', description: 'Promotional and marketing content', isDefault: false );
Update Unsubscribe Group
$updated = $unsubscribeGroup->update( groupId: 12345, name: 'Updated Name', description: 'Updated description', isDefault: true );
Delete Unsubscribe Group
$unsubscribeGroup->delete(12345);
Stats
The Stats entity retrieves campaign statistics.
Get Stats by Single Send ID
use SendgridCampaign\Entities\Stats\Stats; use SendgridCampaign\Entities\Stats\Enums\{AggregatedByType, GroupByType}; $stats = new Stats(apiKey: $apiKey); $results = $stats->getById( singleSendId: 'single-send-id', aggregatedBy: AggregatedByType::DAY, startDate: '2024-01-01', endDate: '2024-12-31', pageSize: 100, groupBy: [GroupByType::AB_VARIATION] ); if (!$results instanceof BaseErrorDTO) { foreach ($results->result as $stat) { echo "Opens: {$stat->stats->opens}\n"; echo "Clicks: {$stat->stats->clicks}\n"; echo "Bounces: {$stat->stats->bounces}\n"; } }
Get All Stats
$allStats = $stats->getAll( singleSendIds: ['id-1', 'id-2'], pageSize: 50 );
Get Link Click Stats
use SendgridCampaign\Entities\Stats\Enums\AbPhaseType; $linkStats = $stats->getLinkStats( singleSendId: 'single-send-id', page_size: 100, group_by: [GroupByType::AB_VARIATION], ab_phase_id: AbPhaseType::SEND ); if (!$linkStats instanceof BaseErrorDTO) { echo "Total Clicks: {$linkStats->total_clicks}\n"; foreach ($linkStats->result as $link) { echo "URL: {$link->url}\n"; echo "Clicks: {$link->clicks}\n"; } }
Export Stats
$csv = $stats->export( singleSendIds: ['id-1', 'id-2'], timezone: 'America/New_York' ); if (!$csv instanceof BaseErrorDTO) { file_put_contents('stats.csv', $csv); }
Test Emails
The TestEmail entity sends test emails.
Send Test Email
use SendgridCampaign\Entities\TestEmail\TestEmail; $testEmail = new TestEmail(apiKey: $apiKey); $testEmail->send( templateId: 'd-template-id', emails: ['test@example.com', 'test2@example.com'], sender_id: 123456, suppression_group_id: 789, custom_unsubscribe_url: 'https://example.com/unsubscribe' );
Complete Workflow Example
Here's a complete example demonstrating a typical workflow:
<?php require_once __DIR__ . '/vendor/autoload.php'; use SendgridCampaign\Clients\SendgridClient; use SendgridCampaign\Entities\Contact\Contact; use SendgridCampaign\Entities\Contact\DTO\ContactDTO; use SendgridCampaign\Entities\ContactList\ContactList; use SendgridCampaign\Entities\CustomField\CustomField; use SendgridCampaign\Entities\CustomField\Enums\CustomFieldType; use SendgridCampaign\Entities\Design\Design; use SendgridCampaign\Entities\Design\Enums\EditorType; use SendgridCampaign\Entities\SingleSend\SingleSend; use SendgridCampaign\Entities\SingleSend\DTO\{SingleSendDTO, SendToDTO, EmailConfigDTO}; $apiKey = 'YOUR_SENDGRID_API_KEY'; // Create shared client $sendgridClient = SendgridClient::create(apiKey: $apiKey); // Initialize entities $contactList = new ContactList(sendgridClient: $sendgridClient); $contact = new Contact(sendgridClient: $sendgridClient); $customField = new CustomField(sendgridClient: $sendgridClient); $design = new Design(sendgridClient: $sendgridClient); $singleSend = new SingleSend(sendgridClient: $sendgridClient); // 1. Create a contact list $newList = $contactList->create('Holiday Campaign'); echo "Created list: {$newList->id}\n"; // 2. Create custom field $customField->create( name: 'loyalty_points', fieldType: CustomFieldType::NUMBER ); // 3. Add contacts $contacts = [ new ContactDTO( email: 'customer1@example.com', first_name: 'John', last_name: 'Doe', custom_fields: ['loyalty_points' => 1500] ), new ContactDTO( email: 'customer2@example.com', first_name: 'Jane', last_name: 'Smith', custom_fields: ['loyalty_points' => 2300] ) ]; $job = $contact->createOrUpdate( contacts: $contacts, listIds: [$newList->id] ); echo "Added contacts, job ID: {$job->job_id}\n"; // 4. Create email design $newDesign = $design->create( name: 'Holiday Special', subject: 'Exclusive Offer for {{first_name}}!', htmlContent: '<html><body> <h1>Hi {{first_name}},</h1> <p>You have {{loyalty_points}} points!</p> </body></html>', editor: EditorType::CODE ); echo "Created design: {$newDesign->id}\n"; // 5. Create and schedule single send $campaign = $singleSend->create( new SingleSendDTO( name: 'Holiday Campaign 2024', send_to: new SendToDTO(list_ids: [$newList->id]), email_config: new EmailConfigDTO( sender_id: YOUR_SENDER_ID, design_id: $newDesign->id, suppression_group_id: YOUR_SUPPRESSION_GROUP_ID ), categories: ['holiday', 'promotion'] ) ); echo "Created campaign: {$campaign->id}\n"; // Schedule to send tomorrow at 9 AM $scheduled = $singleSend->schedule( singleSendId: $campaign->id, sendAt: date('Y-m-d\T09:00:00\Z', strtotime('+1 day')) ); echo "Campaign scheduled for: {$scheduled->send_at}\n";
Error Handling
All API methods return either the expected DTO or a BaseErrorDTO. Always check the return type:
$result = $contact->getById('invalid-id'); if ($result instanceof BaseErrorDTO) { echo "API Error:\n"; foreach ($result->errors as $error) { echo "- Field: {$error->field}\n"; echo " Message: {$error->message}\n"; echo " Error ID: {$error->error_id}\n"; } } else { // Success - $result is ContactDTO echo "Email: {$result->email}\n"; }
Common Exceptions
MissingApiKeyException: Thrown when API key is not providedEmptyContactsException: Thrown when trying to perform operations with an empty contacts array
use SendgridCampaign\Exceptions\MissingApiKeyException; try { $contact = new Contact(); // No API key provided } catch (MissingApiKeyException $e) { echo "Error: {$e->getMessage()}\n"; }
API Reference
Key Classes
| Class | Description | File |
|---|---|---|
Contact |
Manage contacts | Contact.php |
ContactList |
Manage contact lists | ContactList.php |
CustomField |
Manage custom fields | CustomField.php |
Design |
Manage email designs | Design.php |
SingleSend |
Manage campaigns | SingleSend.php |
Sender |
Manage sender identities | Sender.php |
UnsubscribeGroup |
Manage suppression groups | UnsubscribeGroup.php |
Stats |
Get campaign statistics | Stats.php |
TestEmail |
Send test emails | TestEmail.php |
Enums
| Enum | Values | File |
|---|---|---|
ContactIdentifierType |
EMAIL, PHONENUMBERID, EXTERNALID, ANONYMOUSID | ContactIdentifierType.php |
ContactImportFileType |
CSV, JSON | ContactImportFileType.php |
CustomFieldType |
TEXT, NUMBER, DATE | CustomFieldType.php |
EditorType |
DESIGN, CODE | EditorType.php |
StatusType |
DRAFT, SCHEDULED, TRIGGERED | StatusType.php |
AbTestType |
SUBJECT, CONTENT | AbTestType.php |
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
For issues, questions, or contributions, please visit the GitHub repository.
Acknowledgments
- Built on top of GuzzleHttp
- Designed to work with SendGrid's Marketing Campaigns API