Skip to main content
每個 payload 開頭都有 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? } 物件。roleuserassistant —— tool call/result 跟 system prompt 都會被濾掉。timestamp 是該輪對話的 ISO 8601 時間,有記錄到才會出現。語音通話 finalize 後,這裡是 agent-authoritative 的對話內容(使用者實際聽到的版本)。
object
這個 session 提取出來的變數,以名稱為 key。除了 agent 對話中提取的值之外,也包含 session 建立時注入的變數:startAt / endAt、通話 metadata(fromNumbertoNumber),以及 —— 若該電話號碼有設定 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 手上,沒有任何交接發生,通話本身的結束事件已經描述了結果。 網頁通話也會送callTypeweb。網頁的轉接是模擬的:AI 講一句轉接語然後結束通話,沒有 SIP 腿、沒有真人接到這通。這樣設計是為了讓網頁 preview 能完整測到 transfer webhook,所以在對真人派工、開單之類的動作之前,務必先檢查 callType 它帶不了的東西:任何要等通話結束才存在的資料——最終通話狀態、時長、錄音,以及 post-session 抽取的變數。此時通話紀錄那一列還沒寫入。那些請訂閱 session.finalized,兩邊用 sessionId 對起來。
string
擁有此 agent 的 project ID。
string
固定為 transferred——這個事件只在成功時送出。欄位名與值域都跟 call.endedcallStatus 一致,所以不管收到哪個事件,讀通話狀態的方式都一樣。
string
phone(真實的 SIP 轉接,對方確實接通了)或 web(網頁 preview 的模擬轉接,沒有真人接到)。見 通話類型
number
messages 的長度。
array
轉接發生前的對話逐字稿,格式與 session.endedmessages 完全相同({ role, content, timestamp? },只含 user/assistant 輪次)。接手的真人可以直接讀來電者講過什麼。
object
這通電話的電信身分,見下方 SIP 區塊。非電話 session 會整個省略。

通話類型

call.transferredcall.endedsession.finalized 都會帶 callType,說明這通是從哪個介面進來的:
  • phone —— 走 SIP 的真實電話。
  • web —— 瀏覽器通話。對話是真的,但沒有 SIP 腿。
這個值由 session 推導,所以三個事件天生一致。完全不是通話的 session(純文字管道、測試執行)會整個省略這個欄位——那些情況也只有 session.finalized 會發。 差別在 call.transferred 上最要緊:網頁的轉接是模擬的,所以在對交接採取行動之前先檢查 callType

SIP 區塊

call.transferredcall.endedsession.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。可能值:userHangupagentHanguptransferredvoicemailerrorTransferredbusyuserNoAnsweruserRejectedinvalidNumbersipTrunkFailureagentNoAnswererrorunknownEnded
number
通話秒數。對於不計費的結束狀態(busy、no-answer、無效號碼)為 0
string
phoneweb,見 通話類型
object
這通電話的電信身分,見 SIP 區塊

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
合併後音檔的臨時下載連結。只有 successtrue 時才有。連結為短期有效(約 15 分鐘)—— 請儘快下載。過期後目前沒有 API 可重新取得,請改從 Pathors 後台的通話記錄下載錄音。
成功範例:
失敗範例:

session.finalized

這個 session 的所有事情都做完時觸發 —— 對 call session 來說包含錄音處理。Payload 已經把原本要從 session.endedcall.endedrecording.ready 各自抓出來的資料合併到同一個 body,receiver 只需處理這一個事件。 對 text session,call 相關欄位(callStatuscallDuration整個被省略(不是設成 null)。用欄位有沒有來區分 call vs text:
string
擁有此 agent 的 project ID。
string
對話結束時 agent 停在的 pathway node。
number
messages 的長度,同 session.endedmessagesCount
array
對話逐字稿,格式與過濾規則同 session.endedmessages
object
session.endedextractedVariables —— 電話號碼有設定 header capture 時,一樣包含擷取到的 custom SIP header 值。
string
Call session 限定。call.endedcallStatus。Text session 被省略。
number
Call session 限定。call.endedcallDuration。Text session 被省略。
string
phoneweb,見 通話類型
object
Call session 限定。 這通電話的電信身分,見 SIP 區塊。Text session 跟其他 call 側欄位一樣會被省略。
string
Call session 限定。 合併後音檔的臨時下載連結,同 recording.readyrecordingUrl。有錄音且處理成功時才有;text session 與錄音失敗時省略。連結為短期有效(約 15 分鐘)—— 請儘快下載。
Call session 範例:
Text session 範例:

實作範例

event 欄位 dispatch 各事件的 receiver:

順序

session.finalized 對某個 session 一定最後到:它只在所有預期的步驟都完成後才觸發 —— text session 只等 session.ended;call session 等 session.endedcall.ended 加上 recording.ready Call session 的典型順序:
  1. call.transferred(只有轉接的通話才有,且在通話進行中就送出)
  2. session.ended
  3. call.ended
  4. recording.ready
  5. session.finalized
call.transferred 不參與 finalization:沒有轉接的通話一樣會照常收到 session.finalized,不會卡住等一個永遠不會來的事件。 Text session 則是 session.endedsession.finalized 緊接著到。 個別步驟(session.endedcall.endedrecording.ready)彼此之間在 edge case(例如使用者直接掛斷在 agent 還沒走到結尾節點時)沒有嚴格順序,而且 recording.ready 的 HTTP 送達可能跟它解鎖的 session.finalized 請求互相 race。可以依賴的是:收到 session.finalized 時錄音處理必定已結束,成功的錄音 URL 就在該 payload 裡。