Skip to main content
The PHP SDK ships as three Composer packages. This page covers the framework-agnostic core (senderkit/senderkit-php). For framework integrations see the dedicated pages:

Laravel

Service provider, notification channel, mail transport, and webhook middleware.

Symfony

Bundle with autowiring and a webhook request verifier.

Requirements

  • PHP 8.1+
  • A PSR-18 HTTP client — Guzzle or symfony/http-client are auto-discovered; you can also inject your own.

Install

Quickstart

Client construction

string
required
Your API key — must start with sk_live_ (live mode) or sk_test_ (test mode). The constructor throws \InvalidArgumentException for any other prefix. See Authentication.
string
default:"https://api.senderkit.com"
Override the API base URL. Useful for proxies or self-hosted gateways.
int
default:"30000"
Per-request timeout in milliseconds.
int
default:"2"
Max retry attempts for transient failures — network errors, timeouts, 429, and 5xx. Retries use exponential backoff with jitter.
?ClientInterface
Inject a PSR-18 HTTP client. Defaults to auto-discovery via php-http/discovery (Guzzle or symfony/http-client if either is installed).
The $client->mode property ('live' or 'test') is set as a read-only value after construction, derived from the API key prefix.

send()

Send a templated message, substituting variables at send time.
string
required
Template slug, e.g. 'welcome'.
string
required
Recipient address — email, phone number, or push token.
array<string,mixed>
Template variables. Defaults to [].
Channel
Force a channel (Channel::Email, Channel::Sms, Channel::Push, Channel::WebPush). Defaults to the template’s primary channel.
?int
Pin a specific template version. Omit to use the current published version for the environment.
array<string,string|int|bool|float>
Free-form metadata attached to the message. Indexed server-side, so you can later filter with messages->list(new ListMessagesParams(metadata: [...])).
DateTimeInterface|string|null
Defer delivery to a future time — a DateTimeInterface or an ISO 8601 string. Must be in the future and within 30 days. See Sending.
?string
Idempotency key. If omitted, the SDK auto-generates one so a retried request never duplicates a send. Reusing a key returns the original message.
list<string>|null
Cc / Bcc recipients. Email only.
?string
Reply-To address. Email only.
list<Attachment>|null
File or inline attachments (email only). Each Attachment takes filename, contentType, content (base64-encoded bytes), and optional inline/contentId. Provider caps total across all attachments at 10 MB.

Response

SendResult has three properties: id (e.g. "msg_…"), status ("queued" or "scheduled"), and livemode (bool).

sendRaw()

Send inline content without a registered template. Pass one of the typed content classes — the channel is inferred from the content type.
string
required
Recipient address.
EmailContent|SmsContent|PushContent|WebPushContent
required
Typed content object — determines the channel.
?string
Email only. Must match a verified custom sending domain when using the managed email sender.
?bool
Set true to run server-side variable substitution over the raw content using the vars values.

sendBatch()

Send many messages sequentially with per-item error isolation. A failure on one item never throws — each result carries a success flag so the rest of the batch is not affected.
?string
Base idempotency key. Each item is dispatched with {key}-{index} unless the item already carries its own key.
Each BatchResult:
  • $result->oktrue on success, false on failure.
  • $result->index — position in the input array.
  • $result->resultSendResult when ok === true.
  • $result->errorSenderKitException when ok === false.

context()

Fetch the workspace the API key belongs to and the active send mode.
Returns a Context object with workspace (id, slug, name) and mode ('live' or 'test'). Mirrors GET /v1/context.

Templates

list() returns templates without their version body. get($slug) includes currentVersion (versionNumber, variables, publishedAt). A Template has slug, channel, description, status, and updatedAt.
The content (raw HTML/blocks) field is intentionally omitted from list() and get() responses to keep payloads lean. Use the dashboard or the /v1/templates/{slug}/render endpoint when you need the rendered output.

Messages

cancel() only works on scheduled or queued messages — later states return a 409 (ApiException). list() returns data (array of Message) and nextCursor (string or null).

Error handling

All exceptions extend SenderKitException (which extends \RuntimeException). API errors carry $status, $apiCode, $issues, and $requestId (quote $requestId in support requests). The SDK retries 429, 5xx, network, and timeout failures up to $maxRetries with backoff, so a thrown exception means retries were exhausted. A 403 insufficient_scope error (scoped key used outside its grant) comes back as ApiException with $status = 403 and $apiCode = "insufficient_scope". See Authentication → Scopes.

Webhooks

verify() checks the HMAC-SHA256 signature and validates that the timestamp is within 300 seconds (configurable via $toleranceSeconds). It throws SignatureVerificationException on any failure — empty secret, malformed header, stale timestamp, or signature mismatch. See Webhooks for the full event-type list and payload schema.

Laravel

Notification channel, mail transport, and webhook middleware.

Symfony

Bundle autowiring and webhook verifier.

Sending

Channels, scheduling, and delivery lifecycle.

API Reference

The underlying REST endpoints.