Developer docs

IotaBot Email API

Send from your own domain, reply in thread, and receive every email as JSON.

Get an API key

Send and receive email with PHP

Send with cURL, receive with a plain PHP endpoint — no framework needed.

  1. Create an API key (a test key never sends) and a webhook endpoint in Developers.
  2. Set IOTABOT_API_KEY and IOTABOT_WEBHOOK_SECRET (the endpoint's whsec_… secret) in your environment.
  3. Run the program below. Point the endpoint at /iotabot/webhook on your server.
iotabot.php
<?php
// iotabot.php — one function for every send.
const API = 'https://api.iotabot.com/v1';

function iotabot_send(array $body): array {
    $ch = curl_init(API . '/messages');
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . getenv('IOTABOT_API_KEY'),
            'Content-Type: application/json',
            // One Idempotency-Key per message: reuse it if you retry, so it is never sent twice.
            'Idempotency-Key: ' . bin2hex(random_bytes(16)),
        ],
        CURLOPT_POSTFIELDS => json_encode($body),
    ]);
    $res = json_decode(curl_exec($ch), true);
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);
    if ($status >= 300) throw new RuntimeException($res['error']['code'] . ': ' . $res['error']['message']);
    return $res['data'];
}
send.php
<?php
// send.php — start a new email thread from one of your verified addresses (GET /v1/email/senders).
require __DIR__ . '/iotabot.php';

$sent = iotabot_send([
    'channel' => 'email',
    'to'      => 'priya@example.com',
    'from'    => 'support@acme.com',
    'subject' => 'Your order #1042',
    'html'    => '<p>Hi Priya, your order shipped yesterday.</p>',
]);
echo "queued {$sent['id']} in conversation {$sent['conversation_id']}\n";
webhook.php
<?php
// webhook.php — receive inbound email as JSON and reply in the same thread.
require __DIR__ . '/iotabot.php';

function iotabot_verified(string $raw, string $header, string $secret): bool {
    $t = 0; $sigs = [];
    foreach (explode(',', $header) as $part) {
        [$k, $v] = array_pad(explode('=', $part, 2), 2, '');
        if ($k === 't') $t = (int) $v;
        if ($k === 'v1') $sigs[] = $v;
    }
    if (abs(time() - $t) > 300) return false;          // older than 5 minutes
    $expected = hash_hmac('sha256', $t . '.' . $raw, $secret);
    foreach ($sigs as $sig) if (hash_equals($expected, $sig)) return true;
    return false;
}

$raw = file_get_contents('php://input');
$event = json_decode($raw, true);

if ($event['type'] === 'endpoint.verification') {    // sent once, when you add the endpoint
    header('Content-Type: application/json');
    echo json_encode(['challenge' => $event['data']['challenge']]);
    exit;
}
if (!iotabot_verified($raw, $_SERVER['HTTP_IOTABOT_SIGNATURE'] ?? '', getenv('IOTABOT_WEBHOOK_SECRET'))) {
    http_response_code(401);
    exit;
}
if ($event['type'] === 'message.received') {
    $conversation = $event['data']['conversation'];
    $email = $event['data']['message']['email'];
    error_log($email['from'] . ' wrote: ' . $email['subject']);
    iotabot_send(['conversation_id' => $conversation['id'], 'text' => 'Thanks — we are on it!']);
}
http_response_code(200);