HTTP API によるメール送信


KaiMail の HTTP 送信 API を使うと、アプリケーションから HTTPS 経由で独自ドメインのメールを送信できます。SMTP ライブラリは不要です。この API は、ポート 587 の SMTP 認証送信と同じ送信パイプラインへの入り口であり、DKIM 署名・送信者検証・クォータ制御・配信ログはどちらの経路でも同一に動作します。プラットフォームがポート 587 をブロックしている場合、サーバーレス環境から呼び出す場合、あるいは単純に JSON over HTTPS の方が扱いやすい場合にご利用ください。

  • ベース URL: https://kaimail.net/api/v1
  • トランスポート: HTTPS のみ
  • フォーマット: リクエスト・レスポンスともに JSON(UTF-8)
  • 認証: HTTP Basic 認証
  • モデル: 非同期 — 送信はキューに登録され、即座に 202 Accepted を返します

認証

すべてのリクエストに HTTP Basic 認証の資格情報が必要です。

  • ユーザー名 — KaiMail アカウントのメールアドレス
  • パスワード — SMTP パスワード(KaiMail ダッシュボードに表示されます)

これはポート 587 での SMTP 送信に使う資格情報と同一です。Web ダッシュボードへのログインに使うアカウントパスワードでは認証できません

curl -u "[email protected]:YOUR_SMTP_PASSWORD" https://kaimail.net/api/v1/usage

資格情報が欠落・無効の場合は 401(エラーコード invalid_credentials)を返します。資格情報は正しいがプランに送信機能が含まれていない場合は 403(sending_not_allowed)を返します。この区別により、パスワード誤りとプラン制限をインテグレーション側で切り分けられます。

送信には、SMTP 送信が有効なプランが必要です。

レスポンスの構造

成功・エラーを問わず、すべてのレスポンスはリクエストごとに生成される一意の識別子 request_id を含む JSON オブジェクトです。特定の API 呼び出しについてサポートに問い合わせる際は、この値を添えてください。

成功レスポンスはペイロードを data に格納します。

{
  "request_id": "0f6d2c9be5a34f7f9f0c1d2e3a4b5c6d",
  "data": { "...": "..." }
}

エラーレスポンスは、機械可読な code と人間向けの message を持つ error オブジェクトを返します。

{
  "request_id": "0f6d2c9be5a34f7f9f0c1d2e3a4b5c6d",
  "error": {
    "code": "quota_exceeded",
    "message": "Monthly sending quota reached."
  }
}

エラー処理は error.code で分岐してください。message の文言は変更される可能性があります。

送信者の要件

送信できるのは、ご自身が所有するアドレスからのみです。すべての送信リクエストにおいて、送信者アドレスは次の両方を満たす必要があります。

  1. そのドメインが、KaiMail に登録済みの有効なカスタムドメインであること(満たさない場合 domain_not_owned)。
  2. アドレス全体が、そのドメイン上の登録済みの有効なメールボックスであること(満たさない場合 mailbox_not_registered)。

メッセージは送信処理の中でドメインごとの鍵(セレクタ kaimail)により DKIM 署名されます。ダッシュボードに表示される DKIM の DNS レコードを必ず公開しておいてください。


POST /api/v1/email/send

標準的な送信方法です。プレーンなフィールドを渡すだけで、サーバー側が MIME メッセージを構築します(Date ヘッダと Message-ID ヘッダも付与されます)。

リクエストボディ

フィールド 必須 説明
from string はい 送信者アドレス。表示名も指定可能です: "Support <[email protected]>"。アドレス部分は上記「送信者の要件」を満たす必要があります。
to string または string の配列 はい 宛先アドレス。1 通あたり最大 100 件。すべての宛先がメッセージの To: ヘッダに記載されます。Bcc 相当の動作が必要な場合は send-raw を使用してください。
subject string いいえ 件名。998 文字で切り詰められます。
text string 下記参照 プレーンテキスト本文。
html string 下記参照 HTML 本文。

texthtml の少なくとも一方が必須です。両方を指定した場合、multipart/alternative(テキスト + HTML)として送信されます。

curl -X POST https://kaimail.net/api/v1/email/send \
  -u "[email protected]:YOUR_SMTP_PASSWORD" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Support <[email protected]>",
    "to": ["[email protected]"],
    "subject": "ご登録ありがとうございます",
    "text": "こんにちは。ご登録ありがとうございます。",
    "html": "<p>こんにちは。<strong>ご登録</strong>ありがとうございます。</p>"
  }'

レスポンス — 202 Accepted

{
  "request_id": "0f6d2c9be5a34f7f9f0c1d2e3a4b5c6d",
  "data": {
    "tracking_id": "k2vq81xw3n7p5d09smc4",
    "status": "queued",
    "recipients": ["[email protected]"]
  }
}

