HTTP Webhook Integration Guide Print

  • 41

This guide explains how to set up your HTTP endpoint so we can push live delivery, bounce, and defer logs directly to you.

Our webhooks let you see, in (near) real time, what happened to each message you sent via our pre-warmed SMTP servers.

 

1. What Our Webhook Does

For every event we track (delivery, bounce, deferral, etc.) we:

  • Make an HTTP POST request to your endpoint URL

  • Send all event data in the POST body (never in query parameters)

  • Use either NDJSON or CSV format (your choice)

  • Include an optional authentication header (X-IPW-Token) if you want a shared secret

You can then insert these events into your database / message bus and build whatever reporting you like (per-campaign stats, per-recipient history, dashboards, etc.).

 

2. Submit Your Webhook Details (click here)

When you request webhook setup, please provide:

2.1 Endpoint URL

Send us the full HTTPS URL we should POST to, for example:

https://your-domain.com/webhook/ipwarmup

Important:
All event data is in the POST body, not in $_GET.
You do not need to use ?token= or read query parameters for our webhook to work.

If you really want to include a static query string for your own internal routing (e.g. ?source=ipw), that is fine, but it’s purely for your logic. We do not put any event data into query parameters.

2.2 Choose Data Format

We support two formats. Tell us which one you prefer:

  1. NDJSON (newline-delimited JSON)

    • Content-Type: application/x-ndjson

    • Each line is one complete JSON object, easy to parse line-by-line in any language.

  2. CSV

    • Content-Type: text/csv

    • First line is header, subsequent lines are rows.

    • Good if you plan to import into spreadsheets or BI tools.

2.3 Select Events to Receive

Let us know which event types you want us to send:

  • Deliveries only

  • Bounces only

  • Deferrals only

  • All of the above (most common)

We’ll configure your feed so only those event types are POSTed.

 

3. Authentication (Optional)

Shared Secret Header (X-IPW-Token)

We can include a shared secret in an HTTP header like this:

X-IPW-Token: YOUR_SECRET_VALUE

In PHP, you can validate it like this:

<?php
// webhook_receiver.php

// 1) Validate token from header
$expectedToken = 'ABC123'; // replace with your secret from IPwarmup.com
$receivedToken = $_SERVER['HTTP_X_IPW_TOKEN'] ?? '';

if (!hash_equals($expectedToken, $receivedToken)) {
http_response_code(401);
exit('Unauthorized');
}

// continue reading the POST body below...

This is more secure and more reliable than using a ?token= query parameter and reading $_GET['token'].

 

4. What the Request Looks Like

4.1 NDJSON Example

A typical POST to your endpoint:

POST /webhook/ipwarmup
HTTP/1.1 Host: your-domain.com
Content-Type: application/x-ndjson
X-IPW-Token: ABC123

{"type":"d","timeLogged":"2025-11-17 17:50:05+0000","timeQueued":"2025-11-17 17:50:04+0000","orig":"info@example.com","rcpt":"user1@gmail.com","dsnAction":"relayed","dsnStatus":"2.0.0 (success)","dsnDiag":"smtp;250 2.0.0 OK ...","dsnMta":"gmail-smtp-in.l.google.com (173.194.221.26)","bounceCat":"success","srcMta":"your-sending-host","dlvSourceIp":"203.0.113.10","header_Subject":"Example subject","header_Message-ID":"<abcdef@example.com>"}
{"type":"b","timeLogged":"2025-11-17 17:50:06+0000","timeQueued":"2025-11-17 17:50:05+0000","orig":"info@example.com","rcpt":"bad-address@example.net","dsnAction":"failed","dsnStatus":"5.1.1 (bad destination mailbox address)","dsnDiag":"smtp;550 5.1.1 user unknown","dsnMta":"mx.example.net (198.51.100.10)","bounceCat":"hard","srcMta":"your-sending-host","dlvSourceIp":"203.0.113.10","header_Subject":"Example subject","header_Message-ID":"<123456@example.com>"}

Each line is one event:

  • type – e.g. d (delivery), b (bounce), etc.

  • bounceCatsuccess, hard, soft, etc.

  • dsnAction, dsnStatus, dsnDiag – SMTP-level outcome details

  • orig – envelope sender

  • rcpt – final recipient

  • plus subject, message ID, source IP, and more.

4.2 CSV Example

If you pick CSV, the body will look like:

type,timeLogged,timeQueued,orig,rcpt,dsnAction,dsnStatus,dsnDiag,dsnMta,bounceCat,srcMta,dlvSourceIp,header_Subject,header_Message-ID
d,2025-11-17 17:50:05+0000,2025-11-17 17:50:04+0000,info@example.com,user1@gmail.com,relayed,"2.0.0 (success)","smtp;250 2.0.0 OK ...","gmail-smtp-in.l.google.com (173.194.221.26)",success,your-sending-host,203.0.113.10,"Example subject","<abcdef@example.com>"

 

5. How to Build a Simple Receiver (PHP Sample)

<?php
// webhook_receiver.php

// 1) Validate the shared secret header (recommended)
$expectedToken = 'ABC123'; // Set this to the value we give you
$receivedToken = $_SERVER['HTTP_X_IPW_TOKEN'] ?? '';

if (!hash_equals($expectedToken, $receivedToken)) {
http_response_code(401);
exit('Unauthorized');
}

// 2) Read raw POST body (all event data is here, not in $_GET)
$raw = file_get_contents('php://input');

if ($raw === '' || $raw === false || trim($raw) === '') {
// Optionally treat as heartbeat / no content
http_response_code(204);
exit;
}

// 3) Detect content type to decide how to parse
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';

// 3a) NDJSON or JSON
if (stripos($contentType, 'ndjson') !== false || stripos($contentType, 'json') !== false) {
$lines = preg_split('/\r\n|\n|\r/', trim($raw));

foreach ($lines as $line) {
if ($line === '') {
continue;
}

$event = json_decode($line, true);
if (!is_array($event)) {
// malformed line, you may want to log it
continue;
}

// TODO: insert $event into your DB / queue / log
// Example:
// save_event_to_database($event);
}
}
// 3b) CSV
elseif (stripos($contentType, 'csv') !== false) {
$rows = array_map('str_getcsv', preg_split('/\r\n|\n|\r/', trim($raw)));
$header = array_shift($rows);

foreach ($rows as $row) {
if (count($row) !== count($header)) {
continue;
}

$event = array_combine($header, $row);

// TODO: insert $event into your DB / queue / log
// Example:
// save_event_to_database($event);
}
}

// 4) Acknowledge success
http_response_code(200);
echo 'OK';

 

6. Testing & Verification

6.1 Staging a Test

When your endpoint is ready:

  1. Let us know your URL, preferred format (NDJSON or CSV) and desired events.

  2. Confirm in your logs that:

    • Request method = POST

    • X-IPW-Token header is present (if enabled)

    • Raw body contains valid NDJSON/CSV

    • Your receiver returns HTTP 2xx (200, 201, 204, etc.)

6.2 Successful Response

Your endpoint must return an HTTP 2xx code for each POST to acknowledge receipt.

  • Any non-2xx response will be treated as a failure and retried after a short delay.

  • If no events are generated for some time, we may send an empty heartbeat so you know the connection is alive.

 

7. Need Help?

If anything doesn’t work as expected (missing events, auth failures, unexpected formats):

Please open a ticket with:

  • Your endpoint URL

  • Whether you’re using NDJSON or CSV

  • The exact HTTP status code you’re returning

  • Any log snippets showing what you received (headers + body)

Our team will cross-check your endpoint and our sending logs to make sure everything is wired correctly.


Was this answer helpful?

« Back