",
To = ["recipient@example.com"],
Subject = "Hello from SendKit",
Html = "Welcome!
Your first email with SendKit.
"
});
Console.WriteLine($"Email sent: {response.Id}");
```
# Elixir
Source: https://docs.sendkit.dev/sdks/elixir
Send emails from Elixir using the SendKit SDK.
View on GitHub
View on Hex
## Install
Add to your `mix.exs` dependencies:
```elixir theme={null}
def deps do
[
{:sendkit, "~> 1.0"}
]
end
```
Then run:
```bash theme={null}
mix deps.get
```
## Send email
```elixir theme={null}
client = SendKit.new("sk_your_api_key")
{:ok, %{"id" => id}} =
SendKit.Emails.send(client, %{
from: "Your Name ",
to: ["recipient@example.com"],
subject: "Hello from SendKit",
html: "Welcome!
Your first email with SendKit.
"
})
IO.puts("Email sent: #{id}")
```
# Go
Source: https://docs.sendkit.dev/sdks/go
Send emails from Go using the SendKit SDK.
View on GitHub
View on pkg.go.dev
## Install
```bash theme={null}
go get github.com/sendkitdev/sendkit-go
```
## Send email
```go theme={null}
package main
import (
"context"
"fmt"
sendkit "github.com/sendkitdev/sendkit-go"
)
func main() {
client, _ := sendkit.NewClient("sk_your_api_key")
resp, _ := client.Emails.Send(context.Background(), &sendkit.SendEmailParams{
From: "Your Name ",
To: []string{"recipient@example.com"},
Subject: "Hello from SendKit",
HTML: "Welcome!
Your first email with SendKit.
",
})
fmt.Println("Email sent:", resp.ID)
}
```
# Java
Source: https://docs.sendkit.dev/sdks/java
Send emails from Java using the SendKit SDK.
View on GitHub
View on Maven Central
## Install
```xml Maven theme={null}
dev.sendkit
sendkit
1.0.0
```
```groovy Gradle theme={null}
implementation 'dev.sendkit:sendkit:1.0.0'
```
## Send email
```java theme={null}
import dev.sendkit.SendKit;
import dev.sendkit.Emails;
import java.util.List;
SendKit client = new SendKit("sk_your_api_key");
Emails.SendEmailResponse response = client.emails().send(
new Emails.SendEmailParams(
"Your Name ",
List.of("recipient@example.com"),
"Hello from SendKit"
).html("Welcome!
Your first email with SendKit.
")
);
System.out.println("Email sent: " + response.getId());
```
# Laravel
Source: https://docs.sendkit.dev/sdks/laravel
Integrate SendKit with Laravel using the official package.
View on GitHub
View on Packagist
## Install
```bash theme={null}
composer require sendkit/sendkit-laravel
```
## Configure
Add your API key to your `.env` file:
```bash .env theme={null}
SENDKIT_API_KEY=sk_your_api_key
```
## Send email
### Using the Laravel Mail driver
SendKit integrates with Laravel's built-in Mail system. Just set the mailer in your `.env`:
```bash .env theme={null}
MAIL_MAILER=sendkit
```
That's it. Send emails using Laravel's standard `Mail` facade as usual:
```php theme={null}
use App\Mail\WelcomeEmail;
use Illuminate\Support\Facades\Mail;
Mail::to('recipient@example.com')->send(new WelcomeEmail());
```
### Using the SendKit facade
If you need more control, you can use the SendKit facade directly:
```php theme={null}
use SendKit\Laravel\Facades\SendKit;
$response = SendKit::emails()->send([
'from' => 'Your Name ',
'to' => 'recipient@example.com',
'subject' => 'Hello from SendKit',
'html' => 'Welcome!
Your first email with SendKit.
',
]);
echo $response['id'];
```
## Validate email
Validate an email address before sending. Each validation costs credits.
```php theme={null}
use SendKit\Laravel\Facades\SendKit;
$result = SendKit::validateEmail('recipient@example.com');
if ($result['should_block']) {
// Email should not be used
echo $result['block_reason'];
}
echo $result['is_valid']; // "HIGH" or "LOW"
echo $result['evaluations']; // detailed checks
```
The `evaluations` array contains:
| Key | Description |
| ------------------ | ------------------------------------------------ |
| `has_valid_syntax` | Whether the email has valid syntax |
| `has_valid_dns` | Whether the domain has valid DNS records |
| `mailbox_exists` | Whether the mailbox exists |
| `is_role_address` | Whether it's a role address (e.g. info@, admin@) |
| `is_disposable` | Whether it's a disposable email |
| `is_random_input` | Whether it appears to be random input |
## Contacts
### Create or update a contact
Create a new contact or update an existing one if the email already exists (upsert).
```php theme={null}
use SendKit\Laravel\Facades\SendKit;
$contact = SendKit::contacts()->create([
'email' => 'john@example.com',
'first_name' => 'John',
'last_name' => 'Doe',
'list_ids' => ['list-uuid-1', 'list-uuid-2'],
'properties' => ['COMPANY' => 'Acme'],
]);
echo $contact['id'];
```
### List contacts
Retrieve a paginated list of contacts.
```php theme={null}
$contacts = SendKit::contacts()->list();
// With pagination
$contacts = SendKit::contacts()->list(['page' => 2]);
echo $contacts['meta']['total']; // total contacts
```
### Get a contact
```php theme={null}
$contact = SendKit::contacts()->get('contact-uuid');
echo $contact['email'];
echo $contact['properties']['COMPANY'];
```
### Update a contact
```php theme={null}
$contact = SendKit::contacts()->update('contact-uuid', [
'first_name' => 'Johnny',
'unsubscribed' => true,
]);
```
### Delete a contact
```php theme={null}
SendKit::contacts()->delete('contact-uuid');
```
### Add a contact to lists
```php theme={null}
$contact = SendKit::contacts()->addToLists('contact-uuid', [
'list-uuid-1',
'list-uuid-2',
]);
```
### List a contact's lists
```php theme={null}
$lists = SendKit::contacts()->listLists('contact-uuid');
// With pagination
$lists = SendKit::contacts()->listLists('contact-uuid', ['page' => 2]);
```
### Remove a contact from a list
```php theme={null}
SendKit::contacts()->removeFromList('contact-uuid', 'list-uuid');
```
## Contact properties
Contact properties let you define custom fields for your contacts.
### Create a property
```php theme={null}
use SendKit\Laravel\Facades\SendKit;
$property = SendKit::contactProperties()->create([
'key' => 'company',
'type' => 'string', // "string", "number", or "date"
'fallback_value' => 'N/A', // optional
]);
echo $property['id'];
```
### List properties
```php theme={null}
$properties = SendKit::contactProperties()->list();
// With pagination
$properties = SendKit::contactProperties()->list(['page' => 2]);
```
### Update a property
```php theme={null}
$property = SendKit::contactProperties()->update('property-uuid', [
'key' => 'organization',
'fallback_value' => 'Unknown',
]);
```
### Delete a property
```php theme={null}
SendKit::contactProperties()->delete('property-uuid');
```
A `SendKitException` with status `409` is thrown if the property is used in segment filters.
## Webhooks
The package automatically registers a `POST /webhook/sendkit` route in your application — no extra setup needed. When SendKit sends a webhook to this endpoint, the package verifies the signature and dispatches a Laravel event you can listen to.
### Add your webhook secret
To verify that incoming webhooks are actually from SendKit, add your webhook secret to `.env`:
```bash .env theme={null}
SENDKIT_WEBHOOK_SECRET=your-webhook-secret
```
You can find your webhook secret in the [SendKit dashboard](https://app.sendkit.dev). When a secret is configured, every incoming request is verified using HMAC-SHA256. If the signature doesn't match, the request is rejected with a `403` response.
If no secret is configured, signature verification is skipped. We strongly recommend always setting a secret in production.
### Customizing the webhook path
By default the webhook listens at `/webhook/sendkit`. You can change this with an environment variable:
```bash .env theme={null}
SENDKIT_WEBHOOK_PATH=api/webhooks/sendkit
```
This will register the route at `POST /api/webhooks/sendkit` instead. Make sure to update the webhook URL in your SendKit dashboard to match.
### Listening for events
When a webhook is received, the package dispatches a Laravel event based on the event type. You can listen for these events anywhere you normally would — in a listener, a service provider, or an `EventServiceProvider`:
```php theme={null}
use SendKit\Laravel\Events\EmailDelivered;
use Illuminate\Support\Facades\Event;
Event::listen(EmailDelivered::class, function ($event) {
$emailId = $event->payload['email_id'];
// Handle the delivered email
});
```
Every event has a `payload` property with the webhook data sent by SendKit.
### Available events
| Event class | Webhook type | Triggered when |
| ---------------------- | ------------------------ | ------------------------------------ |
| `EmailSent` | `email.sent` | Email accepted for delivery |
| `EmailDelivered` | `email.delivered` | Email delivered to recipient |
| `EmailBounced` | `email.bounced` | Email bounced |
| `EmailComplained` | `email.complained` | Recipient marked as spam |
| `EmailOpened` | `email.opened` | Recipient opened the email |
| `EmailClicked` | `email.clicked` | Recipient clicked a link |
| `EmailFailed` | `email.failed` | Email failed to send |
| `EmailDeliveryDelayed` | `email.delivery_delayed` | Delivery is taking longer than usual |
| `EmailRejected` | `email.rejected` | Email was rejected |
| `ContactCreated` | `contact.created` | Contact was created |
| `ContactUpdated` | `contact.updated` | Contact was updated |
| `ContactDeleted` | `contact.deleted` | Contact was deleted |
All event classes are in the `SendKit\Laravel\Events` namespace.
### Advanced configuration
For full control over the webhook configuration, publish the config file:
```bash theme={null}
php artisan vendor:publish --tag=sendkit-config
```
This creates a `config/sendkit.php` file where you can customize the webhook path, secret, and other options.
# Node.js
Source: https://docs.sendkit.dev/sdks/node
Send emails from Node.js using the SendKit SDK.
View on GitHub
View on npm
## Install
```bash npm theme={null}
npm install @sendkitdev/sdk
```
```bash yarn theme={null}
yarn add @sendkitdev/sdk
```
```bash pnpm theme={null}
pnpm add @sendkitdev/sdk
```
## Send email
```typescript theme={null}
import { SendKit } from '@sendkitdev/sdk';
const sendkit = new SendKit('sk_your_api_key');
const { data, error } = await sendkit.emails.send({
from: 'Your Name ',
to: 'recipient@example.com',
subject: 'Hello from SendKit',
html: 'Welcome!
Your first email with SendKit.
',
});
if (error) {
console.error(error);
} else {
console.log('Email sent:', data.id);
}
```
# PHP
Source: https://docs.sendkit.dev/sdks/php
Send emails from PHP using the SendKit SDK.
View on GitHub
View on Packagist
## Install
```bash theme={null}
composer require sendkit/sendkit-php
```
## Send email
```php theme={null}
use SendKit\SendKit;
$client = SendKit::client('sk_your_api_key');
$response = $client->emails()->send([
'from' => 'Your Name ',
'to' => 'recipient@example.com',
'subject' => 'Hello from SendKit',
'html' => 'Welcome!
Your first email with SendKit.
',
]);
echo $response['id'];
```
## Validate email
Validate an email address before sending. Each validation costs credits.
```php theme={null}
use SendKit\SendKit;
$client = SendKit::client('sk_your_api_key');
$result = $client->validateEmail('recipient@example.com');
if ($result['should_block']) {
// Email should not be used
echo $result['block_reason'];
}
echo $result['is_valid']; // "HIGH" or "LOW"
echo $result['evaluations']; // detailed checks
```
The `evaluations` array contains:
| Key | Description |
| ------------------ | ------------------------------------------------ |
| `has_valid_syntax` | Whether the email has valid syntax |
| `has_valid_dns` | Whether the domain has valid DNS records |
| `mailbox_exists` | Whether the mailbox exists |
| `is_role_address` | Whether it's a role address (e.g. info@, admin@) |
| `is_disposable` | Whether it's a disposable email |
| `is_random_input` | Whether it appears to be random input |
## Contacts
### Create or update a contact
Create a new contact or update an existing one if the email already exists (upsert).
```php theme={null}
$contact = $client->contacts()->create([
'email' => 'john@example.com',
'first_name' => 'John',
'last_name' => 'Doe',
'list_ids' => ['list-uuid-1', 'list-uuid-2'],
'properties' => ['COMPANY' => 'Acme'],
]);
echo $contact['id'];
```
### List contacts
Retrieve a paginated list of contacts.
```php theme={null}
$contacts = $client->contacts()->list();
// With pagination
$contacts = $client->contacts()->list(['page' => 2]);
echo $contacts['meta']['total']; // total contacts
```
### Get a contact
```php theme={null}
$contact = $client->contacts()->get('contact-uuid');
echo $contact['email'];
echo $contact['properties']['COMPANY'];
```
### Update a contact
```php theme={null}
$contact = $client->contacts()->update('contact-uuid', [
'first_name' => 'Johnny',
'unsubscribed' => true,
]);
```
### Delete a contact
```php theme={null}
$client->contacts()->delete('contact-uuid');
```
### Add a contact to lists
```php theme={null}
$contact = $client->contacts()->addToLists('contact-uuid', [
'list-uuid-1',
'list-uuid-2',
]);
```
### List a contact's lists
```php theme={null}
$lists = $client->contacts()->listLists('contact-uuid');
// With pagination
$lists = $client->contacts()->listLists('contact-uuid', ['page' => 2]);
```
### Remove a contact from a list
```php theme={null}
$client->contacts()->removeFromList('contact-uuid', 'list-uuid');
```
## Contact properties
Contact properties let you define custom fields for your contacts.
### Create a property
```php theme={null}
$property = $client->contactProperties()->create([
'key' => 'company',
'type' => 'string', // "string", "number", or "date"
'fallback_value' => 'N/A', // optional
]);
echo $property['id'];
```
### List properties
```php theme={null}
$properties = $client->contactProperties()->list();
// With pagination
$properties = $client->contactProperties()->list(['page' => 2]);
```
### Update a property
```php theme={null}
$property = $client->contactProperties()->update('property-uuid', [
'key' => 'organization',
'fallback_value' => 'Unknown',
]);
```
### Delete a property
```php theme={null}
$client->contactProperties()->delete('property-uuid');
```
A `SendKitException` with status `409` is thrown if the property is used in segment filters.
# Python
Source: https://docs.sendkit.dev/sdks/python
Send emails from Python using the SendKit SDK.
View on GitHub
View on PyPI
## Install
```bash theme={null}
pip install sendkit
```
## Send email
```python theme={null}
from sendkit import SendKit
client = SendKit("sk_your_api_key")
result = client.emails.send(
from_="Your Name ",
to="recipient@example.com",
subject="Hello from SendKit",
html="Welcome!
Your first email with SendKit.
",
)
print(result["id"])
```
# Ruby
Source: https://docs.sendkit.dev/sdks/ruby
Send emails from Ruby using the SendKit SDK.
View on GitHub
View on RubyGems
## Install
```bash gem theme={null}
gem install sendkit
```
```ruby Gemfile theme={null}
gem "sendkit"
```
## Send email
```ruby theme={null}
require "sendkit"
client = SendKit::Client.new("sk_your_api_key")
result = client.emails.send(
from: "Your Name ",
to: "recipient@example.com",
subject: "Hello from SendKit",
html: "Welcome!
Your first email with SendKit.
"
)
puts result["id"]
```
# Rust
Source: https://docs.sendkit.dev/sdks/rust
Send emails from Rust using the SendKit SDK.
View on GitHub
View on crates.io
## Install
Add to your `Cargo.toml`:
```toml theme={null}
[dependencies]
sendkit = "1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
```
## Send email
```rust theme={null}
use sendkit::{SendKit, SendEmailParams};
#[tokio::main]
async fn main() {
let client = SendKit::new("sk_your_api_key").unwrap();
let response = client.emails.send(&client, &SendEmailParams {
from: "Your Name ".into(),
to: vec!["recipient@example.com".into()],
subject: "Hello from SendKit".into(),
html: Some("Welcome!
Your first email with SendKit.
".into()),
..Default::default()
}).await.unwrap();
println!("Email sent: {}", response.id);
}
```
# Event Types
Source: https://docs.sendkit.dev/webhooks/event-types
All webhook event types supported by SendKit
SendKit sends webhook events for both email and contact activity. Each event includes a JSON payload with event-specific data.
## Email events
| Event | Description |
| -------------------------------- | ---------------------------------------------------------- |
|
`email.sent` | Email was accepted and sent to the recipient's mail server |
|
`email.delivered` | Email was successfully delivered to the recipient's inbox |
|
`email.opened` | Recipient opened the email |
|
`email.clicked` | Recipient clicked a link in the email |
|
`email.bounced` | Email permanently rejected by the recipient's mail server |
|
`email.complained` | Recipient marked the email as spam |
|
`email.rejected` | Email was rejected before sending |
|
`email.failed` | Email failed to send |
|
`email.delivery_delayed` | Email delivery was temporarily delayed |
## Contact events
| Event | Description |
| ------------------------- | -------------------------------- |
|
`contact.created` | A new contact was created |
|
`contact.updated` | A contact's details were updated |
|
`contact.deleted` | A contact was deleted |
# contact.created
Source: https://docs.sendkit.dev/webhooks/events/contact-created
Triggered when a new contact is created
## When it triggers
This event fires when a new contact is added to your account, either through the API, the dashboard, or automatically when sending an email to a new address.
## Payload
```json theme={null}
{
"type": "contact.created",
"data": {
"contact_id": "ct_abc123",
"email": "user@example.com",
"first_name": "John",
"last_name": "Doe",
"created_at": "2026-03-02T12:00:00+00:00"
},
"created_at": "2026-03-02T12:00:00+00:00"
}
```
## Payload fields
Unique identifier of the contact.
Contact's email address.
Contact's first name.
Contact's last name.
ISO 8601 timestamp of when the contact was created.
# contact.deleted
Source: https://docs.sendkit.dev/webhooks/events/contact-deleted
Triggered when a contact is deleted
## When it triggers
This event fires when a contact is permanently deleted from your account.
## Payload
```json theme={null}
{
"type": "contact.deleted",
"data": {
"contact_id": "ct_abc123",
"email": "user@example.com"
},
"created_at": "2026-03-02T13:00:00+00:00"
}
```
## Payload fields
Unique identifier of the contact.
Contact's email address.
# contact.updated
Source: https://docs.sendkit.dev/webhooks/events/contact-updated
Triggered when a contact's details are updated
## When it triggers
This event fires when any of the following contact fields are changed: `email`, `first_name`, `last_name`, or `unsubscribed`. Changes to other fields do not trigger this event.
## Payload
```json theme={null}
{
"type": "contact.updated",
"data": {
"contact_id": "ct_abc123",
"email": "user@example.com",
"first_name": "John",
"last_name": "Doe",
"unsubscribed": false,
"updated_at": "2026-03-02T12:30:00+00:00"
},
"created_at": "2026-03-02T12:30:00+00:00"
}
```
## Payload fields
Unique identifier of the contact.
Contact's email address.
Contact's first name.
Contact's last name.
Whether the contact has unsubscribed from emails.
ISO 8601 timestamp of when the contact was updated.
# email.bounced
Source: https://docs.sendkit.dev/webhooks/events/email-bounced
Triggered when the email is permanently rejected by the recipient's mail server
## When it triggers
This event fires when the recipient's mail server permanently rejects the email. Common causes include invalid email addresses, full mailboxes, or domain-level blocks.
Bounced addresses are automatically added to the suppression list to protect your sender reputation. Future emails to this address will be blocked.
## Payload
```json theme={null}
{
"type": "email.bounced",
"data": {
"email_id": "em_abc123",
"from": "hello@yourdomain.com",
"to": "user@example.com",
"subject": "Welcome to SendKit",
"tags": [],
"created_at": "2026-03-02T12:00:02+00:00"
},
"created_at": "2026-03-02T12:00:02+00:00"
}
```
## Payload fields
Unique identifier of the email.
Sender email address.
Recipient email address.
Email subject line.
Tags associated with the email. Each tag has `name` and `value` string fields. Returns an empty array if no tags were set.
ISO 8601 timestamp of when the email was created.
# email.clicked
Source: https://docs.sendkit.dev/webhooks/events/email-clicked
Triggered when the recipient clicks a link in the email
## When it triggers
This event fires when the recipient clicks a link in the email. Click tracking works by rewriting links through a tracking redirect.
## Payload
```json theme={null}
{
"type": "email.clicked",
"data": {
"email_id": "em_abc123",
"from": "hello@yourdomain.com",
"to": "user@example.com",
"subject": "Welcome to SendKit",
"tags": [],
"created_at": "2026-03-02T12:06:00+00:00"
},
"created_at": "2026-03-02T12:06:00+00:00"
}
```
## Payload fields
Unique identifier of the email.
Sender email address.
Recipient email address.
Email subject line.
Tags associated with the email. Each tag has `name` and `value` string fields. Returns an empty array if no tags were set.
ISO 8601 timestamp of when the email was created.
# email.complained
Source: https://docs.sendkit.dev/webhooks/events/email-complained
Triggered when the recipient marks the email as spam
## When it triggers
This event fires when the recipient reports the email as spam through their email client. This generates a complaint feedback loop report.
Complaint rates are closely monitored by email providers. High complaint rates can lead to domain-wide deliverability issues. The recipient is automatically added to the suppression list.
## Payload
```json theme={null}
{
"type": "email.complained",
"data": {
"email_id": "em_abc123",
"from": "hello@yourdomain.com",
"to": "user@example.com",
"subject": "Welcome to SendKit",
"tags": [],
"created_at": "2026-03-02T12:10:00+00:00"
},
"created_at": "2026-03-02T12:10:00+00:00"
}
```
## Payload fields
Unique identifier of the email.
Sender email address.
Recipient email address.
Email subject line.
Tags associated with the email. Each tag has `name` and `value` string fields. Returns an empty array if no tags were set.
ISO 8601 timestamp of when the email was created.
# email.delivered
Source: https://docs.sendkit.dev/webhooks/events/email-delivered
Triggered when the email is confirmed delivered to the recipient's inbox
## When it triggers
This event fires when the recipient's mail server confirms that the email was accepted and delivered to the inbox.
## Payload
```json theme={null}
{
"type": "email.delivered",
"data": {
"email_id": "em_abc123",
"from": "hello@yourdomain.com",
"to": "user@example.com",
"subject": "Welcome to SendKit",
"tags": [],
"created_at": "2026-03-02T12:00:01+00:00"
},
"created_at": "2026-03-02T12:00:01+00:00"
}
```
## Payload fields
Unique identifier of the email.
Sender email address.
Recipient email address.
Email subject line.
Tags associated with the email. Each tag has `name` and `value` string fields. Returns an empty array if no tags were set.
ISO 8601 timestamp of when the email was created.
# email.delivery_delayed
Source: https://docs.sendkit.dev/webhooks/events/email-delivery-delayed
Triggered when email delivery is temporarily delayed
## When it triggers
This event fires when the recipient's mail server temporarily defers delivery. SendKit will continue to retry delivery automatically. If delivery eventually succeeds, you will receive an `email.delivered` event.
## Payload
```json theme={null}
{
"type": "email.delivery_delayed",
"data": {
"email_id": "em_abc123",
"from": "hello@yourdomain.com",
"to": "user@example.com",
"subject": "Welcome to SendKit",
"tags": [],
"created_at": "2026-03-02T12:00:04+00:00"
},
"created_at": "2026-03-02T12:00:04+00:00"
}
```
## Payload fields
Unique identifier of the email.
Sender email address.
Recipient email address.
Email subject line.
Tags associated with the email. Each tag has `name` and `value` string fields. Returns an empty array if no tags were set.
ISO 8601 timestamp of when the email was created.
# email.failed
Source: https://docs.sendkit.dev/webhooks/events/email-failed
Triggered when the email fails to send
## When it triggers
This event fires when SendKit is unable to send the email. This can happen due to internal errors, invalid configurations, or issues with the sending infrastructure.
## Payload
```json theme={null}
{
"type": "email.failed",
"data": {
"email_id": "em_abc123",
"from": "hello@yourdomain.com",
"to": "user@example.com",
"subject": "Welcome to SendKit",
"tags": [],
"created_at": "2026-03-02T12:00:03+00:00"
},
"created_at": "2026-03-02T12:00:03+00:00"
}
```
## Payload fields
Unique identifier of the email.
Sender email address.
Recipient email address.
Email subject line.
Tags associated with the email. Each tag has `name` and `value` string fields. Returns an empty array if no tags were set.
ISO 8601 timestamp of when the email was created.
# email.opened
Source: https://docs.sendkit.dev/webhooks/events/email-opened
Triggered when the recipient opens the email
## When it triggers
This event fires when the recipient opens the email. Open tracking works by embedding a tracking pixel in the email HTML.
Open tracking is not 100% accurate. Some email clients block tracking pixels or pre-load images, which can affect results.
## Payload
```json theme={null}
{
"type": "email.opened",
"data": {
"email_id": "em_abc123",
"from": "hello@yourdomain.com",
"to": "user@example.com",
"subject": "Welcome to SendKit",
"tags": [],
"created_at": "2026-03-02T12:05:00+00:00"
},
"created_at": "2026-03-02T12:05:00+00:00"
}
```
## Payload fields
Unique identifier of the email.
Sender email address.
Recipient email address.
Email subject line.
Tags associated with the email. Each tag has `name` and `value` string fields. Returns an empty array if no tags were set.
ISO 8601 timestamp of when the email was created.
# email.rejected
Source: https://docs.sendkit.dev/webhooks/events/email-rejected
Triggered when an email is rejected before sending
## When it triggers
This event fires when an email is rejected before it is sent. Common causes include invalid email syntax, disposable email addresses, or addresses on the suppression list.
## Payload
```json theme={null}
{
"type": "email.rejected",
"data": {
"email_id": "em_abc123",
"from": "hello@yourdomain.com",
"to": "user@example.com",
"subject": "Welcome to SendKit",
"tags": [],
"created_at": "2026-03-02T12:00:00+00:00"
},
"created_at": "2026-03-02T12:00:00+00:00"
}
```
## Payload fields
Unique identifier of the email.
Sender email address.
Recipient email address.
Email subject line.
Tags associated with the email. Each tag has `name` and `value` string fields. Returns an empty array if no tags were set.
ISO 8601 timestamp of when the email was created.
# email.sent
Source: https://docs.sendkit.dev/webhooks/events/email-sent
Triggered when an email is accepted and sent to the recipient's mail server
## When it triggers
This event fires when SendKit successfully hands off the email to the recipient's mail server. This does not guarantee inbox delivery — see `email.delivered` for confirmation.
## Payload
```json theme={null}
{
"type": "email.sent",
"data": {
"email_id": "em_abc123",
"from": "hello@yourdomain.com",
"to": "user@example.com",
"subject": "Welcome to SendKit",
"tags": [],
"created_at": "2026-03-02T12:00:00+00:00"
},
"created_at": "2026-03-02T12:00:00+00:00"
}
```
## Payload fields
Unique identifier of the email.
Sender email address.
Recipient email address.
Email subject line.
Tags associated with the email. Each tag has `name` and `value` string fields. Returns an empty array if no tags were set.
ISO 8601 timestamp of when the email was created.
# Introduction
Source: https://docs.sendkit.dev/webhooks/introduction
Receive real-time notifications about email and contact events via webhooks
Webhooks allow your application to receive real-time HTTP notifications when events occur in SendKit. Instead of polling the API, SendKit pushes event data to your endpoint as it happens.
## How it works
Provide an HTTPS URL where SendKit will send event notifications.
Subscribe to specific events (e.g. `email.delivered`) or use the wildcard `*` to receive all events.
SendKit sends a `POST` request with a JSON payload and an HMAC-SHA256 signature for verification.
## Payload format
Every webhook delivery is a `POST` request with a JSON body:
```json theme={null}
{
"type": "email.delivered",
"data": {
"email_id": "em_abc123",
"from": "hello@yourdomain.com",
"to": "user@example.com",
"subject": "Welcome to SendKit",
"created_at": "2026-03-02T12:00:00+00:00"
},
"created_at": "2026-03-02T12:00:00+00:00"
}
```
## Available events
SendKit supports two categories of events:
| Category | Events |
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Email** | `email.sent`, `email.delivered`, `email.bounced`, `email.complained`, `email.opened`, `email.clicked`, `email.rejected`, `email.failed`, `email.delivery_delayed` |
| **Contact** | `contact.created`, `contact.updated`, `contact.deleted` |
See the full list of events with payload examples.
# Retries and failures
Source: https://docs.sendkit.dev/webhooks/retries
How SendKit handles failed webhook deliveries
## Retry behavior
When a webhook delivery fails, SendKit automatically retries the request:
SendKit makes up to **3 total attempts** (1 initial + 2 retries) with a 60-second delay between each:
| Attempt | Delay |
| ----------------- | ---------------------------------- |
| Initial request | Immediate |
| 1st retry | 60 seconds after initial failure |
| 2nd retry (final) | 60 seconds after 1st retry failure |
A delivery is considered failed when:
* Your endpoint returns a non-2xx status code
* The request times out (10 second limit)
* The connection cannot be established
## Automatic pausing
If a webhook accumulates **5 consecutive failures**, SendKit automatically pauses it to prevent unnecessary load on your server. A failure is counted once all 3 attempts for a single delivery are exhausted — individual retry attempts do not count separately. When this happens:
* The webhook status changes to **Paused**
* The team owner receives an email notification
* No further deliveries are attempted until you re-enable the webhook
To resume deliveries, go to the webhook settings in your dashboard and change the status back to **Enabled**. This resets the failure counter.
## Best practices
Always return a `200` status code as quickly as possible. Process webhook data asynchronously in a background job.
* **Respond within 10 seconds** — Requests that take longer will time out and count as a failure.
* **Use HTTPS** — Webhook endpoints must use HTTPS.
* **Handle duplicates** — Webhooks may be retried, so make your processing idempotent.
* **Replay events** — Use the replay feature in the dashboard to re-send any past webhook delivery.
## Monitoring
Every webhook delivery is logged with:
* The full request payload
* Response status code and body
* Delivery timestamp or failure timestamp
* Number of attempts
You can view these logs in real time from the webhook detail page in your dashboard.
# Verifying signatures
Source: https://docs.sendkit.dev/webhooks/signatures
Verify webhook authenticity using HMAC-SHA256 signatures
Every webhook request includes an `X-Webhook-Signature` header containing an HMAC-SHA256 signature. You should always verify this signature before processing the payload to ensure the request came from SendKit.
## How it works
SendKit signs the JSON payload using your webhook's signing secret:
```
HMAC-SHA256(JSON payload, signing_secret)
```
The resulting hex digest is sent in the `X-Webhook-Signature` header.
## Verification examples
```js Node.js theme={null}
import crypto from 'crypto';
import express from 'express';
const app = express();
// Important: use raw body for signature verification
app.use(express.json({
verify: (req, res, buf) => { req.rawBody = buf.toString(); }
}));
const verify = (rawBody, signature, secret) => {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
};
app.post('/webhooks/sendkit', (req, res) => {
const signature = req.headers['x-webhook-signature'];
const secret = process.env.SENDKIT_WEBHOOK_SECRET;
if (!verify(req.rawBody, signature, secret)) {
return res.status(401).send('Invalid signature');
}
// Process the event
console.log(req.body.type);
res.status(200).send('OK');
});
```
```php PHP theme={null}
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'];
$secret = env('SENDKIT_WEBHOOK_SECRET');
$expected = hash_hmac('sha256', $payload, $secret);
if (!hash_equals($expected, $signature)) {
http_response_code(401);
exit('Invalid signature');
}
$event = json_decode($payload, true);
// Process the event
```
```python Python theme={null}
import hmac
import hashlib
from flask import Flask, request
app = Flask(__name__)
def verify(raw_body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
raw_body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
@app.route('/webhooks/sendkit', methods=['POST'])
def webhook():
signature = request.headers.get('X-Webhook-Signature')
secret = 'your_signing_secret'
# Important: use raw body, not parsed JSON
if not verify(request.get_data(), signature, secret):
return 'Invalid signature', 401
event = request.get_json()
# Process the event
return 'OK', 200
```
```go Go theme={null}
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"io"
"net/http"
)
func verify(rawBody []byte, signature, secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(rawBody)
expected := hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(signature))
}
func webhookHandler(w http.ResponseWriter, r *http.Request) {
// Important: use raw body, not re-serialized JSON
rawBody, _ := io.ReadAll(r.Body)
signature := r.Header.Get("X-Webhook-Signature")
secret := "your_signing_secret"
if !verify(rawBody, signature, secret) {
http.Error(w, "Invalid signature", http.StatusUnauthorized)
return
}
// Process the event using rawBody
w.WriteHeader(http.StatusOK)
}
```
## Rotating secrets
You can rotate your webhook's signing secret at any time from the dashboard. After rotation, use the new secret to verify future deliveries. Previous deliveries will still show the old signature in logs.
After rotating a secret, update your application immediately. Requests signed with the old secret will fail verification.