1. Go to this page and download the library: Download nickdnk/klaviyo-php-sdk library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
nickdnk / klaviyo-php-sdk example snippets
use nickdnk\Klaviyo\APIClient;
$client = APIClient::withApiKey('pk_...'); // private API key
$client = APIClient::withAccessToken('eyJhbGci...'); // bearer token managed elsewhere, never refreshed
use nickdnk\Klaviyo\APIClient;
use nickdnk\Klaviyo\OAuthCredentials;
use nickdnk\Klaviyo\TokenExchange;
// Loaded from your own storage (database, secret store, ...).
$saved = [
'access_token' => 'eyJhbGciOiJFUzI1NiIsImtpZCI6IjAxYTA2Y...',
'refresh_token' => 'eyJhbGciOiJFUzI1NiIsImtpZCI6IjAxYTA2Y...',
'expires_at' => 1788536447,
];
$client = APIClient::withOAuth(
new OAuthCredentials($saved['access_token'], $saved['refresh_token'], $saved['expires_at']),
clientId: $_ENV['KLAVIYO_CLIENT_ID'],
clientSecret: $_ENV['KLAVIYO_CLIENT_SECRET'],
refresh: function (OAuthCredentials $current, TokenExchange $exchange): OAuthCredentials {
// Called on a 401, before the request that triggered it is retried.
// See lock rotation example below for details.
return $exchange($current);
},
);
if ($client->getCredentials()->isExpired(graceSeconds: 120)) {
$client->refreshCredentials();
}
refresh: function (OAuthCredentials $current, TokenExchange $exchange) use ($store, $lock, $accountId): OAuthCredentials {
return $lock->synchronized("klaviyo-refresh:$accountId", function () use ($current, $exchange, $store, $accountId) {
$stored = $store->load($accountId);
if ($stored->accessToken !== $current->accessToken) {
return $stored; // someone else refreshed while we waited for the lock
}
$fresh = $exchange($stored);
$store->save($accountId, $fresh);
return $fresh;
});
},
use nickdnk\Klaviyo\APIClient;
use nickdnk\Klaviyo\OAuthScope;
$clientId = $_ENV['KLAVIYO_CLIENT_ID'];
$clientSecret = $_ENV['KLAVIYO_CLIENT_SECRET'];
$redirectUri = 'https://example.com/klaviyo/callback';
// 1. Redirect the user. Keep $verifier and $state in the session.
$verifier = APIClient::generateCodeVerifier();
$state = bin2hex(random_bytes(16));
$url = APIClient::getOAuthLink($clientId, $state, $verifier, [OAuthScope::profilesRead, OAuthScope::eventsWrite], $redirectUri);
// 2. On the callback: compare $_GET['state'] with the stored $state, then exchange the code.
$credentials = APIClient::exchangeCodeForToken($clientId, $clientSecret, $_GET['code'], $verifier, $redirectUri);
// persist $credentials as shown under OAuth above
use nickdnk\Klaviyo\Filter;
use nickdnk\Klaviyo\Query;
use nickdnk\Klaviyo\Resources\Request\CreateProfile;
use nickdnk\Klaviyo\Resources\Request\PatchProfile;
$new = new CreateProfile();
$new->email = '[email protected]';
$new->first_name = 'Jane';
$new->properties = ['plan' => 'pro'];
$profile = $client->profiles->create($new); // Resources\Response\Profile
$patch = new PatchProfile($profile->id);
$patch->last_name = 'Doe';
$client->profiles->update($patch);
$query = (new Query())
->filter(Filter::all(
Filter::equals('email', '[email protected]'),
Filter::greaterThan('created', new DateTimeImmutable('-30 days')),
))
->fields('profile', 'email', 'first_name')
->sort('created', descending: true)
->pageSize(50);
$page = $client->profiles->list($query); // ['data' => Profile[], 'links' => ?PaginationLinks]
foreach ($client->profiles->iterate($query) as $p) { // all pages, fetched as you go
// ...
}
use nickdnk\Klaviyo\Filter;
use nickdnk\Klaviyo\Query;
$query = (new Query())
->filter(Filter::equals('email', '[email protected]')) // filter=equals(email,"[email protected]")
->fields('profile', 'email', 'created') // fields[profile]=email,created
->additionalFields('profile', 'subscriptions') // additional-fields[profile]=subscriptions
->
use nickdnk\Klaviyo\Filter;
use nickdnk\Klaviyo\Query;
$query = (new Query())->filter(Filter::equals('email', '[email protected]'))->pageSize(50);
foreach ($client->profiles->iterate($query) as $profile) {
echo $profile->email, PHP_EOL;
}
$all = iterator_to_array($client->profiles->iterate($query)); // everything in one array; fine for small sets
$cursor = $page['links']->next; // store this
$page = $client->profiles->list((new Query())->filter(Filter::equals('email', '[email protected]'))->pageSize(50)->cursor($cursor));
use nickdnk\Klaviyo\Filter;
use nickdnk\Klaviyo\Query;
$tag = $client->tags->get('UXxKzq', (new Query())->er(Filter::equals('messages.channel', 'email'))->
use nickdnk\Klaviyo\Resources\Request\UpdateTrackingSetting;
use nickdnk\Klaviyo\Resources\Shared\Explicit;
$accountId = $client->accounts->list()['data'][0]->id; // tracking settings are keyed by account id
$update = new UpdateTrackingSetting($accountId);
$update->utm_term = Explicit::null();
$update->custom_parameters = Explicit::emptyList();
$client->trackingSettings->update($update);
use nickdnk\Klaviyo\APIClient;
use nickdnk\Klaviyo\Resources\Request\PatchProfile;
$profileIds = ['01HZX4M2K9Q8R7T6V5W4X3Y2Z1', '01HZX4M2K9Q8R7T6V5W4X3Y2Z2'];
$requests = array_map(function (string $id) use ($client) {
$patch = new PatchProfile($id);
$patch->properties = ['synced_at' => date(DATE_ATOM)];
return $client->profiles->update($patch, returnRequest: true);
}, $profileIds);
$results = $client->executePool($requests, concurrency: 5); // Profile[] in input order, exceptions in place of failures
APIClient::assertNoExceptions($results);
use nickdnk\Klaviyo\Resources\Request\ImportProfile;
use nickdnk\Klaviyo\Resources\Request\BulkImportJob;
/** @var iterable<array{email: string, first_name: string}> $rows a database cursor, a CSV reader, anything */
$rows = readRowsFromSomewhere();
$listId = 'YafG4m';
$batches = (function () use ($client, $rows, $listId): Generator {
$chunk = [];
foreach ($rows as $row) {
$profile = new ImportProfile();
$profile->email = $row['email'];
$profile->first_name = $row['first_name'];
$chunk[] = $profile;
if (count($chunk) === 10000) {
// Yielded, not collected: this request exists only while the pool holds it.
yield $client->profiles->bulkImport(new BulkImportJob($chunk, [$listId]), returnRequest: true);
$chunk = [];
}
}
if ($chunk) {
yield $client->profiles->bulkImport(new BulkImportJob($chunk, [$listId]), returnRequest: true);
}
})();
$results = $client->executePool($batches, concurrency: 3);
APIClient::assertNoExceptions($results);
$results = $client->executePoolLazy(
$profileIds,
function (string $id) use ($client) {
$patch = new PatchProfile($id);
$patch->properties = ['synced_at' => date(DATE_ATOM)];
return $client->profiles->update($patch, returnRequest: true);
},
concurrency: 5
);
use nickdnk\Klaviyo\APIClient;
use nickdnk\Klaviyo\Http\RetryPolicy;
$client = APIClient::withApiKey('pk_...', retry: new RetryPolicy(maxAttempts: 5, baseDelaySeconds: 1.0, maxDelaySeconds: 20.0));
$limits = $client->getLastRateLimit(); // RateLimit-Limit / -Remaining / -Reset headers of the last response
if ($limits?->isNearlyExhausted(5)) {
sleep($limits->resetSeconds ?? 1);
}
$limits?->burstLimit(); // e.g. 75 for /api/profiles, 1 for /api/accounts
$limits?->windows; // e.g. [1 => 75, 60 => 750]
use nickdnk\Klaviyo\APIClient;
use nickdnk\Klaviyo\Http\Psr18Transport;
use Symfony\Component\HttpClient\Psr18Client; // symfony/http-client, as one example of a PSR-18 client
$http = new Psr18Client(); // also a PSR-17 factory; pass factories explicitly when discovery should not decide
$client = APIClient::withApiKey('pk_...', Psr18Transport::create($http, requestFactory: $http, streamFactory: $http));
use nickdnk\Klaviyo\APIClient;
use nickdnk\Klaviyo\Resources\Request\CreateWebhook;
use nickdnk\Klaviyo\Resources\Shared\WebhookTopic;
use Psr\Http\Message\ServerRequestInterface;
$secret = bin2hex(random_bytes(32)); // store it; it verifies every delivery
$client->webhooks->create(new CreateWebhook(
'orders', 'https://example.com/klaviyo/webhook', $secret,
[WebhookTopic::OPENED_EMAIL, 'event:api.viewed_product'],
));
// In the endpoint handler, with the incoming PSR-7 request:
function handleKlaviyoWebhook(ServerRequestInterface $request, string $secret): int
{
$webhook = APIClient::parseWebhookRequest($request, $secret);
if ($webhook === null) {
return 400; // bad signature, stale or malformed
}
foreach ($webhook->events as ['topic' => $topic, 'payload' => $event]) {
if ($topic->is(WebhookTopic::OPENED_EMAIL)) { /* $event is a hydrated Event */ }
}
return 200;
}
Loading please wait ...
Before you can download the PHP files, the dependencies should be resolved. This can take some minutes. Please be patient.