event 欄位指出觸發的事件種類。把 body 當 discriminated union、用這個欄位分流。
string
Discriminator。
session.ended / call.transferred / call.ended / session.finalized / recording.ready 之一。string
Pathors session ID。
string
事件 emit 時的 ISO 8601 timestamp。
session.ended
對話結束時觸發。對 call session,通話的最終狀態跟時長此時可能還沒就緒。
string
對話結束時 agent 停在的 pathway node。
number
messages 的長度 —— 這個 payload 送出的 user/assistant 對話輪數。array
對話逐字稿,依對話順序排列的
{ role, content, timestamp? } 物件。role 為 user 或 assistant —— tool call/result 跟 system prompt 都會被濾掉。timestamp 是該輪對話的 ISO 8601 時間,有記錄到才會出現。語音通話 finalize 後,這裡是 agent-authoritative 的對話內容(使用者實際聽到的版本)。object
這個 session 提取出來的變數,以名稱為 key。除了 agent 對話中提取的值之外,也包含 session 建立時注入的變數:
startAt / endAt、通話 metadata(fromNumber、toNumber),以及 —— 若該電話號碼有設定 header capture 的 SIP 通話 —— 被擷取的 custom SIP header 值,以各 header 設定的 attribute 名稱為 key(例如 X-Campaign-Id header 設定擷取為 campaignId,就會出現在 extractedVariables.campaignId)。string
已棄用
event 在多事件支援之前的 legacy 別名。未來版本會移除 —— 新 receiver 請改用 event。call.transferred
轉接對象接通的當下觸發,通話還在進行中。Call session 限定。
這個事件的用途:在交接的當下就拿到對話內容與這通電話的相關資料,而不是等通話結束。 payload 裡沒有任何獨佔的東西——逐字稿、來電號碼、擷取的 SIP header,call.ended / session.finalized 之後都會再給一次。差別在時間點。要把來電者交給真人的系統,需要的是真人接起來的那一刻就有上下文,不是等他掛完電話。
早多少取決於這通是怎麼轉出去的:
除此之外,
session.ended 還要等 post-session 變數抽取(一次 LLM 呼叫)跑完才會發。這個事件不等任何東西:對方接通的瞬間 agent 就回報。
它也是唯一一個通話進行中就送出的事件。
只有轉接成功才會送。 婉拒或失敗(忙線、拒接、無人接、目的地不在允許清單、該號碼沒有可用的外撥 trunk)不會觸發這個事件——來電者還在 AI 手上,沒有任何交接發生,通話本身的結束事件已經描述了結果。
網頁通話也會送,callType 為 web。網頁的轉接是模擬的:AI 講一句轉接語然後結束通話,沒有 SIP 腿、沒有真人接到這通。這樣設計是為了讓網頁 preview 能完整測到 transfer webhook,所以在對真人派工、開單之類的動作之前,務必先檢查 callType。
它帶不了的東西:任何要等通話結束才存在的資料——最終通話狀態、時長、錄音,以及 post-session 抽取的變數。此時通話紀錄那一列還沒寫入。那些請訂閱 session.finalized,兩邊用 sessionId 對起來。
string
擁有此 agent 的 project ID。
string
固定為
transferred——這個事件只在成功時送出。欄位名與值域都跟 call.ended 的 callStatus 一致,所以不管收到哪個事件,讀通話狀態的方式都一樣。number
messages 的長度。array
轉接發生前的對話逐字稿,格式與
session.ended 的 messages 完全相同({ role, content, timestamp? },只含 user/assistant 輪次)。接手的真人可以直接讀來電者講過什麼。通話類型
call.transferred、call.ended 與 session.finalized 都會帶 callType,說明這通是從哪個介面進來的:
phone—— 走 SIP 的真實電話。web—— 瀏覽器通話。對話是真的,但沒有 SIP 腿。
session.finalized 會發。
差別在 call.transferred 上最要緊:網頁的轉接是模擬的,所以在對交接採取行動之前先檢查 callType。
SIP 區塊
call.transferred、call.ended 與 session.finalized 都會帶一個 sip 物件,描述這通電話的電信身分——前兩個是因為這本來就是通話層的資料,session.finalized 則是因為它會把 call 側的東西一起聚合(它本來就帶 callStatus / callDuration / recordingUrl)。
session.ended 不帶:它純粹是 session 層的事件,純文字管道也會發,帶了永遠用不到。
沒有電信身分時(純文字管道,或這個欄位存在之前建立的 session)會整個省略,所以可以用 "sip" in payload 判斷。
這些值同時還是照舊平鋪在 extractedVariables 裡,沒有移除任何東西——現有的 receiver 不用改。sip 區塊解決的是另一件事:extractedVariables.fromNumber 到底是來電號碼、還是 agent 剛好抽出一個叫 fromNumber 的變數,從 payload 上看不出來;而擷取的 custom SIP header 混在裡面,receiver 必須先知道當初設定了哪些 attribute 名稱才挑得出來。
string
來電者號碼(trunk 有提供時為 E.164)。trunk 沒給就整個省略——
sip 區塊有可能只帶 headers。string
被撥打的線路:inbound 是我方號碼,outbound 是受話方。省略條件同
fromNumber。string
Twilio 線路才有的 Call SID。
object
這個號碼設定擷取的 custom SIP header,以設定的 attribute 名稱為 key(例如
{ "campaignId": "42" })。沒設定擷取、或該通電話沒帶到那個 header 時,對應的 key 不會出現(不是空字串)。走 legacy /config + /session 建立的 session 拿不到擷取設定,這裡會是空物件 {}。call.ended
通話結束時觸發。Call session 限定 —— text session 不會收到這個事件。
string
擁有此 agent 的 project ID。
string
最終 domain status。可能值:
userHangup、agentHangup、transferred、voicemail、errorTransferred、busy、userNoAnswer、userRejected、invalidNumber、sipTrunkFailure、agentNoAnswer、error、unknown、Ended。number
通話秒數。對於不計費的結束狀態(busy、no-answer、無效號碼)為
0。recording.ready
通話錄音處理完成後觸發 —— 成功與失敗都會觸發。Call session 限定,text session 不會收到。
對 call session 來說,錄音處理會 gate session.finalized:finalization 會等到錄音成功或失敗才發生(有 timeout backstop),成功的 recordingUrl 也會一併放進 session.finalized 的 payload。想單獨收到錄音訊號才需要訂閱 recording.ready —— 它是唯一會告訴你錄音失敗的事件;否則 session.finalized 已經帶了 URL。
string
擁有此 agent 的 project ID。
boolean
錄音是否成功處理並存檔。為
false 時不帶 recordingUrl。string
合併後音檔的臨時下載連結。只有
success 為 true 時才有。連結為短期有效(約 15 分鐘)—— 請儘快下載。過期後目前沒有 API 可重新取得,請改從 Pathors 後台的通話記錄下載錄音。session.finalized
這個 session 的所有事情都做完時觸發 —— 對 call session 來說包含錄音處理。Payload 已經把原本要從 session.ended、call.ended、recording.ready 各自抓出來的資料合併到同一個 body,receiver 只需處理這一個事件。
對 text session,call 相關欄位(callStatus、callDuration)整個被省略(不是設成 null)。用欄位有沒有來區分 call vs text:
string
擁有此 agent 的 project ID。
string
對話結束時 agent 停在的 pathway node。
number
messages 的長度,同 session.ended 的 messagesCount。array
對話逐字稿,格式與過濾規則同
session.ended 的 messages。object
同
session.ended 的 extractedVariables —— 電話號碼有設定 header capture 時,一樣包含擷取到的 custom SIP header 值。string
Call session 限定。 同
call.ended 的 callStatus。Text session 被省略。number
Call session 限定。 同
call.ended 的 callDuration。Text session 被省略。string
Call session 限定。 合併後音檔的臨時下載連結,同
recording.ready 的 recordingUrl。有錄音且處理成功時才有;text session 與錄音失敗時省略。連結為短期有效(約 15 分鐘)—— 請儘快下載。實作範例
用event 欄位 dispatch 各事件的 receiver:
順序
session.finalized 對某個 session 一定最後到:它只在所有預期的步驟都完成後才觸發 —— text session 只等 session.ended;call session 等 session.ended、call.ended 加上 recording.ready。
Call session 的典型順序:
call.transferred(只有轉接的通話才有,且在通話進行中就送出)session.endedcall.endedrecording.readysession.finalized
call.transferred 不參與 finalization:沒有轉接的通話一樣會照常收到 session.finalized,不會卡住等一個永遠不會來的事件。
Text session 則是 session.ended 跟 session.finalized 緊接著到。
個別步驟(session.ended、call.ended、recording.ready)彼此之間在 edge case(例如使用者直接掛斷在 agent 還沒走到結尾節點時)沒有嚴格順序,而且 recording.ready 的 HTTP 送達可能跟它解鎖的 session.finalized 請求互相 race。可以依賴的是:收到 session.finalized 時錄音處理必定已結束,成功的錄音 URL 就在該 payload 裡。