Official PHP client for the Email Funnel AI integration API. Connect with your project key and secret, then call typed, resource-oriented methods — no manual URLs, headers, or JSON.
- PHP 8.1+
- Zero third-party dependencies (uses
ext-curl) - Fluent, discoverable API with full endpoint coverage
composer require emailfunnelai/sdkuse EmailFunnelAi\Sdk\Client;
$client = Client::connect(
projectKey: 'pk_your_project_key',
projectSecret: 'sk_your_secret_key',
);
// The base URL defaults to https://app.emailfunnel.ai.
// Pass baseUrl: '...' only for staging, self-hosting, or local development.
// 1. Confirm your credentials
$client->validate();
// 2. Get or create a list
$list = $client->lists()->create('Newsletter signups');
// 3. Sync a contact into it
$client->contacts()
->sync([
'email' => 'jane@example.com',
'first_name' => 'Jane',
'source_type' => 'custom_saas',
])
->toList($list['id']);Every group is reached from the client and reads as resource → verb.
// Single contact
$client->contacts()->sync(['email' => 'jane@example.com', 'source_type' => 'app'])->toList($listId);
// Bulk (up to 500; the server queues automatically above 100 rows)
$client->contacts()->bulk([
['email' => 'a@example.com', 'source_type' => 'import'],
['email' => 'b@example.com', 'source_type' => 'import'],
], sourceType: 'import')->toList($listId);
// Real-time webhook event
$client->contacts()->webhook('user.created', ['email' => 'jane@example.com'])->toList($listId);$client->lists()->all();
$client->lists()->create('My leads', 'Optional description');
$client->lists()->find($listId);$client->bindings()->all();
$client->bindings()->create('custom_crm', $listId, ['sync_enabled' => true]);
$client->bindings()->find($bindingId);
$client->bindings()->update($bindingId, ['sync_enabled' => false]);
$client->bindings()->delete($bindingId);
$client->bindings()->status($bindingId, 'completed', errorsCount: 0);$client->fieldMappings()->config('custom_crm');
$client->fieldMappings()->forBinding($bindingId)->get();
$client->fieldMappings()->forBinding($bindingId)->update(['email' => 'email', 'first_name' => 'fname']);
$client->fieldMappings()->forBinding($bindingId)->reset();$client->autoTagging()->rules('custom_crm');
$client->autoTagging()->preview('custom_crm', ['email' => 'jane@example.com', 'plan' => 'enterprise']);$client->analytics()->dashboard();
$client->analytics()->heatmap(['range' => 60, 'email_type' => 'all']);
$client->analytics()->funnels();
$client->analytics()->campaigns();
$client->analytics()->forms();$client->sso()->generate('owner@example.com'); // signed login URL
$client->sso()->teamMembers();Successful calls return the unwrapped data payload as an array. Any error
response raises a typed exception:
use EmailFunnelAi\Sdk\Exceptions\ApiException;
use EmailFunnelAi\Sdk\Exceptions\TransportException;
try {
$client->contacts()->sync(['email' => 'invalid'])->toList($listId);
} catch (ApiException $e) {
$e->status; // 422
$e->errorType; // "validation_error"
$e->messages; // ['contact.email' => ['The email field is required.']]
$e->retryAfter; // set on 429 rate limits
} catch (TransportException $e) {
// network / timeout failure (no HTTP response)
}Both extend EmailFunnelAi\Sdk\Exceptions\EmailFunnelException, so you can catch
that to handle any SDK failure.
Branch on errorType for specific conditions — e.g. a contact list that has been
deactivated rejects new members with a 409 list_inactive:
try {
$client->contacts()->sync($contact)->toList($listId);
} catch (ApiException $e) {
if ($e->errorType === 'list_inactive') {
// The target list is inactive — reactivate it or pick another list.
}
}errorType |
Status | Meaning |
|---|---|---|
invalid_credentials |
401 | Missing/invalid project key or secret |
inactive_project |
403 | The connected project is inactive |
validation_error |
422 | Request body failed validation (messages set) |
invalid_email / suppressed |
422 | Email is undeliverable or suppressed |
list_inactive |
409 | Target contact list is inactive and rejects new members |
rate_limit_exceeded |
429 | 1000 req/hour cap hit (retryAfter set) |
sync_failed |
500 | Unexpected sync failure |
The client uses cURL by default. Inject any EmailFunnelAi\Sdk\Http\HttpClient
implementation (e.g. to route through a PSR-18 client or add logging):
$client = Client::connect($base, $key, $secret, http: new MyHttpClient());composer install
composer testTests mock HTTP and never touch the network. A coverage test asserts the SDK exposes every documented endpoint.
MIT © Email Funnel AI