Search by

sendkuy / sdk

sendkuy

PHP SDK for the SendKuy API: send WhatsApp, SMS and email from your own code.

v1.0.0 2026-10-14 07:57 UTC

This package is auto-updated.

Last update: 2026-10-14 08:02:19 UTC


README

SendKuy PHP SDK

SendKuy PHP SDK

Send WhatsApp, SMS and email from PHP with one API key, and retry without ever sending twice.

Latest version Downloads CI PHPStan level max License: MIT

Baca dalam Bahasa Indonesia

Install

composer require sendkuy/sdk

PHP 8.2 or newer. Building on Laravel? Install sendkuy/laravel instead: it adds a facade, a notification channel and a signed webhook route on top of this package.

Quickstart

use SendKuy\SendKuy;

$sendkuy = new SendKuy(getenv('SENDKUY_API_KEY'));

$messages = $sendkuy->messages()->send([
    'channel' => 'whatsapp',
    'to' => ['+6281234567890'],
    'body' => 'Your order is on its way.',
]);

echo $messages[0]->id; // msg_...

Create a key in the SendKuy dashboard under Developers → Keys. Numbers go in E.164 form, with the leading +. You get one message back per recipient.

Every endpoint has a method

Field names are the API's own, so every example in the API reference maps one to one onto this SDK.

Group Methods
account() get, usage
messages() list, send, sendBatch, get, cancel, resend
numbers() check
verifications() create, get, check
jobs() get
contacts() list, create, bulkImport, lookup, get, update, delete, listAttributes
groups() list, create, get, update, delete, addContacts, removeContacts
templates() list, create, get, update, delete
gateways() list, get
webhooks() list, create, get, update, delete, listDeliveries
campaigns() list, create, get, update, delete, listDispatches, start, pause, resume, cancel
conversations() list, get, listMessages, markRead, reply
workflows() list, get, listExecutions, trigger
executions() get

Objects come back with their fields as properties ($message->status) and as array keys ($message['status']), and your editor autocompletes them. An endpoint newer than your SDK version is still one call away:

$response = $sendkuy->request('GET', '/account');

Retries and idempotency

A timeout tells you nothing about whether a message went out. That is why every write this SDK sends carries an Idempotency-Key, and every retry of that call reuses it: if the first attempt did go through, SendKuy answers the retry with the first result instead of sending again.

The SDK retries connection errors, timeouts, 429 (waiting for Retry-After when it is a minute or less), 5xx and 409 idempotency_request_in_progress, up to twice, with exponential backoff.

When your own code retries, for example a queued job, pass a key derived from something stable in your system:

$sendkuy->messages()->send($params, ['idempotency_key' => 'order-1042-shipped']);
Option Default
base_url https://app.sendkuy.com/api/v1
timeout 30 seconds; also per request: ['timeout' => 10]
max_retries 2
http_client Guzzle any PSR-18 client

Pagination

foreach ($sendkuy->contacts()->list(['limit' => 100])->autoPaginate() as $contact) {
    echo $contact->id;
}

$page = $sendkuy->contacts()->list();
$page->data;       // this page
$page->hasMore;
$page->nextPage(); // null on the last page

Errors

Every failure is a SendKuy\Exceptions\SendKuyException. Branch on the stable code, never on the message:

use SendKuy\Exceptions\InvalidRequestException;
use SendKuy\Exceptions\RateLimitException;
use SendKuy\Exceptions\SendKuyException;

try {
    $sendkuy->messages()->send($params);
} catch (InvalidRequestException $e) {
    $e->getErrorCode(); // validation_failed
    $e->getParam();     // to
} catch (RateLimitException $e) {
    $e->getRetryAfter(); // seconds
} catch (SendKuyException $e) {
    $e->getRequestId(); // quote it when you contact support
}
Exception When
AuthenticationException the key is missing, wrong, revoked or expired
AuthorizationException the key lacks a scope, or the plan lacks the channel
InvalidRequestException validation, not found, idempotency conflicts
RateLimitException too many requests
ApiException SendKuy failed on its side
ConnectionException SendKuy could not be reached

Webhooks

SendKuy signs every webhook. Verify it against the raw body, before any JSON parsing:

use SendKuy\Exceptions\SignatureVerificationException;
use SendKuy\Webhook;

try {
    $event = Webhook::verify(
        file_get_contents('php://input'),
        $_SERVER['HTTP_X_SENDKUY_SIGNATURE'] ?? '',
        getenv('SENDKUY_WEBHOOK_SECRET'),
    );
} catch (SignatureVerificationException $e) {
    http_response_code(403);
    exit;
}

$deliveryId = $_SERVER['HTTP_X_SENDKUY_EVENT_ID'] ?? ''; // the same on every retry: use it to skip duplicates

if ($event['event'] === 'message.received') {
    // ...
}

http_response_code(200);

Use the endpoint secret in full, whsec_ included, and answer within 10 seconds. Slow work belongs in a queue.

Self-hosted SendKuy

$sendkuy = new SendKuy($key, ['base_url' => 'https://sendkuy.example.com/api/v1']);

Testing your code

Pass any PSR-18 client and the SDK sends through it:

$sendkuy = new SendKuy('sk_test_123', ['http_client' => $fakeClient]);

A client you pass in owns its own timeouts.

Links

License

MIT. See LICENSE.