202 は、メッセージが検証を通過しキューに登録されたことを意味します。配信完了を意味するものではありません。後述のステータスエンドポイントをポーリングするため、tracking_id を保存してください。

Python の例

import requests

resp = requests.post(
    "https://kaimail.net/api/v1/email/send",
    auth=("[email protected]", "YOUR_SMTP_PASSWORD"),
    json={
        "from": "Support <[email protected]>",
        "to": ["[email protected]"],
        "subject": "ご登録ありがとうございます",
        "text": "こんにちは。ご登録ありがとうございます。",
    },
    timeout=30,
)
resp.raise_for_status()
tracking_id = resp.json()["data"]["tracking_id"]

PHP の例

<?php
$payload = [
    "from" => "Support <[email protected]>",
    "to" => ["[email protected]"],
    "subject" => "ご登録ありがとうございます",
    "text" => "こんにちは。ご登録ありがとうございます。",
];

$ch = curl_init("https://kaimail.net/api/v1/email/send");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_USERPWD => "[email protected]:YOUR_SMTP_PASSWORD",
    CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
    CURLOPT_POSTFIELDS => json_encode($payload),
    CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

$body = json_decode($response, true);
if ($status !== 202) {
    throw new RuntimeException(
        "send failed: {$body['error']['code']}: {$body['error']['message']}"
    );
}
$trackingId = $body["data"]["tracking_id"];

POST /api/v1/email/send-raw

MIME メッセージを完全に制御したい場合に使用します — 添付ファイル、カスタムヘッダ、Bcc、メールライブラリで構築済みのメッセージなど。RFC 822 メッセージ全体を base64 エンコードし、SMTP エンベロープとともに送信します。

リクエストボディ

フィールド 必須 説明
raw_message string はい RFC 822 メッセージ全体を base64 エンコードしたもの(標準エンコーディング、パディングあり)。
to string の配列 はい エンベロープ宛先(SMTP RCPT TO)。最大 100 件。配信はこの配列のアドレスに対してのみ行われます。メッセージ内の To:/Cc:/Bcc: ヘッダは配送先の決定には使用されません。Bcc はこの仕組みで実現します: アドレスをこの配列に含め、ヘッダには書かないでください。
envelope_from string いいえ エンベロープ送信者(SMTP MAIL FROM)。省略時はメッセージの From: ヘッダのアドレスが使われます。「送信者の要件」を満たす必要があります。

注意: base64 エンコードによりペイロードは約 33% 増加します。プランのメッセージサイズ上限は、デコード後のメッセージバイト数に適用されます。

Python の例

import base64
import requests
from email.message import EmailMessage

msg = EmailMessage()
msg["From"] = "Billing <[email protected]>"
msg["To"] = "[email protected]"
msg["Subject"] = "請求書のご送付"
msg.set_content("請求書を添付いたします。")
msg.add_attachment(
    open("invoice.pdf", "rb").read(),
    maintype="application", subtype="pdf", filename="invoice.pdf",
)

resp = requests.post(
    "https://kaimail.net/api/v1/email/send-raw",
    auth=("[email protected]", "YOUR_SMTP_PASSWORD"),
    json={
        "raw_message": base64.b64encode(msg.as_bytes()).decode("ascii"),
        "to": ["[email protected]", "[email protected]"],
        "envelope_from": "[email protected]"
    },
    timeout=30,
)

PHP の例

PHP には MIME メッセージを構築する標準機能がないため、この例では PHPMailer(composer require phpmailer/phpmailer)でメッセージを構築し、SMTP の代わりに API 経由で送信します。

<?php
require "vendor/autoload.php";

use PHPMailer\PHPMailer\PHPMailer;

$mail = new PHPMailer(true);
$mail->CharSet = "UTF-8";
$mail->setFrom("[email protected]", "Billing");
$mail->addAddress("[email protected]");
$mail->Subject = "請求書のご送付";
$mail->Body = "請求書を添付いたします。";
$mail->addAttachment("invoice.pdf");
$mail->preSend();                        // 送信せずに MIME メッセージのみ構築
$raw = $mail->getSentMIMEMessage();

$payload = [
    "raw_message" => base64_encode($raw),
    "to" => ["[email protected]", "[email protected]"],
    "envelope_from" => "[email protected]",
];

$ch = curl_init("https://kaimail.net/api/v1/email/send-raw");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_USERPWD => "[email protected]:YOUR_SMTP_PASSWORD",
    CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
    CURLOPT_POSTFIELDS => json_encode($payload),
    CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
curl_close($ch);

レスポンスの形式は /email/send と同一です(202 とともに tracking_idstatusrecipients を返します)。


GET /api/v1/email/<tracking_id>

キューに登録したメッセージの送信ステータスを返します。照会できるのは自分のアカウントから送信したメッセージのみです。

curl -u "[email protected]:YOUR_SMTP_PASSWORD" \
  https://kaimail.net/api/v1/email/k2vq81xw3n7p5d09smc4

