{
  "openapi": "3.0.3",
  "info": {
    "title": "TelAPI",
    "description": "TelAPI issues SDK session tokens, exposes endpoint runtime capabilities, receives provider webhooks under /api/v1/webhooks, and hosts the /ws/session WebSocket channel. Authenticated REST endpoints require a team API key in the X-API-Key header with the documented scope for each operation.",
    "version": "0.9.14-patch1"
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      },
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Team API key. Required scopes are documented on each authenticated endpoint."
      }
    },
    "schemas": {
      "ChannelMessage": {
        "type": "object",
        "description": "WebSocket message envelope exchanged on /ws/session. Include the payload object matching the message type.",
        "required": [
          "type",
          "sessionId",
          "messageId",
          "timestamp",
          "direction"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "chat",
              "browser_action",
              "action",
              "action_result",
              "audio",
              "status",
              "control",
              "reconnect",
              "ping",
              "pong",
              "error"
            ]
          },
          "sessionId": {
            "type": "string"
          },
          "messageId": {
            "type": "string",
            "format": "uuid"
          },
          "streamId": {
            "type": "string",
            "description": "Redis stream id included on replayed runtime-to-browser messages."
          },
          "timestamp": {
            "type": "number",
            "description": "Unix timestamp in milliseconds."
          },
          "direction": {
            "type": "string",
            "enum": [
              "to_browser",
              "to_ari"
            ]
          },
          "chat": {
            "type": "object",
            "description": "Present when type is chat.",
            "additionalProperties": true
          },
          "browserAction": {
            "type": "object",
            "description": "Present when type is browser_action.",
            "additionalProperties": true
          },
          "action": {
            "type": "object",
            "description": "Present when type is action.",
            "additionalProperties": true
          },
          "actionResult": {
            "type": "object",
            "description": "Present when type is action_result.",
            "additionalProperties": true
          },
          "audio": {
            "type": "object",
            "description": "Present when type is audio.",
            "additionalProperties": true
          },
          "status": {
            "type": "object",
            "description": "Present when type is status.",
            "additionalProperties": true
          },
          "control": {
            "type": "object",
            "description": "Present when type is control; supports enable_text_chat and disable_text_chat commands.",
            "additionalProperties": true
          },
          "reconnect": {
            "type": "object",
            "description": "Present when type is reconnect.",
            "additionalProperties": true
          },
          "error": {
            "type": "object",
            "description": "Present when type is error.",
            "additionalProperties": true
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "description": "Standard TelAPI error envelope.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable error code.",
                "example": "VALIDATION_ERROR"
              },
              "message": {
                "type": "string",
                "description": "Human-readable error summary.",
                "example": "Request validation failed"
              },
              "details": {
                "description": "Optional structured details, usually validation issues."
              },
              "requestId": {
                "type": "string",
                "description": "Fastify request id for log correlation when available."
              }
            }
          }
        }
      },
      "TobiBotControlEvent": {
        "type": "object",
        "required": [
          "name"
        ],
        "description": "Vodafone bot-originated control event embedded in a spoken message. This is the supported way to request hangup, transfer, or dynamic DTMF/STT/TTS/bot-delay config changes from TOBi callbacks.",
        "properties": {
          "name": {
            "type": "string",
            "enum": [
              "hangup",
              "transfer",
              "handover",
              "config"
            ],
            "description": "Control event name. handover is treated as transfer; config can update DTMF/STT/TTS/bot-delay sessionParams."
          },
          "value": {
            "type": "object",
            "additionalProperties": true,
            "description": "Payload for a Vodafone custom control event (`messages[].content.event.custom`). TelAPI maps hangup, transfer/handover, DTMF config, bot-delay config, and bounded STT/TTS config events to runtime commands for TelPhi.",
            "properties": {
              "sessionParams": {
                "type": "object",
                "additionalProperties": false,
                "description": "Dynamic runtime settings sent by TOBi in a config custom event. Missing keys keep the current call value. DTMF fields update digit handling; STT/TTS fields update bounded timeout/retry settings or select a provider from the configured fallback chain; bot-delay fields update comfort prompts and final no-input timing for the current call.",
                "properties": {
                  "sendDTMF": {
                    "type": "boolean",
                    "description": "Enables forwarding caller DTMF digits to TOBi."
                  },
                  "bargeInOnDTMF": {
                    "type": "boolean",
                    "description": "Allows DTMF input to interrupt current bot playback."
                  },
                  "bargeIn": {
                    "type": "boolean",
                    "description": "General bot barge-in flag. DTMF playback interruption requires this and bargeInOnDTMF to be true."
                  },
                  "bargeInMinWordCount": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Minimum word count threshold associated with bot barge-in configuration."
                  },
                  "dtmfCollect": {
                    "type": "boolean",
                    "description": "Collects multiple DTMF digits before forwarding them to TOBi."
                  },
                  "dtmfDigits": {
                    "type": "string",
                    "description": "Allowed DTMF symbols. Digits outside this set are ignored before collection or forwarding.",
                    "example": "0123456789#*"
                  },
                  "dtmfCollectInterDigitTimeoutMS": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Milliseconds to wait for another digit before forwarding the collected input."
                  },
                  "dtmfCollectMaxDigits": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Maximum digits to collect before forwarding. 0 disables the max-digits trigger."
                  },
                  "dtmfCollectSubmitDigit": {
                    "type": "string",
                    "maxLength": 1,
                    "description": "Submit digit that ends collection. Empty string disables submit-digit termination."
                  },
                  "sttProvider": {
                    "type": "string",
                    "description": "Selects an STT provider from the Delphi-configured primary/fallback chain. Does not create providers or change credentials.",
                    "example": "azure_stt"
                  },
                  "sttConnectionTimeoutMS": {
                    "type": "integer",
                    "minimum": 10,
                    "maximum": 600000,
                    "description": "Maximum wait for STT provider connection/request response."
                  },
                  "sttRetries": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 10,
                    "description": "Same-provider STT retries before fallback/failover."
                  },
                  "ttsProvider": {
                    "type": "string",
                    "description": "Selects a TTS provider from the Delphi-configured primary/fallback chain. Does not create providers or change credentials.",
                    "example": "openai_tts"
                  },
                  "ttsConnectionTimeoutMS": {
                    "type": "integer",
                    "minimum": 10,
                    "maximum": 600000,
                    "description": "Maximum wait for TTS provider response."
                  },
                  "ttsRetries": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 10,
                    "description": "Same-provider TTS retries before fallback/failover."
                  },
                  "botNoInputGiveUpTimeoutMS": {
                    "type": "integer",
                    "minimum": 1000,
                    "maximum": 120000,
                    "description": "Final no-input timeout in milliseconds before bot_timeout failover. Alias botNoInputGiveUpTimeoutMs is also accepted."
                  },
                  "botNoInputGiveUpTimeoutMs": {
                    "type": "integer",
                    "minimum": 1000,
                    "maximum": 120000,
                    "description": "Camel-case alias for botNoInputGiveUpTimeoutMS. Final no-input timeout before bot_timeout failover."
                  },
                  "botNoInputTimeoutMs": {
                    "type": "integer",
                    "minimum": 500,
                    "maximum": 60000,
                    "description": "Milliseconds of bot silence before playing the comfort prompt. Omit to keep the current value."
                  },
                  "botNoInputSpeech": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "TTS text for the comfort prompt while waiting for the bot response.",
                    "example": "One moment while I check that."
                  },
                  "botNoInputUrl": {
                    "type": "string",
                    "description": "Audio URL for the comfort prompt. Used as an alternative to generated speech.",
                    "example": "https://example.com/comfort.wav"
                  },
                  "botNoInputRetries": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 10,
                    "description": "How many times to repeat the comfort prompt while the bot remains silent."
                  },
                  "abortPlayOnBotMessage": {
                    "type": "boolean",
                    "description": "Stops comfort playback as soon as the bot responds."
                  },
                  "resumeRecognitionTimeoutMs": {
                    "type": "integer",
                    "minimum": 1000,
                    "maximum": 60000,
                    "description": "Milliseconds before resuming STT recognition after comfort playback."
                  },
                  "resumeRecognitionSpeech": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "TTS text to play when recognition is resumed.",
                    "example": "Are you still there?"
                  }
                }
              },
              "transferTarget": {
                "type": "string",
                "description": "Transfer destination. May be a phone number, tel: URI, sip: URI, or sips: URI."
              },
              "target": {
                "type": "string",
                "description": "Alternative name for transferTarget."
              },
              "transferMethod": {
                "type": "string",
                "enum": [
                  "sip_refer",
                  "sip_refer_replace",
                  "sip_invite",
                  "sip_invite_bridge",
                  "sip_invite_reinvite_p2p",
                  "sip_bye"
                ],
                "description": "Transfer method. sip_bye can omit transferTarget and reroute by BYE headers."
              },
              "transferReferOutcomeMode": {
                "type": "string",
                "enum": [
                  "blind",
                  "notify"
                ],
                "description": "REFER outcome behavior. blind requests no REFER NOTIFY result; notify requests provider REFER NOTIFY result when supported."
              },
              "transferReferredByURL": {
                "type": "string",
                "description": "SIP Referred-By value for transfer methods that use it."
              },
              "transferNotifications": {
                "type": "boolean",
                "description": "Whether the integration requests transfer outcome notifications."
              },
              "transferNotificationsHangupMS": {
                "type": "number",
                "minimum": 0,
                "maximum": 10000,
                "description": "Grace period in milliseconds before disconnecting after transfer."
              },
              "transferRoutingPool": {
                "type": "string",
                "description": "Routing pool selector for number targets. Ignored for full SIP URI targets."
              },
              "transferSipHeaders": {
                "type": "array",
                "maxItems": 100,
                "items": {
                  "type": "object",
                  "required": [
                    "name",
                    "value"
                  ],
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "SIP header name."
                    },
                    "value": {
                      "type": "string",
                      "description": "SIP header value."
                    }
                  }
                },
                "description": "SIP headers added to the transfer request. Total header name/value length must stay below 12000 characters."
              },
              "transferSipQueryParams": {
                "oneOf": [
                  {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "name",
                        "value"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "description": "SIP header name."
                        },
                        "value": {
                          "type": "string",
                          "description": "SIP header value."
                        }
                      }
                    }
                  }
                ],
                "description": "Refer-To / target URI query parameters. Accepts either an object map or an array of {name, value} pairs."
              },
              "sipQueryParams": {
                "oneOf": [
                  {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "name",
                        "value"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "description": "SIP header name."
                        },
                        "value": {
                          "type": "string",
                          "description": "SIP header value."
                        }
                      }
                    }
                  }
                ],
                "description": "Refer-To / target URI query parameters. Accepts either an object map or an array of {name, value} pairs."
              },
              "handoverReason": {
                "type": "string",
                "description": "Reason used by sip_bye disconnect-reconnect transfers."
              },
              "hangupReason": {
                "type": "string",
                "description": "Hangup reason, or fallback transfer reason for sip_bye."
              },
              "hangupSipHeaders": {
                "type": "array",
                "maxItems": 100,
                "items": {
                  "type": "object",
                  "required": [
                    "name",
                    "value"
                  ],
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "SIP header name."
                    },
                    "value": {
                      "type": "string",
                      "description": "SIP header value."
                    }
                  }
                },
                "description": "SIP headers added to the BYE message for hangup actions."
              },
              "botOperationResult": {
                "type": "string",
                "enum": [
                  "success",
                  "failure"
                ],
                "description": "Bot outcome recorded for CDR / call-status consumers."
              },
              "botOperationData": {
                "type": "object",
                "additionalProperties": true,
                "description": "Additional structured bot outcome data."
              }
            }
          }
        }
      },
      "TobiBotControlEventValue": {
        "type": "object",
        "additionalProperties": true,
        "description": "Payload for a Vodafone custom control event (`messages[].content.event.custom`). TelAPI maps hangup, transfer/handover, DTMF config, bot-delay config, and bounded STT/TTS config events to runtime commands for TelPhi.",
        "properties": {
          "sessionParams": {
            "type": "object",
            "additionalProperties": false,
            "description": "Dynamic runtime settings sent by TOBi in a config custom event. Missing keys keep the current call value. DTMF fields update digit handling; STT/TTS fields update bounded timeout/retry settings or select a provider from the configured fallback chain; bot-delay fields update comfort prompts and final no-input timing for the current call.",
            "properties": {
              "sendDTMF": {
                "type": "boolean",
                "description": "Enables forwarding caller DTMF digits to TOBi."
              },
              "bargeInOnDTMF": {
                "type": "boolean",
                "description": "Allows DTMF input to interrupt current bot playback."
              },
              "bargeIn": {
                "type": "boolean",
                "description": "General bot barge-in flag. DTMF playback interruption requires this and bargeInOnDTMF to be true."
              },
              "bargeInMinWordCount": {
                "type": "integer",
                "minimum": 0,
                "description": "Minimum word count threshold associated with bot barge-in configuration."
              },
              "dtmfCollect": {
                "type": "boolean",
                "description": "Collects multiple DTMF digits before forwarding them to TOBi."
              },
              "dtmfDigits": {
                "type": "string",
                "description": "Allowed DTMF symbols. Digits outside this set are ignored before collection or forwarding.",
                "example": "0123456789#*"
              },
              "dtmfCollectInterDigitTimeoutMS": {
                "type": "integer",
                "minimum": 0,
                "description": "Milliseconds to wait for another digit before forwarding the collected input."
              },
              "dtmfCollectMaxDigits": {
                "type": "integer",
                "minimum": 0,
                "description": "Maximum digits to collect before forwarding. 0 disables the max-digits trigger."
              },
              "dtmfCollectSubmitDigit": {
                "type": "string",
                "maxLength": 1,
                "description": "Submit digit that ends collection. Empty string disables submit-digit termination."
              },
              "sttProvider": {
                "type": "string",
                "description": "Selects an STT provider from the Delphi-configured primary/fallback chain. Does not create providers or change credentials.",
                "example": "azure_stt"
              },
              "sttConnectionTimeoutMS": {
                "type": "integer",
                "minimum": 10,
                "maximum": 600000,
                "description": "Maximum wait for STT provider connection/request response."
              },
              "sttRetries": {
                "type": "integer",
                "minimum": 0,
                "maximum": 10,
                "description": "Same-provider STT retries before fallback/failover."
              },
              "ttsProvider": {
                "type": "string",
                "description": "Selects a TTS provider from the Delphi-configured primary/fallback chain. Does not create providers or change credentials.",
                "example": "openai_tts"
              },
              "ttsConnectionTimeoutMS": {
                "type": "integer",
                "minimum": 10,
                "maximum": 600000,
                "description": "Maximum wait for TTS provider response."
              },
              "ttsRetries": {
                "type": "integer",
                "minimum": 0,
                "maximum": 10,
                "description": "Same-provider TTS retries before fallback/failover."
              },
              "botNoInputGiveUpTimeoutMS": {
                "type": "integer",
                "minimum": 1000,
                "maximum": 120000,
                "description": "Final no-input timeout in milliseconds before bot_timeout failover. Alias botNoInputGiveUpTimeoutMs is also accepted."
              },
              "botNoInputGiveUpTimeoutMs": {
                "type": "integer",
                "minimum": 1000,
                "maximum": 120000,
                "description": "Camel-case alias for botNoInputGiveUpTimeoutMS. Final no-input timeout before bot_timeout failover."
              },
              "botNoInputTimeoutMs": {
                "type": "integer",
                "minimum": 500,
                "maximum": 60000,
                "description": "Milliseconds of bot silence before playing the comfort prompt. Omit to keep the current value."
              },
              "botNoInputSpeech": {
                "type": "string",
                "maxLength": 500,
                "description": "TTS text for the comfort prompt while waiting for the bot response.",
                "example": "One moment while I check that."
              },
              "botNoInputUrl": {
                "type": "string",
                "description": "Audio URL for the comfort prompt. Used as an alternative to generated speech.",
                "example": "https://example.com/comfort.wav"
              },
              "botNoInputRetries": {
                "type": "integer",
                "minimum": 0,
                "maximum": 10,
                "description": "How many times to repeat the comfort prompt while the bot remains silent."
              },
              "abortPlayOnBotMessage": {
                "type": "boolean",
                "description": "Stops comfort playback as soon as the bot responds."
              },
              "resumeRecognitionTimeoutMs": {
                "type": "integer",
                "minimum": 1000,
                "maximum": 60000,
                "description": "Milliseconds before resuming STT recognition after comfort playback."
              },
              "resumeRecognitionSpeech": {
                "type": "string",
                "maxLength": 500,
                "description": "TTS text to play when recognition is resumed.",
                "example": "Are you still there?"
              }
            }
          },
          "transferTarget": {
            "type": "string",
            "description": "Transfer destination. May be a phone number, tel: URI, sip: URI, or sips: URI."
          },
          "target": {
            "type": "string",
            "description": "Alternative name for transferTarget."
          },
          "transferMethod": {
            "type": "string",
            "enum": [
              "sip_refer",
              "sip_refer_replace",
              "sip_invite",
              "sip_invite_bridge",
              "sip_invite_reinvite_p2p",
              "sip_bye"
            ],
            "description": "Transfer method. sip_bye can omit transferTarget and reroute by BYE headers."
          },
          "transferReferOutcomeMode": {
            "type": "string",
            "enum": [
              "blind",
              "notify"
            ],
            "description": "REFER outcome behavior. blind requests no REFER NOTIFY result; notify requests provider REFER NOTIFY result when supported."
          },
          "transferReferredByURL": {
            "type": "string",
            "description": "SIP Referred-By value for transfer methods that use it."
          },
          "transferNotifications": {
            "type": "boolean",
            "description": "Whether the integration requests transfer outcome notifications."
          },
          "transferNotificationsHangupMS": {
            "type": "number",
            "minimum": 0,
            "maximum": 10000,
            "description": "Grace period in milliseconds before disconnecting after transfer."
          },
          "transferRoutingPool": {
            "type": "string",
            "description": "Routing pool selector for number targets. Ignored for full SIP URI targets."
          },
          "transferSipHeaders": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "type": "object",
              "required": [
                "name",
                "value"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "description": "SIP header name."
                },
                "value": {
                  "type": "string",
                  "description": "SIP header value."
                }
              }
            },
            "description": "SIP headers added to the transfer request. Total header name/value length must stay below 12000 characters."
          },
          "transferSipQueryParams": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                }
              },
              {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "name",
                    "value"
                  ],
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "SIP header name."
                    },
                    "value": {
                      "type": "string",
                      "description": "SIP header value."
                    }
                  }
                }
              }
            ],
            "description": "Refer-To / target URI query parameters. Accepts either an object map or an array of {name, value} pairs."
          },
          "sipQueryParams": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                }
              },
              {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "name",
                    "value"
                  ],
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "SIP header name."
                    },
                    "value": {
                      "type": "string",
                      "description": "SIP header value."
                    }
                  }
                }
              }
            ],
            "description": "Refer-To / target URI query parameters. Accepts either an object map or an array of {name, value} pairs."
          },
          "handoverReason": {
            "type": "string",
            "description": "Reason used by sip_bye disconnect-reconnect transfers."
          },
          "hangupReason": {
            "type": "string",
            "description": "Hangup reason, or fallback transfer reason for sip_bye."
          },
          "hangupSipHeaders": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "type": "object",
              "required": [
                "name",
                "value"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "description": "SIP header name."
                },
                "value": {
                  "type": "string",
                  "description": "SIP header value."
                }
              }
            },
            "description": "SIP headers added to the BYE message for hangup actions."
          },
          "botOperationResult": {
            "type": "string",
            "enum": [
              "success",
              "failure"
            ],
            "description": "Bot outcome recorded for CDR / call-status consumers."
          },
          "botOperationData": {
            "type": "object",
            "additionalProperties": true,
            "description": "Additional structured bot outcome data."
          }
        }
      },
      "TobiCallbackBody": {
        "type": "object",
        "description": "TOBi callback payload sent by Vodafone (SendMessageResponse). TelAPI requires conversation.identifier.id so the callback can be correlated to the TOBi channel.",
        "properties": {
          "conversation": {
            "type": "object",
            "properties": {
              "identifier": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "TOBi conversation identifier. Required by TelAPI."
                  },
                  "name": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "from": {
            "type": "object",
            "properties": {
              "identifier": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  }
                }
              },
              "avatar": {
                "type": "string"
              }
            }
          },
          "messages": {
            "type": "object",
            "properties": {
              "timestamp": {
                "type": "string",
                "format": "date-time",
                "description": "Message timestamp as an RFC 3339 / ISO 8601 date-time with timezone.",
                "example": "2026-05-06T10:15:30.000Z"
              },
              "message": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "content"
                  ],
                  "properties": {
                    "messageId": {
                      "type": "number"
                    },
                    "relatesTo": {
                      "type": "number"
                    },
                    "sequence": {
                      "type": "number"
                    },
                    "content": {
                      "type": "object",
                      "properties": {
                        "textMessage": {
                          "type": "object",
                          "properties": {
                            "textPlain": {
                              "type": "string"
                            },
                            "textHtml": {
                              "type": "string"
                            },
                            "textMarkdown": {
                              "type": "string"
                            }
                          }
                        },
                        "voiceMessage": {
                          "type": "string"
                        },
                        "event": {
                          "type": "object",
                          "properties": {
                            "typing": {
                              "type": "object",
                              "required": [
                                "enable"
                              ],
                              "properties": {
                                "enable": {
                                  "type": "boolean"
                                },
                                "duration": {
                                  "type": "number"
                                }
                              }
                            },
                            "pause": {
                              "type": "number"
                            },
                            "messageRead": {
                              "type": "string"
                            },
                            "noInputExpected": {
                              "type": "boolean"
                            },
                            "custom": {
                              "type": "object",
                              "required": [
                                "name"
                              ],
                              "description": "Vodafone bot-originated control event embedded in a spoken message. This is the supported way to request hangup, transfer, or dynamic DTMF/STT/TTS/bot-delay config changes from TOBi callbacks.",
                              "properties": {
                                "name": {
                                  "type": "string",
                                  "enum": [
                                    "hangup",
                                    "transfer",
                                    "handover",
                                    "config"
                                  ],
                                  "description": "Control event name. handover is treated as transfer; config can update DTMF/STT/TTS/bot-delay sessionParams."
                                },
                                "value": {
                                  "type": "object",
                                  "additionalProperties": true,
                                  "description": "Payload for a Vodafone custom control event (`messages[].content.event.custom`). TelAPI maps hangup, transfer/handover, DTMF config, bot-delay config, and bounded STT/TTS config events to runtime commands for TelPhi.",
                                  "properties": {
                                    "sessionParams": {
                                      "type": "object",
                                      "additionalProperties": false,
                                      "description": "Dynamic runtime settings sent by TOBi in a config custom event. Missing keys keep the current call value. DTMF fields update digit handling; STT/TTS fields update bounded timeout/retry settings or select a provider from the configured fallback chain; bot-delay fields update comfort prompts and final no-input timing for the current call.",
                                      "properties": {
                                        "sendDTMF": {
                                          "type": "boolean",
                                          "description": "Enables forwarding caller DTMF digits to TOBi."
                                        },
                                        "bargeInOnDTMF": {
                                          "type": "boolean",
                                          "description": "Allows DTMF input to interrupt current bot playback."
                                        },
                                        "bargeIn": {
                                          "type": "boolean",
                                          "description": "General bot barge-in flag. DTMF playback interruption requires this and bargeInOnDTMF to be true."
                                        },
                                        "bargeInMinWordCount": {
                                          "type": "integer",
                                          "minimum": 0,
                                          "description": "Minimum word count threshold associated with bot barge-in configuration."
                                        },
                                        "dtmfCollect": {
                                          "type": "boolean",
                                          "description": "Collects multiple DTMF digits before forwarding them to TOBi."
                                        },
                                        "dtmfDigits": {
                                          "type": "string",
                                          "description": "Allowed DTMF symbols. Digits outside this set are ignored before collection or forwarding.",
                                          "example": "0123456789#*"
                                        },
                                        "dtmfCollectInterDigitTimeoutMS": {
                                          "type": "integer",
                                          "minimum": 0,
                                          "description": "Milliseconds to wait for another digit before forwarding the collected input."
                                        },
                                        "dtmfCollectMaxDigits": {
                                          "type": "integer",
                                          "minimum": 0,
                                          "description": "Maximum digits to collect before forwarding. 0 disables the max-digits trigger."
                                        },
                                        "dtmfCollectSubmitDigit": {
                                          "type": "string",
                                          "maxLength": 1,
                                          "description": "Submit digit that ends collection. Empty string disables submit-digit termination."
                                        },
                                        "sttProvider": {
                                          "type": "string",
                                          "description": "Selects an STT provider from the Delphi-configured primary/fallback chain. Does not create providers or change credentials.",
                                          "example": "azure_stt"
                                        },
                                        "sttConnectionTimeoutMS": {
                                          "type": "integer",
                                          "minimum": 10,
                                          "maximum": 600000,
                                          "description": "Maximum wait for STT provider connection/request response."
                                        },
                                        "sttRetries": {
                                          "type": "integer",
                                          "minimum": 0,
                                          "maximum": 10,
                                          "description": "Same-provider STT retries before fallback/failover."
                                        },
                                        "ttsProvider": {
                                          "type": "string",
                                          "description": "Selects a TTS provider from the Delphi-configured primary/fallback chain. Does not create providers or change credentials.",
                                          "example": "openai_tts"
                                        },
                                        "ttsConnectionTimeoutMS": {
                                          "type": "integer",
                                          "minimum": 10,
                                          "maximum": 600000,
                                          "description": "Maximum wait for TTS provider response."
                                        },
                                        "ttsRetries": {
                                          "type": "integer",
                                          "minimum": 0,
                                          "maximum": 10,
                                          "description": "Same-provider TTS retries before fallback/failover."
                                        },
                                        "botNoInputGiveUpTimeoutMS": {
                                          "type": "integer",
                                          "minimum": 1000,
                                          "maximum": 120000,
                                          "description": "Final no-input timeout in milliseconds before bot_timeout failover. Alias botNoInputGiveUpTimeoutMs is also accepted."
                                        },
                                        "botNoInputGiveUpTimeoutMs": {
                                          "type": "integer",
                                          "minimum": 1000,
                                          "maximum": 120000,
                                          "description": "Camel-case alias for botNoInputGiveUpTimeoutMS. Final no-input timeout before bot_timeout failover."
                                        },
                                        "botNoInputTimeoutMs": {
                                          "type": "integer",
                                          "minimum": 500,
                                          "maximum": 60000,
                                          "description": "Milliseconds of bot silence before playing the comfort prompt. Omit to keep the current value."
                                        },
                                        "botNoInputSpeech": {
                                          "type": "string",
                                          "maxLength": 500,
                                          "description": "TTS text for the comfort prompt while waiting for the bot response.",
                                          "example": "One moment while I check that."
                                        },
                                        "botNoInputUrl": {
                                          "type": "string",
                                          "description": "Audio URL for the comfort prompt. Used as an alternative to generated speech.",
                                          "example": "https://example.com/comfort.wav"
                                        },
                                        "botNoInputRetries": {
                                          "type": "integer",
                                          "minimum": 0,
                                          "maximum": 10,
                                          "description": "How many times to repeat the comfort prompt while the bot remains silent."
                                        },
                                        "abortPlayOnBotMessage": {
                                          "type": "boolean",
                                          "description": "Stops comfort playback as soon as the bot responds."
                                        },
                                        "resumeRecognitionTimeoutMs": {
                                          "type": "integer",
                                          "minimum": 1000,
                                          "maximum": 60000,
                                          "description": "Milliseconds before resuming STT recognition after comfort playback."
                                        },
                                        "resumeRecognitionSpeech": {
                                          "type": "string",
                                          "maxLength": 500,
                                          "description": "TTS text to play when recognition is resumed.",
                                          "example": "Are you still there?"
                                        }
                                      }
                                    },
                                    "transferTarget": {
                                      "type": "string",
                                      "description": "Transfer destination. May be a phone number, tel: URI, sip: URI, or sips: URI."
                                    },
                                    "target": {
                                      "type": "string",
                                      "description": "Alternative name for transferTarget."
                                    },
                                    "transferMethod": {
                                      "type": "string",
                                      "enum": [
                                        "sip_refer",
                                        "sip_refer_replace",
                                        "sip_invite",
                                        "sip_invite_bridge",
                                        "sip_invite_reinvite_p2p",
                                        "sip_bye"
                                      ],
                                      "description": "Transfer method. sip_bye can omit transferTarget and reroute by BYE headers."
                                    },
                                    "transferReferOutcomeMode": {
                                      "type": "string",
                                      "enum": [
                                        "blind",
                                        "notify"
                                      ],
                                      "description": "REFER outcome behavior. blind requests no REFER NOTIFY result; notify requests provider REFER NOTIFY result when supported."
                                    },
                                    "transferReferredByURL": {
                                      "type": "string",
                                      "description": "SIP Referred-By value for transfer methods that use it."
                                    },
                                    "transferNotifications": {
                                      "type": "boolean",
                                      "description": "Whether the integration requests transfer outcome notifications."
                                    },
                                    "transferNotificationsHangupMS": {
                                      "type": "number",
                                      "minimum": 0,
                                      "maximum": 10000,
                                      "description": "Grace period in milliseconds before disconnecting after transfer."
                                    },
                                    "transferRoutingPool": {
                                      "type": "string",
                                      "description": "Routing pool selector for number targets. Ignored for full SIP URI targets."
                                    },
                                    "transferSipHeaders": {
                                      "type": "array",
                                      "maxItems": 100,
                                      "items": {
                                        "type": "object",
                                        "required": [
                                          "name",
                                          "value"
                                        ],
                                        "properties": {
                                          "name": {
                                            "type": "string",
                                            "description": "SIP header name."
                                          },
                                          "value": {
                                            "type": "string",
                                            "description": "SIP header value."
                                          }
                                        }
                                      },
                                      "description": "SIP headers added to the transfer request. Total header name/value length must stay below 12000 characters."
                                    },
                                    "transferSipQueryParams": {
                                      "oneOf": [
                                        {
                                          "type": "object",
                                          "additionalProperties": {
                                            "type": "string"
                                          }
                                        },
                                        {
                                          "type": "array",
                                          "items": {
                                            "type": "object",
                                            "required": [
                                              "name",
                                              "value"
                                            ],
                                            "properties": {
                                              "name": {
                                                "type": "string",
                                                "description": "SIP header name."
                                              },
                                              "value": {
                                                "type": "string",
                                                "description": "SIP header value."
                                              }
                                            }
                                          }
                                        }
                                      ],
                                      "description": "Refer-To / target URI query parameters. Accepts either an object map or an array of {name, value} pairs."
                                    },
                                    "sipQueryParams": {
                                      "oneOf": [
                                        {
                                          "type": "object",
                                          "additionalProperties": {
                                            "type": "string"
                                          }
                                        },
                                        {
                                          "type": "array",
                                          "items": {
                                            "type": "object",
                                            "required": [
                                              "name",
                                              "value"
                                            ],
                                            "properties": {
                                              "name": {
                                                "type": "string",
                                                "description": "SIP header name."
                                              },
                                              "value": {
                                                "type": "string",
                                                "description": "SIP header value."
                                              }
                                            }
                                          }
                                        }
                                      ],
                                      "description": "Refer-To / target URI query parameters. Accepts either an object map or an array of {name, value} pairs."
                                    },
                                    "handoverReason": {
                                      "type": "string",
                                      "description": "Reason used by sip_bye disconnect-reconnect transfers."
                                    },
                                    "hangupReason": {
                                      "type": "string",
                                      "description": "Hangup reason, or fallback transfer reason for sip_bye."
                                    },
                                    "hangupSipHeaders": {
                                      "type": "array",
                                      "maxItems": 100,
                                      "items": {
                                        "type": "object",
                                        "required": [
                                          "name",
                                          "value"
                                        ],
                                        "properties": {
                                          "name": {
                                            "type": "string",
                                            "description": "SIP header name."
                                          },
                                          "value": {
                                            "type": "string",
                                            "description": "SIP header value."
                                          }
                                        }
                                      },
                                      "description": "SIP headers added to the BYE message for hangup actions."
                                    },
                                    "botOperationResult": {
                                      "type": "string",
                                      "enum": [
                                        "success",
                                        "failure"
                                      ],
                                      "description": "Bot outcome recorded for CDR / call-status consumers."
                                    },
                                    "botOperationData": {
                                      "type": "object",
                                      "additionalProperties": true,
                                      "description": "Additional structured bot outcome data."
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "Legacy provider metadata. Vodafone recommends channelData instead, but TelAPI forwards metadata when present."
          },
          "channelData": {
            "type": "array",
            "description": "Outgoing channel data returned by TOBi on callbacks (Vodafone OutgoingChannelData). This is bot-to-platform context such as scenario flags or routing hints.\n\nDo not confuse this with Delphi inbound channel-data forwarding configured in TelWeb (Flow Builder → channelDataRules), which maps SIP INVITE headers into channelData on create-conversation requests sent to TOBi. Callback channelData travels in the opposite direction and is forwarded opaquely on the TelPhi Redis stream for observability.",
            "items": {
              "type": "object",
              "required": [
                "key",
                "value"
              ],
              "description": "Single TOBi channel-data variable. Matches Vodafone ChannelDataEntry on bot callbacks.",
              "properties": {
                "key": {
                  "type": "string",
                  "description": "Variable name exposed by the bot or channel platform.",
                  "example": "scenarioTriggered"
                },
                "value": {
                  "description": "Variable value. May be a string, number, boolean, object, or array depending on the bot journey."
                },
                "sensitiveData": {
                  "type": "boolean",
                  "description": "When true, marks the value as sensitive for provider-side logging and storage policies."
                }
              }
            },
            "example": [
              {
                "key": "scenarioTriggered",
                "value": "Billing information"
              },
              {
                "key": "phoneCallbackRequired",
                "value": true
              }
            ]
          },
          "nluData": {
            "type": "object",
            "properties": {
              "intents": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": true
                }
              },
              "entities": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": true
                }
              },
              "sentiment": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        }
      },
      "TobiCallbackSuccessResponse": {
        "type": "object",
        "description": "Acknowledgement that TelAPI accepted the callback and published it to the TelPhi consumer stream.",
        "required": [
          "received",
          "streamId"
        ],
        "properties": {
          "received": {
            "type": "boolean",
            "enum": [
              true
            ],
            "description": "Always true when the callback payload is accepted."
          },
          "streamId": {
            "type": "string",
            "description": "Redis stream entry id for the forwarded TOBi payload. The same acknowledgement shape is returned for message, hangup, and transfer callbacks.",
            "example": "1778022191343-0"
          }
        }
      },
      "TobiChannelDataEntry": {
        "type": "object",
        "required": [
          "key",
          "value"
        ],
        "description": "Single TOBi channel-data variable. Matches Vodafone ChannelDataEntry on bot callbacks.",
        "properties": {
          "key": {
            "type": "string",
            "description": "Variable name exposed by the bot or channel platform.",
            "example": "scenarioTriggered"
          },
          "value": {
            "description": "Variable value. May be a string, number, boolean, object, or array depending on the bot journey."
          },
          "sensitiveData": {
            "type": "boolean",
            "description": "When true, marks the value as sensitive for provider-side logging and storage policies."
          }
        }
      },
      "TobiOutgoingChannelData": {
        "type": "array",
        "description": "Outgoing channel data returned by TOBi on callbacks (Vodafone OutgoingChannelData). This is bot-to-platform context such as scenario flags or routing hints.\n\nDo not confuse this with Delphi inbound channel-data forwarding configured in TelWeb (Flow Builder → channelDataRules), which maps SIP INVITE headers into channelData on create-conversation requests sent to TOBi. Callback channelData travels in the opposite direction and is forwarded opaquely on the TelPhi Redis stream for observability.",
        "items": {
          "type": "object",
          "required": [
            "key",
            "value"
          ],
          "description": "Single TOBi channel-data variable. Matches Vodafone ChannelDataEntry on bot callbacks.",
          "properties": {
            "key": {
              "type": "string",
              "description": "Variable name exposed by the bot or channel platform.",
              "example": "scenarioTriggered"
            },
            "value": {
              "description": "Variable value. May be a string, number, boolean, object, or array depending on the bot journey."
            },
            "sensitiveData": {
              "type": "boolean",
              "description": "When true, marks the value as sensitive for provider-side logging and storage policies."
            }
          }
        },
        "example": [
          {
            "key": "scenarioTriggered",
            "value": "Billing information"
          },
          {
            "key": "phoneCallbackRequired",
            "value": true
          }
        ]
      }
    }
  },
  "paths": {
    "/health": {
      "get": {
        "summary": "Service health",
        "tags": [
          "Health"
        ],
        "description": "Returns 200 when required dependencies (Redis) are reachable; otherwise 503. Minimal body — monitor detail via OpenTelemetry.",
        "responses": {
          "200": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "ok"
                      ]
                    }
                  }
                },
                "example": {
                  "status": "ok"
                }
              }
            }
          },
          "503": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "error"
                      ]
                    }
                  }
                },
                "example": {
                  "status": "error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/webhooks/tobi/{channelId}": {
      "post": {
        "summary": "Receive TOBi chatbot callback",
        "tags": [
          "Webhooks"
        ],
        "description": "Receives message callbacks from a TOBi chatbot and forwards them to TelPhi. Call/trace correlation is loaded from the Redis callback session written by TelPhi. The callback URL only needs `?token=`; legacy trace/call query params are ignored when a session exists. Each callback must include the per-conversation callback token in the token header.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "TOBi callback payload sent by Vodafone (SendMessageResponse). TelAPI requires conversation.identifier.id so the callback can be correlated to the TOBi channel.",
                "properties": {
                  "conversation": {
                    "type": "object",
                    "properties": {
                      "identifier": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "TOBi conversation identifier. Required by TelAPI."
                          },
                          "name": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  },
                  "from": {
                    "type": "object",
                    "properties": {
                      "identifier": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          }
                        }
                      },
                      "avatar": {
                        "type": "string"
                      }
                    }
                  },
                  "messages": {
                    "type": "object",
                    "properties": {
                      "timestamp": {
                        "type": "string",
                        "format": "date-time",
                        "description": "Message timestamp as an RFC 3339 / ISO 8601 date-time with timezone.",
                        "example": "2026-05-06T10:15:30.000Z"
                      },
                      "message": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "required": [
                            "content"
                          ],
                          "properties": {
                            "messageId": {
                              "type": "number"
                            },
                            "relatesTo": {
                              "type": "number"
                            },
                            "sequence": {
                              "type": "number"
                            },
                            "content": {
                              "type": "object",
                              "properties": {
                                "textMessage": {
                                  "type": "object",
                                  "properties": {
                                    "textPlain": {
                                      "type": "string"
                                    },
                                    "textHtml": {
                                      "type": "string"
                                    },
                                    "textMarkdown": {
                                      "type": "string"
                                    }
                                  }
                                },
                                "voiceMessage": {
                                  "type": "string"
                                },
                                "event": {
                                  "type": "object",
                                  "properties": {
                                    "typing": {
                                      "type": "object",
                                      "required": [
                                        "enable"
                                      ],
                                      "properties": {
                                        "enable": {
                                          "type": "boolean"
                                        },
                                        "duration": {
                                          "type": "number"
                                        }
                                      }
                                    },
                                    "pause": {
                                      "type": "number"
                                    },
                                    "messageRead": {
                                      "type": "string"
                                    },
                                    "noInputExpected": {
                                      "type": "boolean"
                                    },
                                    "custom": {
                                      "type": "object",
                                      "required": [
                                        "name"
                                      ],
                                      "description": "Vodafone bot-originated control event embedded in a spoken message. This is the supported way to request hangup, transfer, or dynamic DTMF/STT/TTS/bot-delay config changes from TOBi callbacks.",
                                      "properties": {
                                        "name": {
                                          "type": "string",
                                          "enum": [
                                            "hangup",
                                            "transfer",
                                            "handover",
                                            "config"
                                          ],
                                          "description": "Control event name. handover is treated as transfer; config can update DTMF/STT/TTS/bot-delay sessionParams."
                                        },
                                        "value": {
                                          "type": "object",
                                          "additionalProperties": true,
                                          "description": "Payload for a Vodafone custom control event (`messages[].content.event.custom`). TelAPI maps hangup, transfer/handover, DTMF config, bot-delay config, and bounded STT/TTS config events to runtime commands for TelPhi.",
                                          "properties": {
                                            "sessionParams": {
                                              "type": "object",
                                              "additionalProperties": false,
                                              "description": "Dynamic runtime settings sent by TOBi in a config custom event. Missing keys keep the current call value. DTMF fields update digit handling; STT/TTS fields update bounded timeout/retry settings or select a provider from the configured fallback chain; bot-delay fields update comfort prompts and final no-input timing for the current call.",
                                              "properties": {
                                                "sendDTMF": {
                                                  "type": "boolean",
                                                  "description": "Enables forwarding caller DTMF digits to TOBi."
                                                },
                                                "bargeInOnDTMF": {
                                                  "type": "boolean",
                                                  "description": "Allows DTMF input to interrupt current bot playback."
                                                },
                                                "bargeIn": {
                                                  "type": "boolean",
                                                  "description": "General bot barge-in flag. DTMF playback interruption requires this and bargeInOnDTMF to be true."
                                                },
                                                "bargeInMinWordCount": {
                                                  "type": "integer",
                                                  "minimum": 0,
                                                  "description": "Minimum word count threshold associated with bot barge-in configuration."
                                                },
                                                "dtmfCollect": {
                                                  "type": "boolean",
                                                  "description": "Collects multiple DTMF digits before forwarding them to TOBi."
                                                },
                                                "dtmfDigits": {
                                                  "type": "string",
                                                  "description": "Allowed DTMF symbols. Digits outside this set are ignored before collection or forwarding.",
                                                  "example": "0123456789#*"
                                                },
                                                "dtmfCollectInterDigitTimeoutMS": {
                                                  "type": "integer",
                                                  "minimum": 0,
                                                  "description": "Milliseconds to wait for another digit before forwarding the collected input."
                                                },
                                                "dtmfCollectMaxDigits": {
                                                  "type": "integer",
                                                  "minimum": 0,
                                                  "description": "Maximum digits to collect before forwarding. 0 disables the max-digits trigger."
                                                },
                                                "dtmfCollectSubmitDigit": {
                                                  "type": "string",
                                                  "maxLength": 1,
                                                  "description": "Submit digit that ends collection. Empty string disables submit-digit termination."
                                                },
                                                "sttProvider": {
                                                  "type": "string",
                                                  "description": "Selects an STT provider from the Delphi-configured primary/fallback chain. Does not create providers or change credentials.",
                                                  "example": "azure_stt"
                                                },
                                                "sttConnectionTimeoutMS": {
                                                  "type": "integer",
                                                  "minimum": 10,
                                                  "maximum": 600000,
                                                  "description": "Maximum wait for STT provider connection/request response."
                                                },
                                                "sttRetries": {
                                                  "type": "integer",
                                                  "minimum": 0,
                                                  "maximum": 10,
                                                  "description": "Same-provider STT retries before fallback/failover."
                                                },
                                                "ttsProvider": {
                                                  "type": "string",
                                                  "description": "Selects a TTS provider from the Delphi-configured primary/fallback chain. Does not create providers or change credentials.",
                                                  "example": "openai_tts"
                                                },
                                                "ttsConnectionTimeoutMS": {
                                                  "type": "integer",
                                                  "minimum": 10,
                                                  "maximum": 600000,
                                                  "description": "Maximum wait for TTS provider response."
                                                },
                                                "ttsRetries": {
                                                  "type": "integer",
                                                  "minimum": 0,
                                                  "maximum": 10,
                                                  "description": "Same-provider TTS retries before fallback/failover."
                                                },
                                                "botNoInputGiveUpTimeoutMS": {
                                                  "type": "integer",
                                                  "minimum": 1000,
                                                  "maximum": 120000,
                                                  "description": "Final no-input timeout in milliseconds before bot_timeout failover. Alias botNoInputGiveUpTimeoutMs is also accepted."
                                                },
                                                "botNoInputGiveUpTimeoutMs": {
                                                  "type": "integer",
                                                  "minimum": 1000,
                                                  "maximum": 120000,
                                                  "description": "Camel-case alias for botNoInputGiveUpTimeoutMS. Final no-input timeout before bot_timeout failover."
                                                },
                                                "botNoInputTimeoutMs": {
                                                  "type": "integer",
                                                  "minimum": 500,
                                                  "maximum": 60000,
                                                  "description": "Milliseconds of bot silence before playing the comfort prompt. Omit to keep the current value."
                                                },
                                                "botNoInputSpeech": {
                                                  "type": "string",
                                                  "maxLength": 500,
                                                  "description": "TTS text for the comfort prompt while waiting for the bot response.",
                                                  "example": "One moment while I check that."
                                                },
                                                "botNoInputUrl": {
                                                  "type": "string",
                                                  "description": "Audio URL for the comfort prompt. Used as an alternative to generated speech.",
                                                  "example": "https://example.com/comfort.wav"
                                                },
                                                "botNoInputRetries": {
                                                  "type": "integer",
                                                  "minimum": 0,
                                                  "maximum": 10,
                                                  "description": "How many times to repeat the comfort prompt while the bot remains silent."
                                                },
                                                "abortPlayOnBotMessage": {
                                                  "type": "boolean",
                                                  "description": "Stops comfort playback as soon as the bot responds."
                                                },
                                                "resumeRecognitionTimeoutMs": {
                                                  "type": "integer",
                                                  "minimum": 1000,
                                                  "maximum": 60000,
                                                  "description": "Milliseconds before resuming STT recognition after comfort playback."
                                                },
                                                "resumeRecognitionSpeech": {
                                                  "type": "string",
                                                  "maxLength": 500,
                                                  "description": "TTS text to play when recognition is resumed.",
                                                  "example": "Are you still there?"
                                                }
                                              }
                                            },
                                            "transferTarget": {
                                              "type": "string",
                                              "description": "Transfer destination. May be a phone number, tel: URI, sip: URI, or sips: URI."
                                            },
                                            "target": {
                                              "type": "string",
                                              "description": "Alternative name for transferTarget."
                                            },
                                            "transferMethod": {
                                              "type": "string",
                                              "enum": [
                                                "sip_refer",
                                                "sip_refer_replace",
                                                "sip_invite",
                                                "sip_invite_bridge",
                                                "sip_invite_reinvite_p2p",
                                                "sip_bye"
                                              ],
                                              "description": "Transfer method. sip_bye can omit transferTarget and reroute by BYE headers."
                                            },
                                            "transferReferOutcomeMode": {
                                              "type": "string",
                                              "enum": [
                                                "blind",
                                                "notify"
                                              ],
                                              "description": "REFER outcome behavior. blind requests no REFER NOTIFY result; notify requests provider REFER NOTIFY result when supported."
                                            },
                                            "transferReferredByURL": {
                                              "type": "string",
                                              "description": "SIP Referred-By value for transfer methods that use it."
                                            },
                                            "transferNotifications": {
                                              "type": "boolean",
                                              "description": "Whether the integration requests transfer outcome notifications."
                                            },
                                            "transferNotificationsHangupMS": {
                                              "type": "number",
                                              "minimum": 0,
                                              "maximum": 10000,
                                              "description": "Grace period in milliseconds before disconnecting after transfer."
                                            },
                                            "transferRoutingPool": {
                                              "type": "string",
                                              "description": "Routing pool selector for number targets. Ignored for full SIP URI targets."
                                            },
                                            "transferSipHeaders": {
                                              "type": "array",
                                              "maxItems": 100,
                                              "items": {
                                                "type": "object",
                                                "required": [
                                                  "name",
                                                  "value"
                                                ],
                                                "properties": {
                                                  "name": {
                                                    "type": "string",
                                                    "description": "SIP header name."
                                                  },
                                                  "value": {
                                                    "type": "string",
                                                    "description": "SIP header value."
                                                  }
                                                }
                                              },
                                              "description": "SIP headers added to the transfer request. Total header name/value length must stay below 12000 characters."
                                            },
                                            "transferSipQueryParams": {
                                              "oneOf": [
                                                {
                                                  "type": "object",
                                                  "additionalProperties": {
                                                    "type": "string"
                                                  }
                                                },
                                                {
                                                  "type": "array",
                                                  "items": {
                                                    "type": "object",
                                                    "required": [
                                                      "name",
                                                      "value"
                                                    ],
                                                    "properties": {
                                                      "name": {
                                                        "type": "string",
                                                        "description": "SIP header name."
                                                      },
                                                      "value": {
                                                        "type": "string",
                                                        "description": "SIP header value."
                                                      }
                                                    }
                                                  }
                                                }
                                              ],
                                              "description": "Refer-To / target URI query parameters. Accepts either an object map or an array of {name, value} pairs."
                                            },
                                            "sipQueryParams": {
                                              "oneOf": [
                                                {
                                                  "type": "object",
                                                  "additionalProperties": {
                                                    "type": "string"
                                                  }
                                                },
                                                {
                                                  "type": "array",
                                                  "items": {
                                                    "type": "object",
                                                    "required": [
                                                      "name",
                                                      "value"
                                                    ],
                                                    "properties": {
                                                      "name": {
                                                        "type": "string",
                                                        "description": "SIP header name."
                                                      },
                                                      "value": {
                                                        "type": "string",
                                                        "description": "SIP header value."
                                                      }
                                                    }
                                                  }
                                                }
                                              ],
                                              "description": "Refer-To / target URI query parameters. Accepts either an object map or an array of {name, value} pairs."
                                            },
                                            "handoverReason": {
                                              "type": "string",
                                              "description": "Reason used by sip_bye disconnect-reconnect transfers."
                                            },
                                            "hangupReason": {
                                              "type": "string",
                                              "description": "Hangup reason, or fallback transfer reason for sip_bye."
                                            },
                                            "hangupSipHeaders": {
                                              "type": "array",
                                              "maxItems": 100,
                                              "items": {
                                                "type": "object",
                                                "required": [
                                                  "name",
                                                  "value"
                                                ],
                                                "properties": {
                                                  "name": {
                                                    "type": "string",
                                                    "description": "SIP header name."
                                                  },
                                                  "value": {
                                                    "type": "string",
                                                    "description": "SIP header value."
                                                  }
                                                }
                                              },
                                              "description": "SIP headers added to the BYE message for hangup actions."
                                            },
                                            "botOperationResult": {
                                              "type": "string",
                                              "enum": [
                                                "success",
                                                "failure"
                                              ],
                                              "description": "Bot outcome recorded for CDR / call-status consumers."
                                            },
                                            "botOperationData": {
                                              "type": "object",
                                              "additionalProperties": true,
                                              "description": "Additional structured bot outcome data."
                                            }
                                          }
                                        }
                                      }
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Legacy provider metadata. Vodafone recommends channelData instead, but TelAPI forwards metadata when present."
                  },
                  "channelData": {
                    "type": "array",
                    "description": "Outgoing channel data returned by TOBi on callbacks (Vodafone OutgoingChannelData). This is bot-to-platform context such as scenario flags or routing hints.\n\nDo not confuse this with Delphi inbound channel-data forwarding configured in TelWeb (Flow Builder → channelDataRules), which maps SIP INVITE headers into channelData on create-conversation requests sent to TOBi. Callback channelData travels in the opposite direction and is forwarded opaquely on the TelPhi Redis stream for observability.",
                    "items": {
                      "type": "object",
                      "required": [
                        "key",
                        "value"
                      ],
                      "description": "Single TOBi channel-data variable. Matches Vodafone ChannelDataEntry on bot callbacks.",
                      "properties": {
                        "key": {
                          "type": "string",
                          "description": "Variable name exposed by the bot or channel platform.",
                          "example": "scenarioTriggered"
                        },
                        "value": {
                          "description": "Variable value. May be a string, number, boolean, object, or array depending on the bot journey."
                        },
                        "sensitiveData": {
                          "type": "boolean",
                          "description": "When true, marks the value as sensitive for provider-side logging and storage policies."
                        }
                      }
                    },
                    "example": [
                      {
                        "key": "scenarioTriggered",
                        "value": "Billing information"
                      },
                      {
                        "key": "phoneCallbackRequired",
                        "value": true
                      }
                    ]
                  },
                  "nluData": {
                    "type": "object",
                    "properties": {
                      "intents": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "additionalProperties": true
                        }
                      },
                      "entities": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "additionalProperties": true
                        }
                      },
                      "sentiment": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                }
              },
              "examples": {
                "bot_message": {
                  "summary": "Bot text response",
                  "description": "Spoken or written bot content only. TelAPI extracts text from messages and forwards it to TelPhi.",
                  "value": {
                    "conversation": {
                      "identifier": {
                        "id": "tobi-conversation-123",
                        "name": "Customer conversation"
                      }
                    },
                    "messages": {
                      "timestamp": "2026-05-06T10:15:30.000Z",
                      "message": [
                        {
                          "messageId": 1,
                          "sequence": 1,
                          "content": {
                            "textMessage": {
                              "textPlain": "How can I help you today?"
                            }
                          }
                        }
                      ]
                    },
                    "channelData": [
                      {
                        "key": "scenarioTriggered",
                        "value": "General inquiry"
                      }
                    ],
                    "metadata": {
                      "locale": "de-DE"
                    }
                  }
                },
                "bot_hangup": {
                  "summary": "Bot-initiated hangup",
                  "description": "Hangup is sent as a Vodafone custom event on the final spoken message. TelAPI maps it to a runtime hangup command for TelPhi.",
                  "value": {
                    "conversation": {
                      "identifier": {
                        "id": "tobi-conversation-123",
                        "name": "Customer conversation"
                      }
                    },
                    "messages": {
                      "timestamp": "2026-05-06T10:18:45.000Z",
                      "message": [
                        {
                          "messageId": 4,
                          "sequence": 4,
                          "content": {
                            "textMessage": {
                              "textPlain": "Thank you for calling. Goodbye."
                            },
                            "event": {
                              "custom": {
                                "name": "hangup",
                                "value": {
                                  "hangupReason": "conversation_complete",
                                  "botOperationResult": "success"
                                }
                              }
                            }
                          }
                        }
                      ]
                    }
                  }
                },
                "bot_transfer": {
                  "summary": "Bot-initiated transfer",
                  "description": "Transfer destination and SIP options are carried in the same custom event model. TelAPI preserves spoken text and control-event order for TelPhi.",
                  "value": {
                    "conversation": {
                      "identifier": {
                        "id": "tobi-conversation-123",
                        "name": "Customer conversation"
                      }
                    },
                    "messages": {
                      "timestamp": "2026-05-06T10:17:10.000Z",
                      "message": [
                        {
                          "messageId": 3,
                          "sequence": 3,
                          "content": {
                            "textMessage": {
                              "textPlain": "I will connect you to a specialist now."
                            },
                            "event": {
                              "custom": {
                                "name": "transfer",
                                "value": {
                                  "transferTarget": "tel:+491701234567",
                                  "transferMethod": "sip_refer",
                                  "transferSipHeaders": [
                                    {
                                      "name": "X-Queue",
                                      "value": "care"
                                    }
                                  ],
                                  "transferNotifications": true,
                                  "transferNotificationsHangupMS": 3000
                                }
                              }
                            }
                          }
                        }
                      ]
                    }
                  }
                },
                "bot_config": {
                  "summary": "Bot-updated DTMF and speech config",
                  "description": "TOBi can update DTMF handling or bounded STT/TTS timeout/retry settings during an active call with a config custom event. Send separate config events when updating both DTMF and speech settings. Speech provider selection is limited to the Delphi-configured fallback chain.",
                  "value": {
                    "conversation": {
                      "identifier": {
                        "id": "tobi-conversation-123",
                        "name": "Customer conversation"
                      }
                    },
                    "messages": {
                      "timestamp": "2026-05-06T10:19:05.000Z",
                      "message": [
                        {
                          "messageId": 5,
                          "sequence": 5,
                          "content": {
                            "event": {
                              "custom": {
                                "name": "config",
                                "value": {
                                  "sessionParams": {
                                    "sendDTMF": true,
                                    "bargeInOnDTMF": true,
                                    "bargeIn": true,
                                    "bargeInMinWordCount": 1,
                                    "dtmfCollect": true,
                                    "dtmfDigits": "0123456789#*",
                                    "dtmfCollectInterDigitTimeoutMS": 3000,
                                    "dtmfCollectMaxDigits": 15,
                                    "dtmfCollectSubmitDigit": "#"
                                  }
                                }
                              }
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "description": "TOBi callback payload sent by Vodafone (SendMessageResponse). TelAPI requires conversation.identifier.id so the callback can be correlated to the TOBi channel."
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "token",
            "required": false,
            "description": "Optional legacy auth token echo from Vodafone callback URLs. Prefer the token header."
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "channelId",
            "required": true,
            "description": "The channel ID this callback belongs to (TOBi conversation ID)"
          },
          {
            "schema": {
              "type": "string"
            },
            "example": "<token>",
            "in": "header",
            "name": "token",
            "required": false,
            "description": "Per-conversation TOBi callback token. Vodafone submits this as the literal token header value."
          }
        ],
        "responses": {
          "200": {
            "description": "Acknowledgement that TelAPI accepted the callback and published it to the TelPhi consumer stream.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Acknowledgement that TelAPI accepted the callback and published it to the TelPhi consumer stream.",
                  "required": [
                    "received",
                    "streamId"
                  ],
                  "properties": {
                    "received": {
                      "type": "boolean",
                      "enum": [
                        true
                      ],
                      "description": "Always true when the callback payload is accepted."
                    },
                    "streamId": {
                      "type": "string",
                      "description": "Redis stream entry id for the forwarded TOBi payload. The same acknowledgement shape is returned for message, hangup, and transfer callbacks.",
                      "example": "1778022191343-0"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid TOBi payload or missing conversation identifier",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Invalid TOBi payload or missing conversation identifier",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "INVALID_PAYLOAD",
                    "message": "Invalid TOBi payload or missing conversation identifier"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired, or invalid TOBi callback token",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Missing, expired, or invalid TOBi callback token",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "examples": {
                  "example1": {
                    "value": {
                      "error": {
                        "code": "TOBI_CALLBACK_TOKEN_MISSING",
                        "message": "Missing TOBi callback token. Provide the token header with the callback token value."
                      }
                    }
                  },
                  "example2": {
                    "value": {
                      "error": {
                        "code": "TOBI_CALLBACK_TOKEN_NOT_REGISTERED",
                        "message": "No active TOBi callback security token found for this webhook channel. The conversation may not be initialized or the session token may have expired."
                      }
                    }
                  },
                  "example3": {
                    "value": {
                      "error": {
                        "code": "TOBI_CALLBACK_TOKEN_INVALID",
                        "message": "Invalid TOBi callback token. The provided token header value does not match the active session token."
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Unexpected server error",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "INTERNAL_ERROR",
                    "message": "Unexpected server error"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sessions/token": {
      "post": {
        "summary": "Request a session token",
        "tags": [
          "Sessions"
        ],
        "description": "Issues a runtime session id and a short-lived WebSocket token for the requested SDK mode.\n\nAfter this call, connect the browser SDK to:\n  /ws/session?sessionId={sessionId}&token={wsToken}\n\nWebSocket wire format:\n- Messages use the ChannelMessage envelope (see components.schemas.ChannelMessage).\n- The server sends an initial status message with state=connected.\n\nWebSocket close codes:\n- 4001 — missing sessionId or token\n- 4003 — invalid token or expired session\n- 4000 — superseded by a newer connection\n- 4500 — internal server error\n\nAuthentication: requires a team API key with CREATE_CALL_TOKEN scope.\n\nvoice_conversation responses also include WebRTC gateway metadata (telproDomain, webrtcGatewayUrl).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "endpointId",
                  "mode"
                ],
                "properties": {
                  "endpointId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Endpoint to open a session against. It must belong to the API key team.",
                    "example": "0f6c3d4e-8b8f-4c59-9d0e-7e5e3b1f7f0f"
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "text",
                      "audio_playback",
                      "voice_conversation",
                      "browser_actions",
                      "listen"
                    ],
                    "description": "Session mode to provision: text chat, browser TTS playback, WebRTC voice, browser action dispatch, or interpretation listening."
                  }
                }
              },
              "examples": {
                "request_text_capability": {
                  "summary": "Request a text session",
                  "value": {
                    "endpointId": "0f6c3d4e-8b8f-4c59-9d0e-7e5e3b1f7f0f",
                    "mode": "text"
                  }
                },
                "request_audio_playback_capability": {
                  "summary": "Request an audio playback session",
                  "value": {
                    "endpointId": "0f6c3d4e-8b8f-4c59-9d0e-7e5e3b1f7f0f",
                    "mode": "audio_playback"
                  }
                },
                "request_voice_capability": {
                  "summary": "Request a voice conversation session",
                  "value": {
                    "endpointId": "0f6c3d4e-8b8f-4c59-9d0e-7e5e3b1f7f0f",
                    "mode": "voice_conversation"
                  }
                },
                "request_browser_actions_capability": {
                  "summary": "Request a browser actions session",
                  "value": {
                    "endpointId": "0f6c3d4e-8b8f-4c59-9d0e-7e5e3b1f7f0f",
                    "mode": "browser_actions"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "sessionId",
                    "wsToken",
                    "wsTokenExpiresIn",
                    "expiresIn"
                  ],
                  "properties": {
                    "sessionId": {
                      "type": "string",
                      "description": "The runtime session id (also the X-Call-ID for voice)"
                    },
                    "wsToken": {
                      "type": "string",
                      "description": "WebSocket token for /ws/session authentication"
                    },
                    "wsTokenExpiresIn": {
                      "type": "number",
                      "description": "WS token validity in seconds (5 min)"
                    },
                    "expiresIn": {
                      "type": "number",
                      "description": "Session token validity in seconds"
                    },
                    "telproDomain": {
                      "type": "string",
                      "description": "WebRTC gateway / TURN / STUN host (voice_conversation only)"
                    },
                    "webrtcGatewayUrl": {
                      "type": "string",
                      "description": "WebSocket URL of the WebRTC gateway (voice_conversation only)"
                    },
                    "traceId": {
                      "type": "string",
                      "description": "OpenTelemetry trace id returned for voice_conversation sessions."
                    },
                    "spanId": {
                      "type": "string",
                      "description": "OpenTelemetry root span id returned for voice_conversation sessions."
                    }
                  }
                },
                "examples": {
                  "example_text_capability": {
                    "summary": "Text session token",
                    "value": {
                      "sessionId": "call-loc5q7g0-k7p3f9x2",
                      "wsToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
                      "wsTokenExpiresIn": 300,
                      "expiresIn": 3600
                    }
                  },
                  "example_voice_capability": {
                    "summary": "Voice conversation session token",
                    "value": {
                      "sessionId": "call-loc5q7g0-k7p3f9x2",
                      "wsToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
                      "wsTokenExpiresIn": 300,
                      "expiresIn": 3600,
                      "telproDomain": "telpro.example.com",
                      "webrtcGatewayUrl": "wss://telpro.example.com",
                      "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
                      "spanId": "00f067aa0ba902b7"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Request validation failed",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "VALIDATION_ERROR",
                    "message": "Request validation failed"
                  }
                }
              }
            }
          },
          "401": {
            "description": "API key is missing or invalid",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "API key is missing or invalid",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "AUTHENTICATION_ERROR",
                    "message": "API key is missing or invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: wrong team, origin/IP rejected, missing scope, or subscription limit reached",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Forbidden: wrong team, origin/IP rejected, missing scope, or subscription limit reached",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "FORBIDDEN",
                    "message": "Forbidden: wrong team, origin/IP rejected, missing scope, or subscription limit reached"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Endpoint not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Endpoint not found",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "NOT_FOUND",
                    "message": "Endpoint not found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Endpoint flow is not configured or not synced for the requested mode",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Endpoint flow is not configured or not synced for the requested mode",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "FLOW_NOT_CONFIGURED",
                    "message": "Endpoint flow is not configured or not synced for the requested mode"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Too many requests",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "RATE_LIMIT_EXCEEDED",
                    "message": "Too many requests"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Unexpected server error",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "INTERNAL_ERROR",
                    "message": "Unexpected server error"
                  }
                }
              }
            }
          },
          "503": {
            "description": "voice_conversation requested but TelPro is not configured",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "voice_conversation requested but TelPro is not configured",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "TELPRO_UNCONFIGURED",
                    "message": "voice_conversation requested but TelPro is not configured"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sessions/upgrade": {
      "post": {
        "summary": "Upgrade a text session to voice",
        "tags": [
          "Sessions"
        ],
        "description": "Serialises the active TelAPI text FlowEngine session to Redis, then re-issues a voice_conversation token for the same sessionId so TelPhi can resume conversation context when the WebRTC leg connects.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "sessionId",
                  "endpointId"
                ],
                "properties": {
                  "sessionId": {
                    "type": "string"
                  },
                  "endpointId": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "sessionId",
                    "wsToken",
                    "wsTokenExpiresIn",
                    "expiresIn"
                  ],
                  "properties": {
                    "sessionId": {
                      "type": "string"
                    },
                    "wsToken": {
                      "type": "string"
                    },
                    "wsTokenExpiresIn": {
                      "type": "number"
                    },
                    "expiresIn": {
                      "type": "number"
                    },
                    "telproDomain": {
                      "type": "string"
                    },
                    "webrtcGatewayUrl": {
                      "type": "string"
                    },
                    "traceId": {
                      "type": "string"
                    },
                    "spanId": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Request validation failed",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "VALIDATION_ERROR",
                    "message": "Request validation failed"
                  }
                }
              }
            }
          },
          "401": {
            "description": "API key is missing or invalid",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "API key is missing or invalid",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "AUTHENTICATION_ERROR",
                    "message": "API key is missing or invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: wrong team or subscription limit reached",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Forbidden: wrong team or subscription limit reached",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "FORBIDDEN",
                    "message": "Forbidden: wrong team or subscription limit reached"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Session not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Session not found",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "NOT_FOUND",
                    "message": "Session not found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Session is not a text session or flow is not ready",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Session is not a text session or flow is not ready",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "INVALID_STATE",
                    "message": "Session is not a text session or flow is not ready"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Too many requests",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "RATE_LIMIT_EXCEEDED",
                    "message": "Too many requests"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Unexpected server error",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "INTERNAL_ERROR",
                    "message": "Unexpected server error"
                  }
                }
              }
            }
          },
          "503": {
            "description": "TelPro is not configured",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "TelPro is not configured",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "TELPRO_UNCONFIGURED",
                    "message": "TelPro is not configured"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sessions/downgrade": {
      "post": {
        "summary": "Downgrade a voice session back to text",
        "tags": [
          "Sessions"
        ],
        "description": "After the WebRTC leg ends, re-issues a text session token for the same sessionId so TelAPI can resume chat with persisted conversation history.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "sessionId",
                  "endpointId"
                ],
                "properties": {
                  "sessionId": {
                    "type": "string"
                  },
                  "endpointId": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "sessionId",
                    "wsToken",
                    "wsTokenExpiresIn",
                    "expiresIn"
                  ],
                  "properties": {
                    "sessionId": {
                      "type": "string"
                    },
                    "wsToken": {
                      "type": "string"
                    },
                    "wsTokenExpiresIn": {
                      "type": "number"
                    },
                    "expiresIn": {
                      "type": "number"
                    },
                    "traceId": {
                      "type": "string"
                    },
                    "spanId": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Request validation failed",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "VALIDATION_ERROR",
                    "message": "Request validation failed"
                  }
                }
              }
            }
          },
          "401": {
            "description": "API key is missing or invalid",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "API key is missing or invalid",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "AUTHENTICATION_ERROR",
                    "message": "API key is missing or invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: wrong team or subscription limit reached",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Forbidden: wrong team or subscription limit reached",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "FORBIDDEN",
                    "message": "Forbidden: wrong team or subscription limit reached"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Session not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Session not found",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "NOT_FOUND",
                    "message": "Session not found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Session is not a voice session for this endpoint",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Session is not a voice session for this endpoint",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "INVALID_STATE",
                    "message": "Session is not a voice session for this endpoint"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Too many requests",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "RATE_LIMIT_EXCEEDED",
                    "message": "Too many requests"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Unexpected server error",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "INTERNAL_ERROR",
                    "message": "Unexpected server error"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/runtime/capabilities": {
      "get": {
        "summary": "Discover endpoint runtime capabilities",
        "tags": [
          "Runtime"
        ],
        "description": "Returns the interaction modes, transport preferences, flow entry points, and browser actions the SDK can use for an endpoint. Requires a team API key with CREATE_CALL_TOKEN scope.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "0f6c3d4e-8b8f-4c59-9d0e-7e5e3b1f7f0f",
            "in": "query",
            "name": "endpointId",
            "required": true,
            "description": "Endpoint to inspect. Must belong to the API key team."
          }
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "endpointId",
                    "flowDefinitionId",
                    "runtime",
                    "interactionModes",
                    "transports",
                    "flows"
                  ],
                  "properties": {
                    "endpointId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "flowDefinitionId": {
                      "type": [
                        "null",
                        "string"
                      ],
                      "format": "uuid",
                      "description": "Active flow definition id, or null when the endpoint has no linked flow."
                    },
                    "runtime": {
                      "type": "object",
                      "required": [
                        "sessionContinuity",
                        "migration"
                      ],
                      "properties": {
                        "sessionContinuity": {
                          "type": "string",
                          "enum": [
                            "supported"
                          ]
                        },
                        "migration": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "enum": [
                              "text_to_voice",
                              "api_to_media"
                            ]
                          }
                        }
                      }
                    },
                    "interactionModes": {
                      "type": "object",
                      "required": [
                        "text",
                        "audio_playback",
                        "voice_conversation",
                        "browser_actions",
                        "listen"
                      ],
                      "properties": {
                        "text": {
                          "type": "boolean"
                        },
                        "audio_playback": {
                          "type": "boolean"
                        },
                        "voice_conversation": {
                          "type": "boolean"
                        },
                        "browser_actions": {
                          "type": "boolean"
                        },
                        "listen": {
                          "type": "boolean"
                        }
                      }
                    },
                    "transports": {
                      "type": "object",
                      "required": [
                        "preferred",
                        "available"
                      ],
                      "properties": {
                        "preferred": {
                          "type": "array",
                          "description": "Transports ordered by SDK preference.",
                          "items": {
                            "type": "string",
                            "enum": [
                              "rest",
                              "websocket",
                              "existing_media",
                              "webrtc"
                            ]
                          }
                        },
                        "available": {
                          "type": "array",
                          "description": "Every transport this endpoint can support.",
                          "items": {
                            "type": "string",
                            "enum": [
                              "rest",
                              "websocket",
                              "existing_media",
                              "webrtc"
                            ]
                          }
                        }
                      }
                    },
                    "flows": {
                      "type": "object",
                      "required": [
                        "entryPoints",
                        "responseModes",
                        "sideFlows",
                        "browserActions"
                      ],
                      "properties": {
                        "entryPoints": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "enum": [
                              "phone",
                              "web_voice",
                              "web_chat"
                            ]
                          }
                        },
                        "responseModes": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "enum": [
                              "audio_stream",
                              "text_stream",
                              "json",
                              "none"
                            ]
                          }
                        },
                        "sideFlows": {
                          "type": "array",
                          "description": "Advanced side flows that are not generated Browser-Originated Action capabilities.",
                          "items": {
                            "type": "object",
                            "required": [
                              "id",
                              "triggerType"
                            ],
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "name": {
                                "type": "string"
                              },
                              "triggerType": {
                                "type": "string"
                              },
                              "messageType": {
                                "type": "string",
                                "description": "WebSocket message type when triggerType is ws_message."
                              }
                            }
                          }
                        },
                        "browserActions": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "required": [
                              "id",
                              "slug",
                              "label",
                              "type",
                              "messageType",
                              "voiceInvocable"
                            ],
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "slug": {
                                "type": "string"
                              },
                              "label": {
                                "type": "string"
                              },
                              "type": {
                                "type": "string"
                              },
                              "messageType": {
                                "type": "string"
                              },
                              "voiceInvocable": {
                                "type": "boolean"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "endpointId": "0f6c3d4e-8b8f-4c59-9d0e-7e5e3b1f7f0f",
                  "flowDefinitionId": "7e0dca65-b70b-461e-9d12-c18e0ab6e626",
                  "runtime": {
                    "sessionContinuity": "supported",
                    "migration": [
                      "text_to_voice",
                      "api_to_media"
                    ]
                  },
                  "interactionModes": {
                    "text": true,
                    "audio_playback": true,
                    "voice_conversation": true,
                    "browser_actions": true,
                    "listen": true
                  },
                  "transports": {
                    "preferred": [
                      "rest",
                      "websocket",
                      "existing_media",
                      "webrtc"
                    ],
                    "available": [
                      "rest",
                      "websocket",
                      "existing_media",
                      "webrtc"
                    ]
                  },
                  "flows": {
                    "entryPoints": [
                      "web_chat",
                      "web_voice"
                    ],
                    "responseModes": [
                      "json",
                      "audio_stream"
                    ],
                    "sideFlows": [],
                    "browserActions": [
                      {
                        "id": "read-selection",
                        "slug": "read-selection",
                        "label": "Read aloud",
                        "type": "readAloud",
                        "messageType": "browser.action.readAloud",
                        "voiceInvocable": true
                      }
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Request validation failed",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "VALIDATION_ERROR",
                    "message": "Request validation failed"
                  }
                }
              }
            }
          },
          "401": {
            "description": "API key is missing or invalid",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "API key is missing or invalid",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "AUTHENTICATION_ERROR",
                    "message": "API key is missing or invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "API key lacks the required scope or resource access",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "API key lacks the required scope or resource access",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "AUTHORIZATION_ERROR",
                    "message": "API key lacks the required scope or resource access"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Endpoint not found for this team",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Endpoint not found for this team",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "ENDPOINT_NOT_FOUND",
                    "message": "Endpoint not found for this team"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Too many requests",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "RATE_LIMIT_EXCEEDED",
                    "message": "Too many requests"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Unexpected server error",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "INTERNAL_ERROR",
                    "message": "Unexpected server error"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/endpoints": {
      "get": {
        "summary": "List team endpoints",
        "tags": [
          "Endpoints"
        ],
        "description": "Returns all endpoints belonging to the team associated with the API key. Requires a team API key with READ_TEAM_APPS scope.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^[1-9][0-9]*$",
              "default": "1"
            },
            "example": "1",
            "in": "query",
            "name": "page",
            "required": false,
            "description": "Page number (1-based)"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^[1-9][0-9]*$",
              "default": "20"
            },
            "example": "20",
            "in": "query",
            "name": "limit",
            "required": false,
            "description": "Items per page (max 100)"
          }
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "endpoints",
                    "pagination"
                  ],
                  "properties": {
                    "endpoints": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "name"
                        ],
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid",
                            "description": "Endpoint id to use with runtime/session APIs."
                          },
                          "name": {
                            "type": "string",
                            "description": "Endpoint display name."
                          }
                        }
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "required": [
                        "total",
                        "page",
                        "limit",
                        "totalPages"
                      ],
                      "properties": {
                        "total": {
                          "type": "number",
                          "description": "Total matching items."
                        },
                        "page": {
                          "type": "number",
                          "description": "Current 1-based page number."
                        },
                        "limit": {
                          "type": "number",
                          "description": "Items requested per page."
                        },
                        "totalPages": {
                          "type": "number",
                          "description": "Total pages available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "endpoints": [
                    {
                      "id": "0f6c3d4e-8b8f-4c59-9d0e-7e5e3b1f7f0f",
                      "name": "Website assistant"
                    }
                  ],
                  "pagination": {
                    "total": 1,
                    "page": 1,
                    "limit": 20,
                    "totalPages": 1
                  }
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Request validation failed",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "VALIDATION_ERROR",
                    "message": "Request validation failed"
                  }
                }
              }
            }
          },
          "401": {
            "description": "API key is missing or invalid",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "API key is missing or invalid",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "AUTHENTICATION_ERROR",
                    "message": "API key is missing or invalid"
                  }
                }
              }
            }
          },
          "403": {
            "description": "API key lacks the required scope or resource access",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "API key lacks the required scope or resource access",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "AUTHORIZATION_ERROR",
                    "message": "API key lacks the required scope or resource access"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Too many requests",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "RATE_LIMIT_EXCEEDED",
                    "message": "Too many requests"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Unexpected server error",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable error code.",
                          "example": "VALIDATION_ERROR"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error summary.",
                          "example": "Request validation failed"
                        },
                        "details": {
                          "description": "Optional structured details, usually validation issues."
                        },
                        "requestId": {
                          "type": "string",
                          "description": "Fastify request id for log correlation when available."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "INTERNAL_ERROR",
                    "message": "Unexpected server error"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "servers": [
    {
      "url": "http://localhost:3001",
      "description": "Development server"
    }
  ],
  "tags": [
    {
      "name": "Endpoints",
      "description": "Team endpoint discovery using team API keys"
    },
    {
      "name": "Health",
      "description": "GET /health — minimal probe (Redis); detail via OpenTelemetry"
    },
    {
      "name": "Runtime",
      "description": "Runtime capability discovery for SDK clients"
    },
    {
      "name": "Sessions",
      "description": "Session-token issuance for text, audio playback, voice, and browser-action SDK modes"
    },
    {
      "name": "Webhooks",
      "description": "Webhook receivers"
    }
  ]
}
