自動発信

番号リストにアナウンス、IVR、アンケートの発信を行い、結果とキー入力を返します。 連携キー (bt_…、スコープ autocall).

エンドポイント: https://api.buluthat.com/api/autocall.php?action=<işlem>

典型的な流れ:

1. media          -> anons id'sini al (ya da tts_template kullan)
2. trunks         -> çıkış hattının slug'ını al
3. create_campaign -> status "draft", digit_labels ile anket seçenekleri
4. add_leads      -> numaraları partiler hâlinde gönder
5. start          -> kampanyayı kuyruğa al
6. webhook        -> sonuçlar size gelsin (ya da results ile çekin)
7. summary        -> tuş dağılımı

GETping · media · trunks · caller_ids

GET https://api.buluthat.com/api/autocall.php?action=ping
GET https://api.buluthat.com/api/autocall.php?action=media       → [{ "id": 12, "name": "Hatırlatma anonsu" }]
GET https://api.buluthat.com/api/autocall.php?action=trunks      → çıkış hatları (slug)
GET https://api.buluthat.com/api/autocall.php?action=caller_ids  → kullanılabilir arayan numaralar

POSTcreate_campaign

curl -X POST "https://api.buluthat.com/api/autocall.php?action=create_campaign" \
  -H "Authorization: Bearer bt_xxx" -H "Content-Type: application/json" \
  -d '{
    "name": "Randevu hatırlatma",
    "campaign_type": "survey",
    "trunk_slug": "hat-1",
    "caller_id": "02124119610",
    "tts_enabled": true,
    "tts_template": "Merhaba {sayin}, {randevu} randevunuzu hatırlatmak için aradık. Geliyorsanız 1, iptal için 2.",
    "digit_labels": { "1": "Geliyorum", "2": "İptal" },
    "digit_destinations": { "9": "queue:4" },
    "start_time": "09:00", "end_time": "18:00",
    "weekdays": ["mon","tue","wed","thu","fri"],
    "max_channels": 3, "ring_timeout_seconds": 30,
    "retry_count": 1, "retry_delay_minutes": 30,
    "amd_enabled": true, "amd_action": "hangup",
    "webhook_url": "https://ornek.com/buluthat/sonuc",
    "webhook_secret": "gizli",
    "webhook_events": "call_finished,dtmf",
    "status": "draft",
    "leads": [ { "phone": "05551112233", "name": "Ahmet Yılmaz", "external_id": "CRM-1", "fields": { "randevu": "12 Eylül 14:00" } } ]
  }'

必須: name, trunk_slug と media_file_id または tts_template。両方指定した場合は、個別音声が先に、続いて既成のアナウンスが再生されます。

エリア値メモ
campaign_typeannouncement, ivr, surveyannouncement アナウンスを再生して切断し、キー入力は待ちません
digit_labels{"1":"Evet"}アンケート用にご記入ください。 ラベルが付いたキーは、宛先がなくても記録されます
digit_destinations{"9":"queue:4"}キーを押した方を転送: ext:1001, queue:4, ivr:2, flow:3, repeat, hangup
amd_actionhangup, play, continue留守番電話に切り替わったときの動作
tts_template"Sayın {ad}, …"変数: {sayin}, {unvan}, {adi}, {ad}, {telefon} と fields フィールド
statusdraft, queuedqueued を指定すると、キャンペーンはすぐにキューに入ります
retry_count / retry_delay_minutes数不応答/話し中の再発信
thanks_tts_textテキストアンケートのキー入力後のお礼メッセージ

レスポンス: { "ok": true, "data": { "id": 7, "status": "draft", … } }

POSTupdate_campaign

部分更新。送信したフィールドのみが変更されます。 status 送信しないでください(実行中のキャンペーンが停止します)。ステータスは start/pause/cancel ご利用ください。

{ "campaign_id": 7, "max_channels": 5, "retry_count": 2 }

POSTadd_leads

1回のリクエストで最大5.000件。同じキャンペーンで同じ番号は2回追加されません(duplicate).

{ "campaign_id": 7, "leads": [ { "phone": "05551112233", "name": "Ahmet Yılmaz", "external_id": "CRM-1", "fields": { "borc": "1.240 TL" } } ] }

短縮形: { "campaign_id": 7, "phones": ["05551112233", "05551112234"] }

レスポンス: { "inserted": 2, "duplicate": 0, "invalid": 0 }

POSTrequeue_leads

