Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.pathors.com/llms.txt

Use this file to discover all available pages before exploring further.

Each payload starts with an event field that identifies which lifecycle event fired. Treat the body as a discriminated union and branch on that field.
type LifecycleWebhookPayload =
  | SessionEndedPayload
  | CallEndedPayload
  | SessionFinalizedPayload;
Common fields on every event:
event
string
Discriminator. One of session.ended, call.ended, session.finalized.
sessionId
string
The Pathors session ID.
timestamp
string
ISO 8601 timestamp of when the event was emitted.

session.ended

Fires when the conversation has ended. For call sessions, the call’s final status and duration may not be available yet at this moment.
currentNodeId
string
Pathway node the agent was on when the conversation ended.
messagesCount
number
Total messages exchanged in the session.
extractedVariables
object
Variables extracted across the session, keyed by name.
reason
string
deprecated
Legacy alias for event from before multi-event support existed; Will be removed in a future version — new receivers should branch on event.
{
  "event": "session.ended",
  "sessionId": "2c4f9a13-7e6b-4d8a-9f25-c81e3a7b6d04",
  "timestamp": "2026-05-03T08:42:11.512Z",
  "currentNodeId": "ask_intention",
  "messagesCount": 8,
  "extractedVariables": {
    "name": "Nancy",
    "intention": "book a demo"
  },
  "reason": "session_ended"
}

call.ended

Fires when the call has ended. Call sessions only — text sessions never produce this event.
projectId
string
The project that owns the agent.
callStatus
string
Final domain status. One of userHangup, agentHangup, transferred, voicemail, errorTransferred, busy, userNoAnswer, userRejected, invalidNumber, sipTrunkFailure, agentNoAnswer, error, unknown, Ended.
callDuration
number
Call duration in seconds. 0 for non-traffic outcomes (busy, no-answer, invalid number).
{
  "event": "call.ended",
  "sessionId": "2c4f9a13-7e6b-4d8a-9f25-c81e3a7b6d04",
  "projectId": "8f3e2c91-4a7b-4d6e-a23c-9b1f5e8d4c20",
  "timestamp": "2026-05-03T08:42:14.022Z",
  "callStatus": "userHangup",
  "callDuration": 78
}

session.finalized

Fires once everything is done for this session. The payload merges the data you would otherwise piece together from session.ended and call.ended, so your receiver only has to handle one event. For text sessions, the call fields (callStatus, callDuration) are omitted entirely — not set to null. Branch on whether they’re present to discriminate call vs text:
if ("callStatus" in payload) {
  // call session — payload.callStatus, .callDuration are present
} else {
  // text session — call fields are absent
}
projectId
string
The project that owns the agent.
currentNodeId
string
Pathway node the agent was on when the conversation ended.
messagesCount
number
Total messages exchanged in the session.
extractedVariables
object
Variables extracted across the session.
callStatus
string
Call sessions only. Same as call.ended’s callStatus. Omitted for text sessions.
callDuration
number
Call sessions only. Same as call.ended’s callDuration. Omitted for text sessions.
Call session example:
{
  "event": "session.finalized",
  "sessionId": "2c4f9a13-7e6b-4d8a-9f25-c81e3a7b6d04",
  "projectId": "8f3e2c91-4a7b-4d6e-a23c-9b1f5e8d4c20",
  "timestamp": "2026-05-03T08:42:14.300Z",
  "currentNodeId": "ask_intention",
  "messagesCount": 8,
  "extractedVariables": {
    "name": "Nancy",
    "intention": "book a demo"
  },
  "callStatus": "userHangup",
  "callDuration": 78
}
Text session example:
{
  "event": "session.finalized",
  "sessionId": "5d8e1f2a-9c4b-4e7d-b3a8-6f9c2d5e8a01",
  "projectId": "8f3e2c91-4a7b-4d6e-a23c-9b1f5e8d4c20",
  "timestamp": "2026-05-03T08:48:15.288Z",
  "currentNodeId": "start",
  "messagesCount": 10,
  "extractedVariables": { "name": "Nancy" }
}

Implementation example

A receiver that handles all three events with event as the discriminator:
import express from "express";

const app = express();
app.use(express.json());

app.post("/pathors-webhook", async (req, res) => {
  const payload = req.body;

  switch (payload.event) {
    case "session.ended":
      await onSessionEnded(payload);
      break;

    case "call.ended":
      await onCallEnded(payload);
      break;

    case "session.finalized":
      if ("callStatus" in payload) {
        await onCallCompleted(payload);
      } else {
        await onTextCompleted(payload);
      }
      break;

    default:
      // Unknown event — accept and ignore so future Pathors event types
      // do not break your receiver.
      break;
  }

  res.status(200).json({ status: "ok" });
});

Ordering

If you subscribe to multiple events, session.finalized is always last for a given session. Typical order for a call session:
  1. session.ended
  2. call.ended
  3. session.finalized
For text sessions, session.ended and session.finalized arrive back-to-back. session.ended and call.ended are not strictly ordered with respect to each other in edge cases (e.g. hard hangup before the agent reaches its final node).