レスポンス — 200 OK

{
  "request_id": "6b1a2c3d4e5f60718293a4b5c6d7e8f9",
  "data": {
    "tracking_id": "k2vq81xw3n7p5d09smc4",
    "status": "sent",
    "envelope_from": "[email protected]",
    "recipients": ["[email protected]"],
    "subject": "ご登録ありがとうございます",
    "created_time": "2026-07-20T04:12:33Z",
    "processed_time": "2026-07-20T04:12:41Z",
    "error": null
  }
}

タイムスタンプは ISO 8601(UTC)です。存在しない tracking_id404(コード not_found)を返します。

ステータスのライフサイクル

queued ──▶ sending ──▶ sent
                  └──▶ failed
ステータス 意味
queued 受理され、送信待ちの状態。
sending ワーカーが送信処理中。
sent KaiMail のメールサーバーが配信のために受理した状態。 宛先ごとの配信結果とバウンスはログダッシュボードで確認できます。
failed 送信を断念。理由は error に記録されます。

セマンティクスに関する注意:

  • 一時的な送信エラーは自動的にリトライされます(バックオフ付きで最大 3 回)。それでも失敗した場合にのみ failed になります。
  • 一部の宛先のみが拒否された場合(全宛先ではない場合)、ステータスは sent となり、拒否された宛先が error に記録されます。
  • KaiMail が同一のキュー済みメッセージを二重送信することはありません。送信の途中でワーカーが中断された稀なケースでは、配信結果が不明なため、重複配信のリスクを避けてジョブを failed にします。再送する前にログダッシュボードをご確認ください。

GET /api/v1/usage

送信機能の有効状態と残りクォータを返します。送信前のチェックや監視に利用できます。

curl -u "[email protected]:YOUR_SMTP_PASSWORD" https://kaimail.net/api/v1/usage

レスポンス — 200 OK

{
  "request_id": "9c8b7a6d5e4f30211203f4e5d6c7b8a9",
  "data": {
    "smtp_sending": true,
    "sent_this_period": 42,
    "quota": 1000,
    "remaining": 958,
    "period_days": 30
  }
}
フィールド 説明
smtp_sending プランで送信機能が有効かどうか。
sent_this_period 集計期間内に送信したメール数(宛先ごとにカウント)。
quota 期間あたりの送信クォータ。-1 は無制限を意味します。
remaining 残りクォータ。無制限の場合は null
period_days ローリング集計期間の長さ(現在は 30 日)。

クォータは宛先ごとにカウントされます: 10 宛先への 1 通は 10 とカウントされます。キュー登録済みでまだ送信されていないメッセージも、リクエスト時点のクォータ判定に含まれるため、大量のキュー投入によって上限を超過することはできません。


エラーリファレンス

HTTP error.code 意味 / 対処
400 invalid_json ボディが JSON オブジェクトではない。
400 invalid_request フィールドの欠落または不正(宛先アドレスの誤り、from の欠落、宛先数超過、text/html の両方が未指定、など)。詳細は message に記載されます。
400 invalid_message send-raw のみ: raw_message が有効な base64 でない、またはデコード結果が空。
401 invalid_credentials Basic 認証の資格情報が誤っているか未指定。アカウントのメールアドレスと SMTP パスワードを使用してください。
403 sending_not_allowed 資格情報は正しいが、プランに送信機能が含まれていない。
403 domain_not_owned 送信者のドメインが、有効なカスタムドメインとして登録されていない。
403 mailbox_not_registered 送信者アドレスが、登録済みの有効なメールボックスではない。
404 not_found 指定の tracking_id を持つメッセージがアカウントに存在しない。
413 message_too_large メッセージがプランのサイズ上限を超過。両方のサイズが message に記載されます。
429 quota_exceeded 月間送信クォータに到達(キュー済みメッセージを含む)。/usage を確認してください。

制限値一覧

制限
1 通あたりの宛先数 100
件名の長さ 998 文字
メッセージサイズ プランによる(プラン詳細を参照。send-raw ではデコード後のバイト数に適用)
送信クォータ プランによる。ローリング 30 日間で宛先ごとにカウント
送信リトライ バックオフ付きで 3 回試行後、failed

インテグレーションのチェックリスト

  1. SMTP 送信が有効なプランに加入し、カスタムドメインを追加して、ダッシュボードに表示される DKIM レコードを公開する。
  2. 送信元として使うメールボックスを登録する。
  3. ダッシュボードで SMTP パスワードを取得する — API はポート 587 と同じパスワードを使用します。
  4. POST /api/v1/email/send を呼び出し、返された tracking_id を保存する。
  5. GET /api/v1/email/<tracking_id>sent または failed になるまでポーリングする。sent は「メールサーバーへの受け渡し完了」と解釈し、バウンスはログダッシュボードで確認する。
  6. 429 を受け取ったらバックオフし、GET /api/v1/usage で状況を確認する。