過去に発信した番号を再度キューに入れます(calls がリセットされ、キー入力がクリアされます)。すでに待機中の番号には触れません(skipped)。債権回収のリマインドでは、「支払い済み」のキー入力を信用せず、ご自身の記録に基づいて再発信する用途にご利用ください。

{ "campaign_id": 7, "phones": ["05551112233"] }

POSTstart · pause · cancel

{ "campaign_id": 7 }

start キャンペーンをキューに入れます。発信は曜日/時間帯の範囲内で開始されます。完了したキャンペーンに番号を追加すると、キャンペーンは自動的に再開されます。

GETresults

番号ごとの結果。 limit (デフォルト200、最大1000)、 offset;フィルター status, dtmf, phone.

GET https://api.buluthat.com/api/autocall.php?action=results&campaign_id=7&dtmf=1
{
  "ok": true, "total": 512, "limit": 200, "offset": 0,
  "data": [ {
    "campaign_id": 7, "lead_id": 91, "external_id": "CRM-1",
    "phone": "05551112233", "name": "Ahmet Yılmaz",
    "status": "answered", "attempts": 1,
    "dtmf": "1", "dtmf_label": "Geliyorum", "dtmf_all": ["1"],
    "amd_result": "HUMAN", "duration_seconds": 34, "talk_seconds": 22,
    "hangup_cause": "ANSWERED",
    "started_at": "2026-09-04 10:12:03", "answered_at": "2026-09-04 10:12:11", "ended_at": "2026-09-04 10:12:33",
    "custom_fields": { "randevu": "12 Eylül 14:00" }
  } ]
}

ステータス: pending, calling, answered, machine, no_answer, busy, failed, cancelled.

GETsummary

{ "ok": true, "data": {
  "total": 512, "answered": 388, "pressed": 301, "machine": 44,
  "answer_rate": 75.8, "press_rate": 77.6, "talk_seconds": 8624, "avg_talk_seconds": 22,
  "digits": [ { "digit": "1", "label": "Geliyorum", "total": 214, "percent": 55.2 }, { "digit": "2", "label": "İptal", "total": 87, "percent": 22.4 } ]
} }

GETevents

キャンペーンのイベントログ(originate、answered、amd、dtmf、cdr、error)— 診断用。

Webhook

webhook_url が定義されている場合、Buluthatが結果をPOSTします。イベント: call_finished (番号ごとに1回)、 dtmf (キーが押された瞬間)、 campaign_finished。ヘッダー X-Buluthat-Event, X-Buluthat-Delivery, X-Buluthat-Signature: sha256=… (webhook_secret 指定した場合)。本文 results と同じフィールドを持ちます。署名検証と再試行ポリシーについては参照: Webhook.

個別アナウンスとコスト

テンプレートは固定部分と変動部分に分割されます。固定の文は1,000人のリストでも1回だけ音声合成され、同じ {tutar} 同じ値は2回生成されません(実測の削減率%97,9)。変数の値は標準的な形式で送信してください("1.000 TL" 常に同じ表記にしてください)。Bey/Hanımの区別は名前から自動判定され、男女どちらともとれる名前では敬称は空欄になります。

PHPクライアント

パネル > 自動発信 > APIタブからダウンロードした buluthat-autocall-client.php:

require_once 'buluthat-autocall-client.php';
$bt = new BuluthatAutoCall('https://api.buluthat.com', 'bt_xxx');
$k = $bt->createCampaign(['name' => 'Ödeme hatırlatma', 'campaign_type' => 'survey', 'trunk_slug' => 'hat-1',
    'tts_enabled' => true, 'tts_template' => 'Merhaba {sayin}, {tutar} tutarındaki ödemenizi hatırlatmak için aradık. Ödediyseniz 1, ödemediyseniz 2.',
    'digit_labels' => ['1' => 'Ödedim', '2' => 'Ödemedim'], 'webhook_url' => 'https://crm.example.com/buluthat-sonuc.php']);
$bt->addLeads($k['id'], [['phone' => '05551112233', 'name' => 'Burak Yılmaz', 'external_id' => 'CARI-451', 'fields' => ['tutar' => '1.000 TL']]]);
$bt->start($k['id']);

// webhook tarafı
$olay = BuluthatAutoCall::readWebhook($secret);   // imzayı doğrular
if ($olay['event'] === 'call_finished' && $olay['data']['dtmf'] === '1') { /* ödedim dedi */ }