From 96387b430db4ece009db96deb1a1e60246e109c1 Mon Sep 17 00:00:00 2001 From: Devon-White Date: Wed, 2 Sep 2026 13:02:39 -0400 Subject: [PATCH 01/13] docs: enhance llms.txt and welcome.mdx with clearer guidance on building applications --- fern/products/home/pages/welcome.mdx | 126 ++++++++++++++++++++++----- scripts/llm-export-cases.json | 36 +++++++- 2 files changed, 134 insertions(+), 28 deletions(-) diff --git a/fern/products/home/pages/welcome.mdx b/fern/products/home/pages/welcome.mdx index 49c0930867..41f25c9a9a 100644 --- a/fern/products/home/pages/welcome.mdx +++ b/fern/products/home/pages/welcome.mdx @@ -122,30 +122,108 @@ layout: custom SignalWire is a programmable communications platform for building voice, video, messaging, and AI applications. -Use the build type or interface below to choose the shortest path into the documentation. - -## Choose what to build - -| Goal | Start here | Use it for | -|---|---|---| -| AI voice agent | [AI application path](/docs/platform/getting-started#ai-application) | LLM-powered phone agents and virtual assistants | -| Browser or mobile experience | [Browser or mobile path](/docs/platform/getting-started#browser-or-mobile-app) | WebRTC voice, video, and chat in an application | -| Server application | [Server application path](/docs/platform/getting-started#server-application) | Backend call routing, messaging, interactive voice response, and realtime control | -| No-code application | [No-code and low-code path](/docs/platform/getting-started#no-code--low-code) | Dashboard AI agents and drag-and-drop call flows | -| Twilio migration | [Compatibility API](/docs/compatibility-api) | Existing applications that use Twilio-compatible REST APIs or TwiML-compatible markup | - -## Choose an interface - -| Interface | Start here | Best fit | -|---|---|---| -| REST APIs | [REST API documentation](/docs/apis) | Programmatic management of calls, messages, phone numbers, video rooms, and resources | -| SWML | [SignalWire Markup Language](/docs/swml) | Declarative call and messaging applications returned from webhooks or hosted by SignalWire | -| Server SDKs | [Server SDK documentation](/docs/server-sdks) | AI agents and realtime call control in a supported programming language | -| Browser SDK | [Browser SDK documentation](/docs/browser-sdk) | Voice, video, and chat inside browser applications | -| Call Flow Builder | [Call Flow Builder documentation](/docs/call-flow-builder) | Visual, no-code voice applications | -| Platform and Dashboard | [Platform documentation](/docs/platform) | Spaces, credentials, phone numbers, resources, and operational configuration | -| Wirestarter | [Wirestarter documentation](/docs/platform/wirestarter) | Scaffolding a new SignalWire project | -| SWSH | [SWSH documentation](/docs/platform/swsh) | Interactively testing SWML scripts and API calls | +The SignalWire SDKs are the recommended way to build. Learn the shared platform concepts first, then pick a goal below for the shortest path into the documentation. + +## Start here + +Start with a SignalWire Space and the platform's shared routing model. Add credentials, phone numbers, or webhooks only when your chosen interface and transport require them. + +- [Create a SignalWire Space](/docs/platform/signing-up-for-a-space) +- [Understand Resources](/docs/platform/resources): communication entities such as AI agents, SWML scripts, Subscribers, and Video Rooms. +- [Understand Addresses](/docs/platform/addresses): the aliases, phone numbers, and SIP URIs that make Resources reachable. +- [Find your Project ID and API token](/docs/platform/your-signalwire-api-space) when an SDK or API needs to authenticate. +- [Buy or port a phone number](/docs/platform/phone-numbers) when your application uses a phone number for PSTN calling, SMS, or MMS. +- [Configure webhooks](/docs/platform/webhooks) when your application receives HTTP events or returns call and messaging instructions over HTTP. +- [Full platform walkthrough](/docs/platform/getting-started) + +## Choose by goal + +### Build an AI voice agent + +- Recommended: [Server SDKs](/docs/server-sdks) +- Use when: you want an LLM-powered phone agent with prompts, tools, state, or several agents working together. +- Start: [Server SDK quick start](/docs/server-sdks/guides/quickstart), then [Server SDK architecture](/docs/server-sdks/guides/architecture) +- Also possible: the [SWML `ai` method](/docs/swml/reference/calling/ai) when AI is one step inside a declarative call flow, or [SignalWire AI](/docs/platform/ai) to configure an agent in the Dashboard without writing code. +- Do not start with: the Compatibility API, which does not support SignalWire AI. + +### Add voice, video, or chat to a browser or mobile app + +- Recommended: [Browser SDK](/docs/browser-sdk/v4/guides/overview) +- Use when: the media endpoint is a browser or a mobile client over WebRTC. +- Start: [Browser SDK overview](/docs/browser-sdk/v4/guides/overview), then [Browser SDK authentication](/docs/browser-sdk/v4/guides/authentication) +- Authentication: authenticated-user applications issue Subscriber Access Tokens from a trusted backend. Public widgets can instead use an embed token without running a backend. The media itself flows over WebRTC between the client and SignalWire. + +### Build a server-side call or messaging application + +- Recommended: [Server SDKs](/docs/server-sdks) +- Use when: your backend routes calls, sends messages, or runs interactive voice response. +- Start: the [Server SDK landing page](/docs/server-sdks), then choose the guide or reference for your language and control model. +- Choose [Relay](/docs/server-sdks/guides/relay-client) for persistent, event-driven call or messaging control; choose [SWML](/docs/swml) when your application only needs to return declarative instructions over HTTP or you want SignalWire to host the script. + +### Control a live call in real time + +- Recommended: [Relay, in the Server SDKs](/docs/server-sdks/guides/relay-client) +- Use when: your application drives the call asynchronously, reacting to events as they arrive over an open connection — IVR menus, call routing, recording pipelines, custom media flows. +- Start: [Relay client guide](/docs/server-sdks/guides/relay-client) +- How it works: an asynchronous, event-driven WebSocket connection using JSON-RPC 2.0. Your process subscribes to contexts and receives call events directly, so it needs no webhook endpoint. +- Also possible: [call commands over HTTP](/docs/apis/rest/calls/call-commands) act on an active call — update the dialplan, play, record, collect, transfer, transcribe, tap. Each is a synchronous request that returns immediately; a command that keeps running afterward reports its completion to your `status_url` webhooks. Choose Relay when your application reacts to events as the call unfolds, and call commands when a synchronous HTTP request fits your stack. + +### Write call flows without an SDK dependency, or let SignalWire host them + +- Recommended: [SWML](/docs/swml) +- Use when: you want a language-agnostic document that fits into an existing stack with nothing to install, or you want SignalWire to host the script so you run no server of your own. +- Start: [Introduction to SWML](/docs/swml), then the [SWML quickstart](/docs/swml/guides) +- Deployment: use the [SWML quickstart](/docs/swml/guides) to host a script in the Dashboard, or follow the [deployment guide](/docs/swml/guides/deployment) to serve SWML from a web server or invoke it from a Relay application. For AI agents, a [Server SDK generates the SWML](/docs/server-sdks/guides/swml) from your agent configuration. +- Document types: [Calling SWML](/docs/swml/reference/calling) for voice calls and [Messaging SWML](/docs/swml/reference/messaging) for inbound SMS and MMS. Each has its own method set, so a Calling method is not valid in a Messaging document. +- Errors: [SWML error reference](/docs/swml/reference/errors) + +### Build without writing code + +- Recommended: [Call Flow Builder](/docs/call-flow-builder) +- Use when: the person building the flow is not a developer and wants a drag-and-drop editor in the Dashboard. +- Start: [Call Flow Builder overview](/docs/call-flow-builder), then the [node reference](/docs/call-flow-builder/reference/nodes) +- Also possible: [SignalWire AI](/docs/platform/ai) for an AI agent configured in the Dashboard. +- Beyond the built-in nodes: the [Request node](/docs/call-flow-builder/reference/request) calls an HTTP endpoint and the [Execute SWML node](/docs/call-flow-builder/reference/execute-swml) runs a SWML document returned by a remote URL, so a flow is not limited to the node set. + +### Provision and manage account resources + +- Recommended: [REST APIs](/docs/apis) +- Use when: your code creates, lists, or updates phone numbers, resources, subscribers, video rooms, call logs, or messages. +- Start: [REST API overview](/docs/apis), then [REST API authorization](/docs/apis/authorization) and the [REST API reference](/docs/apis/rest) +- Errors: [REST API error codes](/docs/apis/error-codes) +- Also covers: [call commands](/docs/apis/rest/calls/call-commands) that act on an active call over synchronous HTTP requests. To react to call events asynchronously instead, use Relay above. + +### Migrate an existing Twilio application + +- Recommended: [Compatibility API](/docs/compatibility-api) +- Use when: you have a working Twilio-compatible application and want it running on SignalWire with minimal code change. +- Start: [Compatibility API overview](/docs/compatibility-api), then the [cXML specification](/docs/compatibility-api/cxml) and the [Compatibility REST API](/docs/compatibility-api/rest) +- Do not use: for SignalWire AI, which the Compatibility API does not support. Use SignalWire's native interfaces for AI features. + +## Interfaces at a glance + +| Interface | What it is | Protocol | Pattern | Best fit | +|---|---|---|---|---| +| [Server SDKs — Agents](/docs/server-sdks) | Native libraries that build AI agents; they generate SWML for you | HTTPS | Declarative — return a SWML document describing the call flow | Voice bots, AI assistants, multi-step workflows | +| [Server SDKs — Relay](/docs/server-sdks/guides/relay-client) | Native libraries that control calls and messages over a persistent connection | WebSocket, JSON-RPC 2.0 (asynchronous) | Imperative — issue async commands and react to events | IVR systems, call routing, recording pipelines, custom media flows | +| [Browser SDK](/docs/browser-sdk/v4/guides/overview) | Client library for browser and mobile endpoints | WebRTC | Client-side session using a Subscriber Access Token or embed token | Voice, video, and chat inside an application | +| [SWML](/docs/swml) | Declarative call and messaging documents written in JSON or YAML | HTTP | Request/response — SignalWire requests a document, your side returns one | Language-agnostic flows with no SDK dependency, or scripts hosted by SignalWire | +| [REST APIs](/docs/apis) | Resource management, plus command-based control of active calls | HTTPS (synchronous) | Request/response — commands return immediately; long-running ones report completion to webhooks | Provisioning and querying resources, and HTTP-driven call commands | +| [Call Flow Builder](/docs/call-flow-builder) | Drag-and-drop flow editor in the Dashboard | None | Visual graph of nodes, with escape hatches to HTTP and SWML | No-code voice applications | +| [Compatibility API](/docs/compatibility-api) | Twilio-compatible REST API and cXML markup | HTTPS | Request/response — form-encoded requests and Call SIDs | Migrating an existing Twilio application | + +Development tools: [Wirestarter](/docs/platform/wirestarter) scaffolds a new project, and [SWSH](/docs/platform/swsh) tests SWML scripts and API calls interactively. + +## Use this, not that + +- Use the Server SDKs, not hand-written SWML, when the SDK supports your language and you want the document generated for you. +- Use SWML, not a Server SDK, when you want no SDK dependency in your stack or you want SignalWire to host the script. +- Use SWML, not Relay, when your side only returns a declarative sequence of call instructions and needs no open session. +- Use Relay, not HTTP call commands, when your application reacts to call events asynchronously as they arrive. Both can act on a live call; the difference is that Relay is event-driven over an open connection, while call commands are synchronous HTTP requests. +- Use HTTP call commands, not Relay, when a synchronous request fits your stack and you would rather take any follow-up by webhook than hold a connection open. +- Use the Browser SDK for media in a browser or mobile client. Issue Subscriber Access Tokens from a trusted backend for authenticated users; public widgets can use embed tokens without a backend. +- Use SignalWire's native interfaces for AI. The Compatibility API does not support it. +- Use Call Flow Builder when the person building the flow is not a developer, and an SDK when the flow belongs in version-controlled application code. [Create a SignalWire account](https://signalwire.com/signup), or follow the [Platform getting-started guide](/docs/platform/getting-started) to configure an existing Space. diff --git a/scripts/llm-export-cases.json b/scripts/llm-export-cases.json index c84720d5ad..05b3757e9c 100644 --- a/scripts/llm-export-cases.json +++ b/scripts/llm-export-cases.json @@ -4,14 +4,28 @@ "path": ".md", "contains": [ "SignalWire is a programmable communications platform", - "## Choose what to build", + "The SignalWire SDKs are the recommended way to build", + "## Start here", + "## Choose by goal", + "### Build an AI voice agent", + "[Understand Resources](/docs/platform/resources)", + "[Understand Addresses](/docs/platform/addresses)", + "## Interfaces at a glance", + "## Use this, not that", + "Do not start with: the Compatibility API, which does not support SignalWire AI.", "[Platform getting-started guide](/docs/platform/getting-started)", "Twilio and TwiML are trademarks" ], "notContains": [ "class=\"fern-page-heading", " Date: Fri, 4 Sep 2026 12:53:36 -0400 Subject: [PATCH 02/13] docs: refine llms routing index --- fern/products/home/pages/welcome.mdx | 199 ++++++++++++++++----------- scripts/llm-export-cases.json | 76 ++++++---- 2 files changed, 172 insertions(+), 103 deletions(-) diff --git a/fern/products/home/pages/welcome.mdx b/fern/products/home/pages/welcome.mdx index 41f25c9a9a..46cdc5a780 100644 --- a/fern/products/home/pages/welcome.mdx +++ b/fern/products/home/pages/welcome.mdx @@ -2,7 +2,7 @@ id: 0f7a51b9-708a-48fa-bccf-d9a449c4489c title: SignalWire Documentation slug: / -description: Build powerful communication applications with SignalWire. Explore our REST APIs, Realtime SDKs, and comprehensive guides. +description: Build voice, video, messaging, and artificial intelligence applications with SignalWire developer tools and implementation guides. hide-toc: true layout: custom --- @@ -121,109 +121,150 @@ layout: custom -SignalWire is a programmable communications platform for building voice, video, messaging, and AI applications. -The SignalWire SDKs are the recommended way to build. Learn the shared platform concepts first, then pick a goal below for the shortest path into the documentation. +SignalWire is a programmable communications platform for building voice, video, messaging, and artificial intelligence (AI) applications. +The SignalWire software development kits (SDKs) are the recommended way to build. Start with the first section to learn the platform, then pick the section that matches how your application takes part. -## Start here +## Choose a path -Start with a SignalWire Space and the platform's shared routing model. Add credentials, phone numbers, or webhooks only when your chosen interface and transport require them. +Each heading below describes a setup task or a way an application can handle communication. Links go to a landing or getting-started page for each option, listed in order of preference. -- [Create a SignalWire Space](/docs/platform/signing-up-for-a-space) -- [Understand Resources](/docs/platform/resources): communication entities such as AI agents, SWML scripts, Subscribers, and Video Rooms. -- [Understand Addresses](/docs/platform/addresses): the aliases, phone numbers, and SIP URIs that make Resources reachable. -- [Find your Project ID and API token](/docs/platform/your-signalwire-api-space) when an SDK or API needs to authenticate. -- [Buy or port a phone number](/docs/platform/phone-numbers) when your application uses a phone number for PSTN calling, SMS, or MMS. -- [Configure webhooks](/docs/platform/webhooks) when your application receives HTTP events or returns call and messaging instructions over HTTP. -- [Full platform walkthrough](/docs/platform/getting-started) +### Learn the platform and get started -## Choose by goal +- [Get started](/docs/platform/getting-started): Walkthrough of your Space, the build paths, and the core concepts. +- [Create an account](/docs/platform/signing-up-for-a-space): Open a new Space on the SignalWire platform. +- [Application programming interface (API) credentials](/docs/platform/your-signalwire-api-space): Find the project identifier (Project ID) and API token that SDKs and APIs use to authenticate. +- [Resources](/docs/platform/resources): Communication entities such as AI agents, SignalWire Markup Language (SWML) scripts, Subscribers, and Video Rooms. +- [Addresses](/docs/platform/addresses): The aliases, phone numbers, and Session Initiation Protocol (SIP) uniform resource identifiers (URIs) that make a Resource reachable. +- [Subscribers](/docs/platform/subscribers): Resources that represent users, and the basis of user management. +- [Phone numbers](/docs/platform/phone-numbers): Buy, port, and configure phone numbers. +- [Webhooks](/docs/platform/webhooks): Receive events about calls and messages over Hypertext Transfer Protocol (HTTP). -### Build an AI voice agent +### Understand the communication channels -- Recommended: [Server SDKs](/docs/server-sdks) -- Use when: you want an LLM-powered phone agent with prompts, tools, state, or several agents working together. -- Start: [Server SDK quick start](/docs/server-sdks/guides/quickstart), then [Server SDK architecture](/docs/server-sdks/guides/architecture) -- Also possible: the [SWML `ai` method](/docs/swml/reference/calling/ai) when AI is one step inside a declarative call flow, or [SignalWire AI](/docs/platform/ai) to configure an agent in the Dashboard without writing code. -- Do not start with: the Compatibility API, which does not support SignalWire AI. +- [Calling](/docs/platform/calling): Voice, video, fax, and AI on one platform. +- [Voice](/docs/platform/voice): Programmable calling with integrated AI. +- [Video](/docs/platform/video): Programmable video conferencing. +- [Fax](/docs/platform/fax): Send and receive faxes. +- [Messaging](/docs/platform/messaging): Programmable Short Message Service (SMS) and Multimedia Messaging Service (MMS). +- [WhatsApp](/docs/platform/messaging/whatsapp): Send and receive WhatsApp messages. Early access. +- [Chat](/docs/platform/chat): Programmable chat for applications. +- [AI](/docs/platform/ai): Conversational agents directed by your code, data, and business rules. -### Add voice, video, or chat to a browser or mobile app +### Return call or messaging instructions from your server over HTTP, one synchronous request at a time -- Recommended: [Browser SDK](/docs/browser-sdk/v4/guides/overview) -- Use when: the media endpoint is a browser or a mobile client over WebRTC. -- Start: [Browser SDK overview](/docs/browser-sdk/v4/guides/overview), then [Browser SDK authentication](/docs/browser-sdk/v4/guides/authentication) -- Authentication: authenticated-user applications issue Subscriber Access Tokens from a trusted backend. Public widgets can instead use an embed token without running a backend. The media itself flows over WebRTC between the client and SignalWire. +- [Server SDKs](/docs/server-sdks/guides/quickstart) +- [SWML](/docs/swml/guides) -### Build a server-side call or messaging application +### Handle communication over a persistent WebSocket connection, issuing commands and reacting to events asynchronously -- Recommended: [Server SDKs](/docs/server-sdks) -- Use when: your backend routes calls, sends messages, or runs interactive voice response. -- Start: the [Server SDK landing page](/docs/server-sdks), then choose the guide or reference for your language and control model. -- Choose [Relay](/docs/server-sdks/guides/relay-client) for persistent, event-driven call or messaging control; choose [SWML](/docs/swml) when your application only needs to return declarative instructions over HTTP or you want SignalWire to host the script. +- [Server SDKs, Relay client](/docs/server-sdks/guides/relay-client) +- [Browser SDK](/docs/browser-sdk/v4/guides/overview) -### Control a live call in real time +### Act on a live call, or send a message, with a single HTTP request -- Recommended: [Relay, in the Server SDKs](/docs/server-sdks/guides/relay-client) -- Use when: your application drives the call asynchronously, reacting to events as they arrive over an open connection — IVR menus, call routing, recording pipelines, custom media flows. -- Start: [Relay client guide](/docs/server-sdks/guides/relay-client) -- How it works: an asynchronous, event-driven WebSocket connection using JSON-RPC 2.0. Your process subscribes to contexts and receives call events directly, so it needs no webhook endpoint. -- Also possible: [call commands over HTTP](/docs/apis/rest/calls/call-commands) act on an active call — update the dialplan, play, record, collect, transfer, transcribe, tap. Each is a synchronous request that returns immediately; a command that keeps running afterward reports its completion to your `status_url` webhooks. Choose Relay when your application reacts to events as the call unfolds, and call commands when a synchronous HTTP request fits your stack. +- [Server SDKs](/docs/server-sdks) +- [Representational State Transfer (REST) APIs](/docs/apis) -### Write call flows without an SDK dependency, or let SignalWire host them +### Run an AI agent in voice or text conversations -- Recommended: [SWML](/docs/swml) -- Use when: you want a language-agnostic document that fits into an existing stack with nothing to install, or you want SignalWire to host the script so you run no server of your own. -- Start: [Introduction to SWML](/docs/swml), then the [SWML quickstart](/docs/swml/guides) -- Deployment: use the [SWML quickstart](/docs/swml/guides) to host a script in the Dashboard, or follow the [deployment guide](/docs/swml/guides/deployment) to serve SWML from a web server or invoke it from a Relay application. For AI agents, a [Server SDK generates the SWML](/docs/server-sdks/guides/swml) from your agent configuration. -- Document types: [Calling SWML](/docs/swml/reference/calling) for voice calls and [Messaging SWML](/docs/swml/reference/messaging) for inbound SMS and MMS. Each has its own method set, so a Calling method is not valid in a Messaging document. -- Errors: [SWML error reference](/docs/swml/reference/errors) +- [Server SDKs](/docs/server-sdks/guides/quickstart) +- [SWML](/docs/swml/guides) +- [Dashboard AI agent](/docs/platform/resources#in-the-dashboard) -### Build without writing code +### Build call or messaging logic with low-code or no-code tools and no server of your own -- Recommended: [Call Flow Builder](/docs/call-flow-builder) -- Use when: the person building the flow is not a developer and wants a drag-and-drop editor in the Dashboard. -- Start: [Call Flow Builder overview](/docs/call-flow-builder), then the [node reference](/docs/call-flow-builder/reference/nodes) -- Also possible: [SignalWire AI](/docs/platform/ai) for an AI agent configured in the Dashboard. -- Beyond the built-in nodes: the [Request node](/docs/call-flow-builder/reference/request) calls an HTTP endpoint and the [Execute SWML node](/docs/call-flow-builder/reference/execute-swml) runs a SWML document returned by a remote URL, so a flow is not limited to the node set. +- [SWML](/docs/swml/guides) +- [Call Flow Builder](/docs/call-flow-builder) -### Provision and manage account resources +### Build a call flow with a no-code visual editor -- Recommended: [REST APIs](/docs/apis) -- Use when: your code creates, lists, or updates phone numbers, resources, subscribers, video rooms, call logs, or messages. -- Start: [REST API overview](/docs/apis), then [REST API authorization](/docs/apis/authorization) and the [REST API reference](/docs/apis/rest) -- Errors: [REST API error codes](/docs/apis/error-codes) -- Also covers: [call commands](/docs/apis/rest/calls/call-commands) that act on an active call over synchronous HTTP requests. To react to call events asynchronously instead, use Relay above. +- [Call Flow Builder](/docs/call-flow-builder) -### Migrate an existing Twilio application +### Browser or mobile app calling and chat -- Recommended: [Compatibility API](/docs/compatibility-api) -- Use when: you have a working Twilio-compatible application and want it running on SignalWire with minimal code change. -- Start: [Compatibility API overview](/docs/compatibility-api), then the [cXML specification](/docs/compatibility-api/cxml) and the [Compatibility REST API](/docs/compatibility-api/rest) -- Do not use: for SignalWire AI, which the Compatibility API does not support. Use SignalWire's native interfaces for AI features. +- [Browser SDK](/docs/browser-sdk/v4/guides/overview) +- [REST APIs](/docs/apis) -## Interfaces at a glance +### Video conferences -| Interface | What it is | Protocol | Pattern | Best fit | -|---|---|---|---|---| -| [Server SDKs — Agents](/docs/server-sdks) | Native libraries that build AI agents; they generate SWML for you | HTTPS | Declarative — return a SWML document describing the call flow | Voice bots, AI assistants, multi-step workflows | -| [Server SDKs — Relay](/docs/server-sdks/guides/relay-client) | Native libraries that control calls and messages over a persistent connection | WebSocket, JSON-RPC 2.0 (asynchronous) | Imperative — issue async commands and react to events | IVR systems, call routing, recording pipelines, custom media flows | -| [Browser SDK](/docs/browser-sdk/v4/guides/overview) | Client library for browser and mobile endpoints | WebRTC | Client-side session using a Subscriber Access Token or embed token | Voice, video, and chat inside an application | -| [SWML](/docs/swml) | Declarative call and messaging documents written in JSON or YAML | HTTP | Request/response — SignalWire requests a document, your side returns one | Language-agnostic flows with no SDK dependency, or scripts hosted by SignalWire | -| [REST APIs](/docs/apis) | Resource management, plus command-based control of active calls | HTTPS (synchronous) | Request/response — commands return immediately; long-running ones report completion to webhooks | Provisioning and querying resources, and HTTP-driven call commands | -| [Call Flow Builder](/docs/call-flow-builder) | Drag-and-drop flow editor in the Dashboard | None | Visual graph of nodes, with escape hatches to HTTP and SWML | No-code voice applications | -| [Compatibility API](/docs/compatibility-api) | Twilio-compatible REST API and cXML markup | HTTPS | Request/response — form-encoded requests and Call SIDs | Migrating an existing Twilio application | +- [Browser SDK](/docs/browser-sdk/v4/guides/overview) +- [REST APIs](/docs/apis) +- [Video](/docs/platform/video) -Development tools: [Wirestarter](/docs/platform/wirestarter) scaffolds a new project, and [SWSH](/docs/platform/swsh) tests SWML scripts and API calls interactively. +### Send and receive SMS and MMS -## Use this, not that +- [Server SDKs](/docs/server-sdks) +- [SWML](/docs/swml/guides) +- [REST APIs](/docs/apis) +- [Messaging](/docs/platform/messaging) -- Use the Server SDKs, not hand-written SWML, when the SDK supports your language and you want the document generated for you. -- Use SWML, not a Server SDK, when you want no SDK dependency in your stack or you want SignalWire to host the script. -- Use SWML, not Relay, when your side only returns a declarative sequence of call instructions and needs no open session. -- Use Relay, not HTTP call commands, when your application reacts to call events asynchronously as they arrive. Both can act on a live call; the difference is that Relay is event-driven over an open connection, while call commands are synchronous HTTP requests. -- Use HTTP call commands, not Relay, when a synchronous request fits your stack and you would rather take any follow-up by webhook than hold a connection open. -- Use the Browser SDK for media in a browser or mobile client. Issue Subscriber Access Tokens from a trusted backend for authenticated users; public widgets can use embed tokens without a backend. -- Use SignalWire's native interfaces for AI. The Compatibility API does not support it. -- Use Call Flow Builder when the person building the flow is not a developer, and an SDK when the flow belongs in version-controlled application code. +### Provision and manage numbers, resources, subscribers, and logs + +- [Server SDKs](/docs/server-sdks) +- [REST APIs](/docs/apis) + +### Migrate a Twilio application + +- [Compatibility API](/docs/compatibility-api) + +## Popular features + +Feature pages are grouped by channel. SDK pages come first when available, followed by SWML or platform pages when no SDK feature page exists. + +### Calling + +- [Call recording](/docs/server-sdks/guides/call-recording): Record a call and receive a completion webhook with the uniform resource locator (URL) for downloading the recording. +- [Call transfer](/docs/server-sdks/guides/call-transfer): Transfer a call to a phone number, SIP endpoint, or SWML document. +- [Real-time transcription](/docs/server-sdks/reference/python/relay/call/live-transcribe): Start or stop live transcription on a call. +- [Live translation](/docs/server-sdks/reference/python/relay/call/live-translate): Start or stop live translation on a call. +- [Audio streaming](/docs/server-sdks/reference/python/relay/call/stream): Stream call audio to a WebSocket endpoint. +- [Conferencing](/docs/server-sdks/reference/python/relay/call/join-conference): Join a call to an ad-hoc audio conference. +- [Secure payments](/docs/server-sdks/reference/python/relay/call/pay): Collect payment information on a call. +- [Collect caller input](/docs/server-sdks/reference/python/relay/call/play-and-collect): Play audio and collect speech or key presses. +- [Answering machine detection](/docs/server-sdks/reference/python/relay/call/detect): Detect answering machines, fax tones, or digits on a call. +- [Noise reduction](/docs/server-sdks/reference/python/relay/call/denoise): Start noise reduction on a call. +- [Call queues](/docs/server-sdks/reference/python/relay/call/queue-enter): Place a call into a named queue. +- [Text-to-speech voices](/docs/platform/voice/tts): Supported providers, voices, and languages. +- [Caller ID and caller name (CNAM)](/docs/platform/voice/how-to-set-caller-id-or-cnam): Configure an outbound number and a caller name that supported carriers can display. +- [STIR/SHAKEN caller verification](/docs/platform/voice/stir-shaken): STIR (Secure Telephone Identity Revisited) and SHAKEN (Secure Handling of Asserted information using toKENs) provide caller identity attestation for outbound calls. +- [SIP](/docs/platform/voice/sip): SIP trunking, SIP credentials, and bring your own carrier. +- [Web Real-Time Communication (WebRTC)](/docs/platform/voice/what-is-webrtc): How SignalWire uses WebRTC for real-time media. + +### Video + +- [Voice and video in the browser](/docs/browser-sdk/v4/guides/build-voice-video): Build a calling or video experience with the Browser SDK. +- [Screen sharing](/docs/browser-sdk/v4/guides/screen-sharing): Share a screen in a video call. +- [Layouts and participant views](/docs/browser-sdk/v4/guides/layouts): Arrange participants in a video call. +- [Device management](/docs/browser-sdk/v4/guides/device-management): Choose cameras, microphones, and speakers. +- [Click-to-call widget](/docs/browser-sdk/v4/guides/click-to-call-widget): Add a call button to a public web page. +- [Join a video room from a call](/docs/server-sdks/reference/python/relay/call/join-room): Join a video or audio room from a call. + +### Messaging + +- [Chat in the browser](/docs/browser-sdk/v4/guides/messaging-chat): Add messaging and chat to a browser app. +- [Send SMS and MMS](/docs/server-sdks/reference/python/relay/client/send-message): Send an outbound SMS or MMS message. +- [Campaign Registry registration for 10-digit long code (10DLC) numbers](/docs/platform/messaging/campaign-registry/registration): Register a brand and campaign for business SMS in the United States. +- [Toll-free verification](/docs/platform/messaging/toll-free-numbers): Verify a toll-free number for messaging. +- [Hosted messaging](/docs/platform/messaging/hosted-messaging): Port messaging service only to SignalWire. +- [Delivery best practices](/docs/platform/messaging/sms-best-practices): Improve message delivery rates. +- [Character limits](/docs/platform/messaging/character-limits): How message segments are measured and priced. +- [Multipurpose Internet Mail Extensions (MIME) types](/docs/platform/messaging/mime-types): Media types supported in MMS. + +### AI + +- [Tool calling with the SignalWire AI Gateway (SWAIG)](/docs/server-sdks/guides/swaig): Let the agent call functions on your backend through webhooks. +- [Skills](/docs/server-sdks/guides/understanding-skills): Add reusable capabilities to an agent without custom code. +- [Prompts and the Prompt Object Model (POM)](/docs/server-sdks/guides/prompts-pom): Build structured prompts with sections, subsections, and bullets. +- [Contexts and workflows](/docs/server-sdks/guides/contexts-workflows): Multi-step conversation flows with branching and validation. +- [DataMap](/docs/server-sdks/guides/data-map): Call REST APIs from the agent without running a server. +- [Search and knowledge](/docs/server-sdks/guides/search-knowledge): Add knowledge search to an agent with vector indexes. +- [Model Context Protocol (MCP) Gateway](/docs/server-sdks/guides/mcp-gateway): Connect MCP servers to an agent. +- [Multi-agent servers](/docs/server-sdks/guides/multi-agent): Run several agents on one server. +- [Voice and language](/docs/server-sdks/guides/voice-language): Configure the agent's voice, language, and pronunciation. +- [AI sidecar](/docs/swml/reference/calling/ai-sidecar): Attach a real-time AI observer to a live call. +- [Prompt engineering](/docs/platform/ai/prompt-engineering): Where to place instructions and how to structure a prompt. +- [Conversation analytics](/docs/platform/ai/analytics): Analyze conversations with post-prompt reports and debug webhooks. +- [Sensitive content](/docs/platform/ai/content-redaction): Redact sensitive content from AI conversations. [Create a SignalWire account](https://signalwire.com/signup), or follow the [Platform getting-started guide](/docs/platform/getting-started) to configure an existing Space. diff --git a/scripts/llm-export-cases.json b/scripts/llm-export-cases.json index 05b3757e9c..2b18cddfe9 100644 --- a/scripts/llm-export-cases.json +++ b/scripts/llm-export-cases.json @@ -4,15 +4,23 @@ "path": ".md", "contains": [ "SignalWire is a programmable communications platform", - "The SignalWire SDKs are the recommended way to build", - "## Start here", - "## Choose by goal", - "### Build an AI voice agent", - "[Understand Resources](/docs/platform/resources)", - "[Understand Addresses](/docs/platform/addresses)", - "## Interfaces at a glance", - "## Use this, not that", - "Do not start with: the Compatibility API, which does not support SignalWire AI.", + "The SignalWire software development kits (SDKs) are the recommended way to build", + "## Choose a path", + "### Learn the platform and get started", + "### Understand the communication channels", + "- [Resources](/docs/platform/resources)", + "- [Addresses](/docs/platform/addresses)", + "- [Server SDKs, Relay client](/docs/server-sdks/guides/relay-client)", + "### Run an AI agent in voice or text conversations", + "### Build call or messaging logic with low-code or no-code tools and no server of your own", + "### Build a call flow with a no-code visual editor", + "### Migrate a Twilio application", + "## Popular features", + "### Calling", + "### Video", + "### Messaging", + "### AI", + "- [Call recording](/docs/server-sdks/guides/call-recording):", "[Platform getting-started guide](/docs/platform/getting-started)", "Twilio and TwiML are trademarks" ], @@ -20,10 +28,16 @@ "class=\"fern-page-heading", " Date: Fri, 4 Sep 2026 13:06:49 -0400 Subject: [PATCH 03/13] docs: explain markdown export navigation --- fern/docs.yml | 9 +++++++-- fern/products/home/pages/welcome.mdx | 7 +++++++ fern/snippets/llms-hint.mdx | 2 +- scripts/llm-export-cases.json | 10 ++++++++++ 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/fern/docs.yml b/fern/docs.yml index 48c3fd1571..1c2187a075 100644 --- a/fern/docs.yml +++ b/fern/docs.yml @@ -25,8 +25,13 @@ landing-page: agents: page-directive: >- - For a complete index of all SignalWire documentation pages, fetch - https://signalwire.com/docs/llms.txt + Fetch every SignalWire documentation URL under https://signalwire.com/docs + with the HTTP request header `Accept: text/markdown` to receive clean + Markdown. Start at https://signalwire.com/docs/llms.txt. The root index is + an orientation page and directory of product-level `llms.txt` indexes, not + an exhaustive list of documentation pages. Open the relevant product index + to discover its page links, follow any nested `llms.txt` indexes, and fetch + selected pages with the same `Accept` header. analytics: gtm: diff --git a/fern/products/home/pages/welcome.mdx b/fern/products/home/pages/welcome.mdx index 46cdc5a780..bb38bb3506 100644 --- a/fern/products/home/pages/welcome.mdx +++ b/fern/products/home/pages/welcome.mdx @@ -124,6 +124,13 @@ layout: custom SignalWire is a programmable communications platform for building voice, video, messaging, and artificial intelligence (AI) applications. The SignalWire software development kits (SDKs) are the recommended way to build. Start with the first section to learn the platform, then pick the section that matches how your application takes part. +## Navigate the documentation + +- Request every `https://signalwire.com/docs/...` URL with the HTTP header `Accept: text/markdown` to receive clean Markdown. +- Treat the [root `llms.txt`](https://signalwire.com/docs/llms.txt) as an orientation page and directory of product indexes, not an exhaustive list of documentation pages. +- Open the relevant product's `llms.txt` to discover its documentation links. Follow any nested `llms.txt` indexes for large sections, then fetch the selected page with the same `Accept` header. +- If a page is not listed in the root index, check its product index before concluding that the documentation does not contain it. + ## Choose a path Each heading below describes a setup task or a way an application can handle communication. Links go to a landing or getting-started page for each option, listed in order of preference. diff --git a/fern/snippets/llms-hint.mdx b/fern/snippets/llms-hint.mdx index 503e0011b4..5c1eea278c 100644 --- a/fern/snippets/llms-hint.mdx +++ b/fern/snippets/llms-hint.mdx @@ -1,3 +1,3 @@ - + diff --git a/scripts/llm-export-cases.json b/scripts/llm-export-cases.json index 2b18cddfe9..2cfc4825e7 100644 --- a/scripts/llm-export-cases.json +++ b/scripts/llm-export-cases.json @@ -5,6 +5,10 @@ "contains": [ "SignalWire is a programmable communications platform", "The SignalWire software development kits (SDKs) are the recommended way to build", + "## Navigate the documentation", + "`Accept: text/markdown` to receive clean Markdown", + "directory of product indexes, not an exhaustive list of documentation pages", + "Open the relevant product's `llms.txt` to discover its documentation links", "## Choose a path", "### Learn the platform and get started", "### Understand the communication channels", @@ -37,6 +41,7 @@ "| Channel | Features |", "\n|", "JSON-RPC", + "For a complete index of all SignalWire documentation pages", "### Run an AI agent on calls and messages", "/docs/platform/getting-started#ai-application", "/docs/platform/getting-started#server-application" @@ -48,6 +53,10 @@ "contains": [ "SignalWire is a programmable communications platform", "The SignalWire software development kits (SDKs) are the recommended way to build", + "## Navigate the documentation", + "`Accept: text/markdown` to receive clean Markdown", + "directory of product indexes, not an exhaustive list of documentation pages", + "Open the relevant product's `llms.txt` to discover its documentation links", "## Choose a path", "### Learn the platform and get started", "### Understand the communication channels", @@ -77,6 +86,7 @@ "| Channel | Features |", "\n|", "JSON-RPC", + "For a complete index of all SignalWire documentation pages", "### Run an AI agent on calls and messages", "/docs/platform/getting-started#ai-application", "/docs/platform/getting-started#server-application" From c31769c50111c99d65ec6b89374a644eaa7ff198 Mon Sep 17 00:00:00 2001 From: Devon-White Date: Fri, 4 Sep 2026 13:18:21 -0400 Subject: [PATCH 04/13] docs: remove obsolete LLM export scripts and test cases --- scripts/check-llm-exports.mjs | 110 -------- scripts/check-llm-exports.test.js | 35 --- scripts/llm-export-cases.json | 434 ------------------------------ 3 files changed, 579 deletions(-) delete mode 100644 scripts/check-llm-exports.mjs delete mode 100644 scripts/check-llm-exports.test.js delete mode 100644 scripts/llm-export-cases.json diff --git a/scripts/check-llm-exports.mjs b/scripts/check-llm-exports.mjs deleted file mode 100644 index a25c37099e..0000000000 --- a/scripts/check-llm-exports.mjs +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env node - -import { readFile } from 'node:fs/promises'; -import { dirname, join } from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url)); -const DEFAULT_MANIFEST = join(SCRIPT_DIR, 'llm-export-cases.json'); -const MAX_ATTEMPTS = 5; - -function normalizeDocsBase(rawBaseUrl) { - const url = new URL(rawBaseUrl); - url.hash = ''; - url.search = ''; - url.pathname = url.pathname.replace(/\/+$/, ''); - if (!url.pathname.endsWith('/docs')) url.pathname = `${url.pathname || ''}/docs`.replace(/\/{2,}/g, '/'); - return url.toString().replace(/\/$/, ''); -} - -function checkBody(testCase, body) { - const failures = []; - - if (/(?:^|\n)# Page Not Found(?:\n|$)/.test(body)) { - failures.push('received a Page Not Found stub'); - } - - if (body.includes('X-Amz-Signature')) { - failures.push('contains a presigned image URL'); - } - - for (const required of testCase.contains ?? []) { - if (!body.includes(required)) failures.push(`missing required text: ${JSON.stringify(required)}`); - } - - for (const forbidden of testCase.notContains ?? []) { - if (body.includes(forbidden)) failures.push(`contains forbidden text: ${JSON.stringify(forbidden)}`); - } - - return failures; -} - -async function fetchWithRetry(url) { - let lastError; - - for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) { - try { - const response = await fetch(url, { - headers: { 'User-Agent': 'signalwire-docs-llm-export-check' }, - signal: AbortSignal.timeout(30_000), - }); - - if (response.ok) return response.text(); - lastError = new Error(`HTTP ${response.status}`); - if (response.status < 500 && response.status !== 429) break; - } catch (error) { - lastError = error; - } - - if (attempt < MAX_ATTEMPTS) { - await new Promise((resolve) => setTimeout(resolve, 2 ** attempt * 1_000)); - } - } - - throw lastError; -} - -async function main() { - const [rawBaseUrl, manifestPath = DEFAULT_MANIFEST] = process.argv.slice(2); - if (!rawBaseUrl) { - console.error('Usage: node scripts/check-llm-exports.mjs [manifest]'); - process.exit(2); - } - - const baseUrl = normalizeDocsBase(rawBaseUrl); - const manifest = JSON.parse(await readFile(manifestPath, 'utf8')); - const testCases = manifest.flatMap((testCase) => { - const paths = testCase.paths ?? [testCase.path]; - return paths.map((path) => ({ ...testCase, path, paths: undefined })); - }); - const failures = []; - - await Promise.all(testCases.map(async (testCase) => { - const url = `${baseUrl}${testCase.path}`; - try { - const body = await fetchWithRetry(url); - for (const failure of checkBody(testCase, body)) { - failures.push(`${testCase.name} (${url}): ${failure}`); - } - } catch (error) { - failures.push(`${testCase.name} (${url}): ${error.message}`); - } - })); - - if (failures.length > 0) { - console.error(`LLM export checks failed (${failures.length}):`); - for (const failure of failures) console.error(`- ${failure}`); - process.exit(1); - } - - console.log(`LLM export checks passed for ${testCases.length} routes.`); -} - -export { checkBody, normalizeDocsBase }; - -if (process.argv[1] === fileURLToPath(import.meta.url)) { - main().catch((error) => { - console.error(error.stack ?? error.message); - process.exit(2); - }); -} diff --git a/scripts/check-llm-exports.test.js b/scripts/check-llm-exports.test.js deleted file mode 100644 index f4d2e4269c..0000000000 --- a/scripts/check-llm-exports.test.js +++ /dev/null @@ -1,35 +0,0 @@ -import assert from 'node:assert/strict'; -import test from 'node:test'; - -import { checkBody, normalizeDocsBase } from './check-llm-exports.mjs'; - -test('normalizes Fern preview URLs to the docs base', () => { - assert.equal(normalizeDocsBase('https://example.docs.buildwithfern.com'), 'https://example.docs.buildwithfern.com/docs'); - assert.equal(normalizeDocsBase('https://example.docs.buildwithfern.com/docs/'), 'https://example.docs.buildwithfern.com/docs'); -}); - -test('reports missing and forbidden export content', () => { - const failures = checkBody({ - contains: ['orientation', 'functional link'], - notContains: ['class="fern-page-heading"'], - }, 'orientation\nclass="fern-page-heading"'); - - assert.deepEqual(failures, [ - 'missing required text: "functional link"', - 'contains forbidden text: "class=\\"fern-page-heading\\""', - ]); -}); - -test('reports Fern soft-404 bodies even for notContains-only cases', () => { - const failures = checkBody({ - notContains: [' Documentation index\n\n# Page Not Found\n'); - - assert.deepEqual(failures, ['received a Page Not Found stub']); -}); - -test('reports presigned image URLs', () => { - const failures = checkBody({}, 'https://example.com/image.webp?X-Amz-Signature=temporary'); - - assert.deepEqual(failures, ['contains a presigned image URL']); -}); diff --git a/scripts/llm-export-cases.json b/scripts/llm-export-cases.json deleted file mode 100644 index 2cfc4825e7..0000000000 --- a/scripts/llm-export-cases.json +++ /dev/null @@ -1,434 +0,0 @@ -[ - { - "name": "Documentation homepage", - "path": ".md", - "contains": [ - "SignalWire is a programmable communications platform", - "The SignalWire software development kits (SDKs) are the recommended way to build", - "## Navigate the documentation", - "`Accept: text/markdown` to receive clean Markdown", - "directory of product indexes, not an exhaustive list of documentation pages", - "Open the relevant product's `llms.txt` to discover its documentation links", - "## Choose a path", - "### Learn the platform and get started", - "### Understand the communication channels", - "- [Resources](/docs/platform/resources)", - "- [Addresses](/docs/platform/addresses)", - "- [Server SDKs, Relay client](/docs/server-sdks/guides/relay-client)", - "### Run an AI agent in voice or text conversations", - "### Build call or messaging logic with low-code or no-code tools and no server of your own", - "### Build a call flow with a no-code visual editor", - "### Migrate a Twilio application", - "## Popular features", - "### Calling", - "### Video", - "### Messaging", - "### AI", - "- [Call recording](/docs/server-sdks/guides/call-recording):", - "[Platform getting-started guide](/docs/platform/getting-started)", - "Twilio and TwiML are trademarks" - ], - "notContains": [ - "class=\"fern-page-heading", - "PSTN · SIP · WebRTC\"]", - "Your backend — the only network hop off platform" - ], - "notContains": [ - "ai-agent-flow-themed.svg" - ] - }, - { - "name": "Prompt engineering surfaces", - "path": "/platform/ai/prompt-engineering.md", - "contains": [ - "## Prompt surfaces", - "| Prompt surface", - "A prompt requests behavior; code enforces it." - ], - "notContains": [ - "" - ], - "notContains": [ - "/assets/images/sdks/diagrams/" - ] - } -] From 56717edabd92339c2bb25d7ad6984a14cf44bcac Mon Sep 17 00:00:00 2001 From: Devon-White Date: Fri, 4 Sep 2026 13:21:22 -0400 Subject: [PATCH 05/13] docs: update documentation for AI applications and remove obsolete LLM export scripts --- fern/products/home/pages/welcome.mdx | 26 +++++++++++++------------- package.json | 1 - scripts/README.md | 2 -- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/fern/products/home/pages/welcome.mdx b/fern/products/home/pages/welcome.mdx index bb38bb3506..e9b1f2358f 100644 --- a/fern/products/home/pages/welcome.mdx +++ b/fern/products/home/pages/welcome.mdx @@ -2,7 +2,7 @@ id: 0f7a51b9-708a-48fa-bccf-d9a449c4489c title: SignalWire Documentation slug: / -description: Build voice, video, messaging, and artificial intelligence applications with SignalWire developer tools and implementation guides. +description: Build voice, video, messaging, and AI applications with SignalWire developer tools and implementation guides. hide-toc: true layout: custom --- @@ -121,8 +121,8 @@ layout: custom -SignalWire is a programmable communications platform for building voice, video, messaging, and artificial intelligence (AI) applications. -The SignalWire software development kits (SDKs) are the recommended way to build. Start with the first section to learn the platform, then pick the section that matches how your application takes part. +SignalWire is a programmable communications platform for building voice, video, messaging, and AI applications. +The SignalWire SDKs are the recommended way to build. Start with the first section to learn the platform, then pick the section that matches how your application takes part. ## Navigate the documentation @@ -139,12 +139,12 @@ Each heading below describes a setup task or a way an application can handle com - [Get started](/docs/platform/getting-started): Walkthrough of your Space, the build paths, and the core concepts. - [Create an account](/docs/platform/signing-up-for-a-space): Open a new Space on the SignalWire platform. -- [Application programming interface (API) credentials](/docs/platform/your-signalwire-api-space): Find the project identifier (Project ID) and API token that SDKs and APIs use to authenticate. +- [API credentials](/docs/platform/your-signalwire-api-space): Find the Project ID and API token that SDKs and APIs use to authenticate. - [Resources](/docs/platform/resources): Communication entities such as AI agents, SignalWire Markup Language (SWML) scripts, Subscribers, and Video Rooms. -- [Addresses](/docs/platform/addresses): The aliases, phone numbers, and Session Initiation Protocol (SIP) uniform resource identifiers (URIs) that make a Resource reachable. +- [Addresses](/docs/platform/addresses): The aliases, phone numbers, and SIP URIs that make a Resource reachable. - [Subscribers](/docs/platform/subscribers): Resources that represent users, and the basis of user management. - [Phone numbers](/docs/platform/phone-numbers): Buy, port, and configure phone numbers. -- [Webhooks](/docs/platform/webhooks): Receive events about calls and messages over Hypertext Transfer Protocol (HTTP). +- [Webhooks](/docs/platform/webhooks): Receive events about calls and messages over HTTP. ### Understand the communication channels @@ -152,8 +152,8 @@ Each heading below describes a setup task or a way an application can handle com - [Voice](/docs/platform/voice): Programmable calling with integrated AI. - [Video](/docs/platform/video): Programmable video conferencing. - [Fax](/docs/platform/fax): Send and receive faxes. -- [Messaging](/docs/platform/messaging): Programmable Short Message Service (SMS) and Multimedia Messaging Service (MMS). -- [WhatsApp](/docs/platform/messaging/whatsapp): Send and receive WhatsApp messages. Early access. +- [Messaging](/docs/platform/messaging): Programmable SMS and MMS. +- [WhatsApp](/docs/platform/messaging/whatsapp): Send and receive WhatsApp messages. - [Chat](/docs/platform/chat): Programmable chat for applications. - [AI](/docs/platform/ai): Conversational agents directed by your code, data, and business rules. @@ -170,7 +170,7 @@ Each heading below describes a setup task or a way an application can handle com ### Act on a live call, or send a message, with a single HTTP request - [Server SDKs](/docs/server-sdks) -- [Representational State Transfer (REST) APIs](/docs/apis) +- [REST APIs](/docs/apis) ### Run an AI agent in voice or text conversations @@ -220,7 +220,7 @@ Feature pages are grouped by channel. SDK pages come first when available, follo ### Calling -- [Call recording](/docs/server-sdks/guides/call-recording): Record a call and receive a completion webhook with the uniform resource locator (URL) for downloading the recording. +- [Call recording](/docs/server-sdks/guides/call-recording): Record a call and receive a completion webhook with the URL for downloading the recording. - [Call transfer](/docs/server-sdks/guides/call-transfer): Transfer a call to a phone number, SIP endpoint, or SWML document. - [Real-time transcription](/docs/server-sdks/reference/python/relay/call/live-transcribe): Start or stop live transcription on a call. - [Live translation](/docs/server-sdks/reference/python/relay/call/live-translate): Start or stop live translation on a call. @@ -232,7 +232,7 @@ Feature pages are grouped by channel. SDK pages come first when available, follo - [Noise reduction](/docs/server-sdks/reference/python/relay/call/denoise): Start noise reduction on a call. - [Call queues](/docs/server-sdks/reference/python/relay/call/queue-enter): Place a call into a named queue. - [Text-to-speech voices](/docs/platform/voice/tts): Supported providers, voices, and languages. -- [Caller ID and caller name (CNAM)](/docs/platform/voice/how-to-set-caller-id-or-cnam): Configure an outbound number and a caller name that supported carriers can display. +- [Caller ID and CNAM](/docs/platform/voice/how-to-set-caller-id-or-cnam): Configure an outbound number and a caller name that supported carriers can display. - [STIR/SHAKEN caller verification](/docs/platform/voice/stir-shaken): STIR (Secure Telephone Identity Revisited) and SHAKEN (Secure Handling of Asserted information using toKENs) provide caller identity attestation for outbound calls. - [SIP](/docs/platform/voice/sip): SIP trunking, SIP credentials, and bring your own carrier. - [Web Real-Time Communication (WebRTC)](/docs/platform/voice/what-is-webrtc): How SignalWire uses WebRTC for real-time media. @@ -255,7 +255,7 @@ Feature pages are grouped by channel. SDK pages come first when available, follo - [Hosted messaging](/docs/platform/messaging/hosted-messaging): Port messaging service only to SignalWire. - [Delivery best practices](/docs/platform/messaging/sms-best-practices): Improve message delivery rates. - [Character limits](/docs/platform/messaging/character-limits): How message segments are measured and priced. -- [Multipurpose Internet Mail Extensions (MIME) types](/docs/platform/messaging/mime-types): Media types supported in MMS. +- [MIME types](/docs/platform/messaging/mime-types): Media types supported in MMS. ### AI @@ -265,7 +265,7 @@ Feature pages are grouped by channel. SDK pages come first when available, follo - [Contexts and workflows](/docs/server-sdks/guides/contexts-workflows): Multi-step conversation flows with branching and validation. - [DataMap](/docs/server-sdks/guides/data-map): Call REST APIs from the agent without running a server. - [Search and knowledge](/docs/server-sdks/guides/search-knowledge): Add knowledge search to an agent with vector indexes. -- [Model Context Protocol (MCP) Gateway](/docs/server-sdks/guides/mcp-gateway): Connect MCP servers to an agent. +- [MCP Gateway](/docs/server-sdks/guides/mcp-gateway): Connect MCP servers to an agent. - [Multi-agent servers](/docs/server-sdks/guides/multi-agent): Run several agents on one server. - [Voice and language](/docs/server-sdks/guides/voice-language): Configure the agent's voice, language, and pronunciation. - [AI sidecar](/docs/swml/reference/calling/ai-sidecar): Attach a real-time AI observer to a live call. diff --git a/package.json b/package.json index c9612a0685..8027a94083 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,6 @@ "check-links": "node scripts/check-links.js", "check-md-exports": "node scripts/check-md-exports.js", "export-rag-corpus": "node scripts/export-rag-corpus.js", - "check-llm-exports": "node scripts/check-llm-exports.mjs", "test:scripts": "node --test \"scripts/**/*.test.js\"", "format:specs": "yarn workspace signalwire-docs-specs format", "format:specs:check": "yarn workspace signalwire-docs-specs format:check", diff --git a/scripts/README.md b/scripts/README.md index 4af9d585d8..cd447a4617 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -11,8 +11,6 @@ dependencies beyond what's in `package.json`) and share conventions: hand-rolled | `check-md-exports.js` | `yarn check-md-exports` | Audit the `.md` exports and `llms.txt` indexes served for AI consumption. | | `check-md-exports.test.js` | `yarn test:scripts` | Fixture tests locking in `check-md-exports.js`'s detection heuristics. | | `export-rag-corpus.js` | `yarn export-rag-corpus` | Download the raw Fern Markdown response for every page in `sitemap.xml`. | -| `check-llm-exports.mjs` | `yarn check-llm-exports ` | Check issue-specific required and forbidden content in preview `.md` exports. | -| `llm-export-cases.json` | Used by `check-llm-exports.mjs` | Manifest of routes and semantic export assertions. | | `llm-agent-scenarios.md` | Manual preview evaluation | Context-isolated agent prompts and expected documentation destinations. | | `postman/` | `yarn postman:build` / `postman:publish` | Build and publish the Postman collection from the OpenAPI specs. | | `utils/logger.js` | — | Shared leveled logger (text/JSON, collectors) used by the scripts above. | From 6bfe0712b6fc08b1b92a4cafcda6b017549056bf Mon Sep 17 00:00:00 2001 From: Devon-White Date: Fri, 4 Sep 2026 14:03:56 -0400 Subject: [PATCH 06/13] docs: clarify LLM index navigation --- fern/docs.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fern/docs.yml b/fern/docs.yml index 1c2187a075..a8b277799a 100644 --- a/fern/docs.yml +++ b/fern/docs.yml @@ -25,13 +25,13 @@ landing-page: agents: page-directive: >- - Fetch every SignalWire documentation URL under https://signalwire.com/docs - with the HTTP request header `Accept: text/markdown` to receive clean - Markdown. Start at https://signalwire.com/docs/llms.txt. The root index is - an orientation page and directory of product-level `llms.txt` indexes, not - an exhaustive list of documentation pages. Open the relevant product index - to discover its page links, follow any nested `llms.txt` indexes, and fetch - selected pages with the same `Accept` header. + Request every URL under https://signalwire.com/docs with the HTTP header + `Accept: text/markdown` to receive clean Markdown. Begin at + https://signalwire.com/docs/llms.txt. This root file is an orientation page, + not a complete list of documentation pages. Its product-level `llms.txt` + indexes are listed at the bottom. Open the relevant product index to find + page links, follow any nested `llms.txt` indexes, and request the selected + pages with the same header. analytics: gtm: From 0a445f507cbd1a86c21600e53582e0c55ceba6ec Mon Sep 17 00:00:00 2001 From: Devon-White Date: Fri, 4 Sep 2026 14:09:09 -0400 Subject: [PATCH 07/13] docs: align LLM navigation hint --- fern/snippets/llms-hint.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fern/snippets/llms-hint.mdx b/fern/snippets/llms-hint.mdx index 5c1eea278c..bb262df30c 100644 --- a/fern/snippets/llms-hint.mdx +++ b/fern/snippets/llms-hint.mdx @@ -1,3 +1,3 @@ - + From 6559d002c6b2ef8157d138d87fab0dd6250363aa Mon Sep 17 00:00:00 2001 From: Devon-White Date: Fri, 4 Sep 2026 15:36:56 -0400 Subject: [PATCH 08/13] fix: address LLM routing review findings --- fern/docs.yml | 11 +- fern/products/home/pages/welcome.mdx | 40 +-- fern/snippets/llms-hint.mdx | 2 +- package.json | 1 + scripts/README.md | 2 + scripts/check-llm-exports.mjs | 110 ++++++++ scripts/check-llm-exports.test.js | 35 +++ scripts/check-md-exports.js | 14 +- scripts/check-md-exports.test.js | 7 +- scripts/llm-export-cases.json | 382 +++++++++++++++++++++++++++ 10 files changed, 574 insertions(+), 30 deletions(-) create mode 100644 scripts/check-llm-exports.mjs create mode 100644 scripts/check-llm-exports.test.js create mode 100644 scripts/llm-export-cases.json diff --git a/fern/docs.yml b/fern/docs.yml index a8b277799a..608f9c93b5 100644 --- a/fern/docs.yml +++ b/fern/docs.yml @@ -25,13 +25,10 @@ landing-page: agents: page-directive: >- - Request every URL under https://signalwire.com/docs with the HTTP header - `Accept: text/markdown` to receive clean Markdown. Begin at - https://signalwire.com/docs/llms.txt. This root file is an orientation page, - not a complete list of documentation pages. Its product-level `llms.txt` - indexes are listed at the bottom. Open the relevant product index to find - page links, follow any nested `llms.txt` indexes, and request the selected - pages with the same header. + Fetch clean Markdown by appending `.md` to any page URL under + https://signalwire.com/docs or requesting it with the HTTP header + `Accept: text/markdown`. Start at https://signalwire.com/docs/llms.txt. + Append `/llms.txt` to a product or section URL for its index. analytics: gtm: diff --git a/fern/products/home/pages/welcome.mdx b/fern/products/home/pages/welcome.mdx index e9b1f2358f..a0b9855d0a 100644 --- a/fern/products/home/pages/welcome.mdx +++ b/fern/products/home/pages/welcome.mdx @@ -122,18 +122,18 @@ layout: custom SignalWire is a programmable communications platform for building voice, video, messaging, and AI applications. -The SignalWire SDKs are the recommended way to build. Start with the first section to learn the platform, then pick the section that matches how your application takes part. +Start with the navigation and platform sections, then choose the path that matches how your application takes part. ## Navigate the documentation -- Request every `https://signalwire.com/docs/...` URL with the HTTP header `Accept: text/markdown` to receive clean Markdown. -- Treat the [root `llms.txt`](https://signalwire.com/docs/llms.txt) as an orientation page and directory of product indexes, not an exhaustive list of documentation pages. -- Open the relevant product's `llms.txt` to discover its documentation links. Follow any nested `llms.txt` indexes for large sections, then fetch the selected page with the same `Accept` header. +- Treat the [root `llms.txt`](https://signalwire.com/docs/llms.txt) as an orientation page and directory, not an exhaustive list of documentation pages. Product indexes appear under `## Products` at the bottom of the file. +- Fetch a documentation page as clean Markdown by appending `.md` to its URL or requesting it with the HTTP header `Accept: text/markdown`. +- Open a product index to discover its page links. To inspect a product or section index directly, append `/llms.txt` to its documentation URL; nested indexes are not necessarily linked from their parent index. - If a page is not listed in the root index, check its product index before concluding that the documentation does not contain it. ## Choose a path -Each heading below describes a setup task or a way an application can handle communication. Links go to a landing or getting-started page for each option, listed in order of preference. +Each heading below describes a setup task or a way an application can handle communication. Links go to a landing or getting-started page; choose the option that matches your architecture. ### Learn the platform and get started @@ -141,7 +141,7 @@ Each heading below describes a setup task or a way an application can handle com - [Create an account](/docs/platform/signing-up-for-a-space): Open a new Space on the SignalWire platform. - [API credentials](/docs/platform/your-signalwire-api-space): Find the Project ID and API token that SDKs and APIs use to authenticate. - [Resources](/docs/platform/resources): Communication entities such as AI agents, SignalWire Markup Language (SWML) scripts, Subscribers, and Video Rooms. -- [Addresses](/docs/platform/addresses): The aliases, phone numbers, and SIP URIs that make a Resource reachable. +- [Addresses](/docs/platform/addresses): The aliases, phone numbers, and Session Initiation Protocol (SIP) URIs that make a Resource reachable. - [Subscribers](/docs/platform/subscribers): Resources that represent users, and the basis of user management. - [Phone numbers](/docs/platform/phone-numbers): Buy, port, and configure phone numbers. - [Webhooks](/docs/platform/webhooks): Receive events about calls and messages over HTTP. @@ -152,15 +152,23 @@ Each heading below describes a setup task or a way an application can handle com - [Voice](/docs/platform/voice): Programmable calling with integrated AI. - [Video](/docs/platform/video): Programmable video conferencing. - [Fax](/docs/platform/fax): Send and receive faxes. -- [Messaging](/docs/platform/messaging): Programmable SMS and MMS. +- [Messaging](/docs/platform/messaging): Programmable Short Message Service (SMS) and Multimedia Messaging Service (MMS). - [WhatsApp](/docs/platform/messaging/whatsapp): Send and receive WhatsApp messages. - [Chat](/docs/platform/chat): Programmable chat for applications. - [AI](/docs/platform/ai): Conversational agents directed by your code, data, and business rules. +### Set up a local development and testing environment + +- [WireStarter](/docs/platform/wirestarter) + +### Configure and manage a Space from the command line + +- [SWSH](/docs/platform/swsh) + ### Return call or messaging instructions from your server over HTTP, one synchronous request at a time -- [Server SDKs](/docs/server-sdks/guides/quickstart) - [SWML](/docs/swml/guides) +- [Server SDKs for AI agents](/docs/server-sdks/guides/quickstart) ### Handle communication over a persistent WebSocket connection, issuing commands and reacting to events asynchronously @@ -178,14 +186,10 @@ Each heading below describes a setup task or a way an application can handle com - [SWML](/docs/swml/guides) - [Dashboard AI agent](/docs/platform/resources#in-the-dashboard) -### Build call or messaging logic with low-code or no-code tools and no server of your own - -- [SWML](/docs/swml/guides) -- [Call Flow Builder](/docs/call-flow-builder) - -### Build a call flow with a no-code visual editor +### Build a voice application with low-code or no-code tools and no server of your own - [Call Flow Builder](/docs/call-flow-builder) +- [SWML](/docs/swml/guides) ### Browser or mobile app calling and chat @@ -220,7 +224,7 @@ Feature pages are grouped by channel. SDK pages come first when available, follo ### Calling -- [Call recording](/docs/server-sdks/guides/call-recording): Record a call and receive a completion webhook with the URL for downloading the recording. +- [Call recording](/docs/swml/guides/record-calls): Record an ongoing call with a hosted SWML script and access the recording in your Space. - [Call transfer](/docs/server-sdks/guides/call-transfer): Transfer a call to a phone number, SIP endpoint, or SWML document. - [Real-time transcription](/docs/server-sdks/reference/python/relay/call/live-transcribe): Start or stop live transcription on a call. - [Live translation](/docs/server-sdks/reference/python/relay/call/live-translate): Start or stop live translation on a call. @@ -232,7 +236,7 @@ Feature pages are grouped by channel. SDK pages come first when available, follo - [Noise reduction](/docs/server-sdks/reference/python/relay/call/denoise): Start noise reduction on a call. - [Call queues](/docs/server-sdks/reference/python/relay/call/queue-enter): Place a call into a named queue. - [Text-to-speech voices](/docs/platform/voice/tts): Supported providers, voices, and languages. -- [Caller ID and CNAM](/docs/platform/voice/how-to-set-caller-id-or-cnam): Configure an outbound number and a caller name that supported carriers can display. +- [Caller ID and Calling Name (CNAM)](/docs/platform/voice/how-to-set-caller-id-or-cnam): Configure an outbound number and a caller name that supported carriers can display. - [STIR/SHAKEN caller verification](/docs/platform/voice/stir-shaken): STIR (Secure Telephone Identity Revisited) and SHAKEN (Secure Handling of Asserted information using toKENs) provide caller identity attestation for outbound calls. - [SIP](/docs/platform/voice/sip): SIP trunking, SIP credentials, and bring your own carrier. - [Web Real-Time Communication (WebRTC)](/docs/platform/voice/what-is-webrtc): How SignalWire uses WebRTC for real-time media. @@ -263,9 +267,9 @@ Feature pages are grouped by channel. SDK pages come first when available, follo - [Skills](/docs/server-sdks/guides/understanding-skills): Add reusable capabilities to an agent without custom code. - [Prompts and the Prompt Object Model (POM)](/docs/server-sdks/guides/prompts-pom): Build structured prompts with sections, subsections, and bullets. - [Contexts and workflows](/docs/server-sdks/guides/contexts-workflows): Multi-step conversation flows with branching and validation. -- [DataMap](/docs/server-sdks/guides/data-map): Call REST APIs from the agent without running a server. +- [DataMap](/docs/server-sdks/guides/data-map): Call REST APIs from the agent without running code on your server. - [Search and knowledge](/docs/server-sdks/guides/search-knowledge): Add knowledge search to an agent with vector indexes. -- [MCP Gateway](/docs/server-sdks/guides/mcp-gateway): Connect MCP servers to an agent. +- [Model Context Protocol (MCP) Gateway](/docs/server-sdks/guides/mcp-gateway): Connect MCP servers to an agent. - [Multi-agent servers](/docs/server-sdks/guides/multi-agent): Run several agents on one server. - [Voice and language](/docs/server-sdks/guides/voice-language): Configure the agent's voice, language, and pronunciation. - [AI sidecar](/docs/swml/reference/calling/ai-sidecar): Attach a real-time AI observer to a live call. diff --git a/fern/snippets/llms-hint.mdx b/fern/snippets/llms-hint.mdx index bb262df30c..8c3e5ad1c3 100644 --- a/fern/snippets/llms-hint.mdx +++ b/fern/snippets/llms-hint.mdx @@ -1,3 +1,3 @@ - + diff --git a/package.json b/package.json index 8027a94083..c9612a0685 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "check-links": "node scripts/check-links.js", "check-md-exports": "node scripts/check-md-exports.js", "export-rag-corpus": "node scripts/export-rag-corpus.js", + "check-llm-exports": "node scripts/check-llm-exports.mjs", "test:scripts": "node --test \"scripts/**/*.test.js\"", "format:specs": "yarn workspace signalwire-docs-specs format", "format:specs:check": "yarn workspace signalwire-docs-specs format:check", diff --git a/scripts/README.md b/scripts/README.md index cd447a4617..4af9d585d8 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -11,6 +11,8 @@ dependencies beyond what's in `package.json`) and share conventions: hand-rolled | `check-md-exports.js` | `yarn check-md-exports` | Audit the `.md` exports and `llms.txt` indexes served for AI consumption. | | `check-md-exports.test.js` | `yarn test:scripts` | Fixture tests locking in `check-md-exports.js`'s detection heuristics. | | `export-rag-corpus.js` | `yarn export-rag-corpus` | Download the raw Fern Markdown response for every page in `sitemap.xml`. | +| `check-llm-exports.mjs` | `yarn check-llm-exports ` | Check issue-specific required and forbidden content in preview `.md` exports. | +| `llm-export-cases.json` | Used by `check-llm-exports.mjs` | Manifest of routes and semantic export assertions. | | `llm-agent-scenarios.md` | Manual preview evaluation | Context-isolated agent prompts and expected documentation destinations. | | `postman/` | `yarn postman:build` / `postman:publish` | Build and publish the Postman collection from the OpenAPI specs. | | `utils/logger.js` | — | Shared leveled logger (text/JSON, collectors) used by the scripts above. | diff --git a/scripts/check-llm-exports.mjs b/scripts/check-llm-exports.mjs new file mode 100644 index 0000000000..a25c37099e --- /dev/null +++ b/scripts/check-llm-exports.mjs @@ -0,0 +1,110 @@ +#!/usr/bin/env node + +import { readFile } from 'node:fs/promises'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url)); +const DEFAULT_MANIFEST = join(SCRIPT_DIR, 'llm-export-cases.json'); +const MAX_ATTEMPTS = 5; + +function normalizeDocsBase(rawBaseUrl) { + const url = new URL(rawBaseUrl); + url.hash = ''; + url.search = ''; + url.pathname = url.pathname.replace(/\/+$/, ''); + if (!url.pathname.endsWith('/docs')) url.pathname = `${url.pathname || ''}/docs`.replace(/\/{2,}/g, '/'); + return url.toString().replace(/\/$/, ''); +} + +function checkBody(testCase, body) { + const failures = []; + + if (/(?:^|\n)# Page Not Found(?:\n|$)/.test(body)) { + failures.push('received a Page Not Found stub'); + } + + if (body.includes('X-Amz-Signature')) { + failures.push('contains a presigned image URL'); + } + + for (const required of testCase.contains ?? []) { + if (!body.includes(required)) failures.push(`missing required text: ${JSON.stringify(required)}`); + } + + for (const forbidden of testCase.notContains ?? []) { + if (body.includes(forbidden)) failures.push(`contains forbidden text: ${JSON.stringify(forbidden)}`); + } + + return failures; +} + +async function fetchWithRetry(url) { + let lastError; + + for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) { + try { + const response = await fetch(url, { + headers: { 'User-Agent': 'signalwire-docs-llm-export-check' }, + signal: AbortSignal.timeout(30_000), + }); + + if (response.ok) return response.text(); + lastError = new Error(`HTTP ${response.status}`); + if (response.status < 500 && response.status !== 429) break; + } catch (error) { + lastError = error; + } + + if (attempt < MAX_ATTEMPTS) { + await new Promise((resolve) => setTimeout(resolve, 2 ** attempt * 1_000)); + } + } + + throw lastError; +} + +async function main() { + const [rawBaseUrl, manifestPath = DEFAULT_MANIFEST] = process.argv.slice(2); + if (!rawBaseUrl) { + console.error('Usage: node scripts/check-llm-exports.mjs [manifest]'); + process.exit(2); + } + + const baseUrl = normalizeDocsBase(rawBaseUrl); + const manifest = JSON.parse(await readFile(manifestPath, 'utf8')); + const testCases = manifest.flatMap((testCase) => { + const paths = testCase.paths ?? [testCase.path]; + return paths.map((path) => ({ ...testCase, path, paths: undefined })); + }); + const failures = []; + + await Promise.all(testCases.map(async (testCase) => { + const url = `${baseUrl}${testCase.path}`; + try { + const body = await fetchWithRetry(url); + for (const failure of checkBody(testCase, body)) { + failures.push(`${testCase.name} (${url}): ${failure}`); + } + } catch (error) { + failures.push(`${testCase.name} (${url}): ${error.message}`); + } + })); + + if (failures.length > 0) { + console.error(`LLM export checks failed (${failures.length}):`); + for (const failure of failures) console.error(`- ${failure}`); + process.exit(1); + } + + console.log(`LLM export checks passed for ${testCases.length} routes.`); +} + +export { checkBody, normalizeDocsBase }; + +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main().catch((error) => { + console.error(error.stack ?? error.message); + process.exit(2); + }); +} diff --git a/scripts/check-llm-exports.test.js b/scripts/check-llm-exports.test.js new file mode 100644 index 0000000000..f4d2e4269c --- /dev/null +++ b/scripts/check-llm-exports.test.js @@ -0,0 +1,35 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; + +import { checkBody, normalizeDocsBase } from './check-llm-exports.mjs'; + +test('normalizes Fern preview URLs to the docs base', () => { + assert.equal(normalizeDocsBase('https://example.docs.buildwithfern.com'), 'https://example.docs.buildwithfern.com/docs'); + assert.equal(normalizeDocsBase('https://example.docs.buildwithfern.com/docs/'), 'https://example.docs.buildwithfern.com/docs'); +}); + +test('reports missing and forbidden export content', () => { + const failures = checkBody({ + contains: ['orientation', 'functional link'], + notContains: ['class="fern-page-heading"'], + }, 'orientation\nclass="fern-page-heading"'); + + assert.deepEqual(failures, [ + 'missing required text: "functional link"', + 'contains forbidden text: "class=\\"fern-page-heading\\""', + ]); +}); + +test('reports Fern soft-404 bodies even for notContains-only cases', () => { + const failures = checkBody({ + notContains: [' Documentation index\n\n# Page Not Found\n'); + + assert.deepEqual(failures, ['received a Page Not Found stub']); +}); + +test('reports presigned image URLs', () => { + const failures = checkBody({}, 'https://example.com/image.webp?X-Amz-Signature=temporary'); + + assert.deepEqual(failures, ['contains a presigned image URL']); +}); diff --git a/scripts/check-md-exports.js b/scripts/check-md-exports.js index cc09d1d2d4..c6846bab6f 100644 --- a/scripts/check-md-exports.js +++ b/scripts/check-md-exports.js @@ -205,6 +205,14 @@ const TERM_RE = /^\*\*`[^`\n]+`\*\*/m; const PARAMS_HEADING_RE = /^#{2,4}\s+\**(Properties|Parameters|Attributes|Fields|Variables|Returns)\b/im; const HR_RE = /^\s*---\s*$/m; +/** Remove a Fern page directive blockquote when it appears before the H1. */ +function stripLeadingPageDirective(body) { + return body.replace( + /^(?:[ \t]*\n)*(?:[ \t]*>[^\n]*(?:\n|$))+(?:[ \t]*\n)*(?=[ \t]*#\s)/, + '', + ); +} + /** * Each check receives { body, stripped } and returns a message string (finding) * or null (clean). Severity tiers: error affects the exit code; warn is surfaced; @@ -215,7 +223,7 @@ const CHECKS = [ id: 'soft-404', severity: 'error', test({ body }) { - return body.trimStart().startsWith('# Page Not Found') + return stripLeadingPageDirective(body).trimStart().startsWith('# Page Not Found') ? 'listed in llms.txt but the .md export is a "Page Not Found" stub' : null; }, @@ -224,9 +232,9 @@ const CHECKS = [ id: 'empty-body', severity: 'error', test({ body }) { - const content = body + const content = stripLeadingPageDirective(body) .split('\n') - .filter((l) => !/^>\s*For a complete index/.test(l) && !/^#\s/.test(l) && l.trim() !== '') + .filter((l) => !/^#\s/.test(l) && l.trim() !== '') .join('\n'); return content.length < 80 ? `body is nearly empty (${content.length} chars of content)` : null; }, diff --git a/scripts/check-md-exports.test.js b/scripts/check-md-exports.test.js index eb25af8d40..ef8fa41e48 100644 --- a/scripts/check-md-exports.test.js +++ b/scripts/check-md-exports.test.js @@ -197,12 +197,17 @@ test('a "Page Not Found" stub is a soft-404 error', () => { assert.deepEqual(checkIds(body, { severity: 'error' }), ['soft-404']); }); +test('a page directive does not hide a "Page Not Found" stub', () => { + const body = `> Fetch clean Markdown from this deliberately long page directive that may change independently of the checker.\n\n# Page Not Found\n`; + assert.deepEqual(checkIds(body, { severity: 'error' }), ['soft-404']); +}); + test('HTTP 404 on a listed page is a hard-404 error', () => { assert.deepEqual(checkIds('irrelevant', { status: 404 }), ['hard-404']); }); test('a nearly empty body is an error', () => { - const body = '> For a complete index of all SignalWire documentation pages, fetch x\n\n# title\n\nshort.\n'; + const body = '> Fetch clean Markdown from this deliberately long page directive that may change independently of the checker.\n\n# title\n\nshort.\n'; assert.deepEqual(checkIds(body, { severity: 'error' }), ['empty-body']); }); diff --git a/scripts/llm-export-cases.json b/scripts/llm-export-cases.json new file mode 100644 index 0000000000..4455cfafec --- /dev/null +++ b/scripts/llm-export-cases.json @@ -0,0 +1,382 @@ +[ + { + "name": "Documentation homepage", + "path": ".md", + "contains": [ + "Build voice, video, messaging, and AI applications with SignalWire developer tools and implementation guides.", + "SignalWire is a programmable communications platform", + "## Navigate the documentation", + "## Choose a path", + "[WireStarter](/docs/platform/wirestarter)", + "## Popular features", + "[Platform getting-started guide](/docs/platform/getting-started)", + "Twilio and TwiML are trademarks" + ], + "notContains": [ + "class=\"fern-page-heading", + "PSTN · SIP · WebRTC\"]", + "Your backend — the only network hop off platform" + ], + "notContains": [ + "ai-agent-flow-themed.svg" + ] + }, + { + "name": "Prompt engineering surfaces", + "path": "/platform/ai/prompt-engineering.md", + "contains": [ + "## Prompt surfaces", + "| Prompt surface", + "A prompt requests behavior; code enforces it." + ], + "notContains": [ + "" + ], + "notContains": [ + "/assets/images/sdks/diagrams/" + ] + } +] From 093f7381e112261aa4e9173137a4cdcdc6618934 Mon Sep 17 00:00:00 2001 From: Devon-White Date: Fri, 4 Sep 2026 15:42:10 -0400 Subject: [PATCH 09/13] docs: clarify generated LLM product indexes --- fern/docs.yml | 5 +++-- fern/products/home/pages/welcome.mdx | 4 ++-- fern/snippets/llms-hint.mdx | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/fern/docs.yml b/fern/docs.yml index 608f9c93b5..10771d4cb5 100644 --- a/fern/docs.yml +++ b/fern/docs.yml @@ -27,8 +27,9 @@ agents: page-directive: >- Fetch clean Markdown by appending `.md` to any page URL under https://signalwire.com/docs or requesting it with the HTTP header - `Accept: text/markdown`. Start at https://signalwire.com/docs/llms.txt. - Append `/llms.txt` to a product or section URL for its index. + `Accept: text/markdown`. Start at https://signalwire.com/docs/llms.txt, + which lists the product indexes. Append `/llms.txt` to a section URL for a + narrower index. analytics: gtm: diff --git a/fern/products/home/pages/welcome.mdx b/fern/products/home/pages/welcome.mdx index a0b9855d0a..5ef6c2a447 100644 --- a/fern/products/home/pages/welcome.mdx +++ b/fern/products/home/pages/welcome.mdx @@ -126,9 +126,9 @@ Start with the navigation and platform sections, then choose the path that match ## Navigate the documentation -- Treat the [root `llms.txt`](https://signalwire.com/docs/llms.txt) as an orientation page and directory, not an exhaustive list of documentation pages. Product indexes appear under `## Products` at the bottom of the file. +- Treat the [root `llms.txt`](https://signalwire.com/docs/llms.txt) as an orientation page and directory, not an exhaustive list of documentation pages. Fern automatically lists the product indexes under `## Products` at the bottom of the file. - Fetch a documentation page as clean Markdown by appending `.md` to its URL or requesting it with the HTTP header `Accept: text/markdown`. -- Open a product index to discover its page links. To inspect a product or section index directly, append `/llms.txt` to its documentation URL; nested indexes are not necessarily linked from their parent index. +- Open one of those product indexes to discover its page links. For a narrower section index, append `/llms.txt` to the section URL. Some section indexes are linked from their product index; others are available only at the constructed URL. - If a page is not listed in the root index, check its product index before concluding that the documentation does not contain it. ## Choose a path diff --git a/fern/snippets/llms-hint.mdx b/fern/snippets/llms-hint.mdx index 8c3e5ad1c3..e8280af45c 100644 --- a/fern/snippets/llms-hint.mdx +++ b/fern/snippets/llms-hint.mdx @@ -1,3 +1,3 @@ - + From 092950cbda4066bd12aeddf365f82b7f59565b3e Mon Sep 17 00:00:00 2001 From: Devon-White Date: Fri, 4 Sep 2026 15:47:13 -0400 Subject: [PATCH 10/13] docs: make LLM directive page-neutral --- fern/docs.yml | 6 +++--- fern/snippets/llms-hint.mdx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fern/docs.yml b/fern/docs.yml index 10771d4cb5..e1333c8662 100644 --- a/fern/docs.yml +++ b/fern/docs.yml @@ -27,9 +27,9 @@ agents: page-directive: >- Fetch clean Markdown by appending `.md` to any page URL under https://signalwire.com/docs or requesting it with the HTTP header - `Accept: text/markdown`. Start at https://signalwire.com/docs/llms.txt, - which lists the product indexes. Append `/llms.txt` to a section URL for a - narrower index. + `Accept: text/markdown`. The root index at + https://signalwire.com/docs/llms.txt lists the product indexes. Append + `/llms.txt` to a section URL for a narrower index. analytics: gtm: diff --git a/fern/snippets/llms-hint.mdx b/fern/snippets/llms-hint.mdx index e8280af45c..54e1ca0b1c 100644 --- a/fern/snippets/llms-hint.mdx +++ b/fern/snippets/llms-hint.mdx @@ -1,3 +1,3 @@ - + From f802202cffa8bbc038c0cc1b402b9f9ff7d42753 Mon Sep 17 00:00:00 2001 From: Devon-White Date: Fri, 4 Sep 2026 15:49:01 -0400 Subject: [PATCH 11/13] docs: simplify LLM index guidance --- fern/docs.yml | 4 ++-- fern/products/home/pages/welcome.mdx | 6 +++--- fern/snippets/llms-hint.mdx | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fern/docs.yml b/fern/docs.yml index e1333c8662..76b47e5bd9 100644 --- a/fern/docs.yml +++ b/fern/docs.yml @@ -28,8 +28,8 @@ agents: Fetch clean Markdown by appending `.md` to any page URL under https://signalwire.com/docs or requesting it with the HTTP header `Accept: text/markdown`. The root index at - https://signalwire.com/docs/llms.txt lists the product indexes. Append - `/llms.txt` to a section URL for a narrower index. + https://signalwire.com/docs/llms.txt lists the available documentation + indexes. analytics: gtm: diff --git a/fern/products/home/pages/welcome.mdx b/fern/products/home/pages/welcome.mdx index 5ef6c2a447..53d96aad37 100644 --- a/fern/products/home/pages/welcome.mdx +++ b/fern/products/home/pages/welcome.mdx @@ -126,10 +126,10 @@ Start with the navigation and platform sections, then choose the path that match ## Navigate the documentation -- Treat the [root `llms.txt`](https://signalwire.com/docs/llms.txt) as an orientation page and directory, not an exhaustive list of documentation pages. Fern automatically lists the product indexes under `## Products` at the bottom of the file. +- Treat the [root `llms.txt`](https://signalwire.com/docs/llms.txt) as an orientation page and directory, not an exhaustive list of documentation pages. Fern automatically lists the available documentation indexes under `## Products` at the bottom of the file. - Fetch a documentation page as clean Markdown by appending `.md` to its URL or requesting it with the HTTP header `Accept: text/markdown`. -- Open one of those product indexes to discover its page links. For a narrower section index, append `/llms.txt` to the section URL. Some section indexes are linked from their product index; others are available only at the constructed URL. -- If a page is not listed in the root index, check its product index before concluding that the documentation does not contain it. +- Open the relevant documentation index to discover its page links. +- If a page is not listed in the root index, check the relevant documentation index before concluding that the documentation does not contain it. ## Choose a path diff --git a/fern/snippets/llms-hint.mdx b/fern/snippets/llms-hint.mdx index 54e1ca0b1c..721a995052 100644 --- a/fern/snippets/llms-hint.mdx +++ b/fern/snippets/llms-hint.mdx @@ -1,3 +1,3 @@ - + From fdfe0a87be11edc50f40dcb2fdf5b78ab6663a45 Mon Sep 17 00:00:00 2001 From: Devon-White Date: Fri, 4 Sep 2026 16:12:28 -0400 Subject: [PATCH 12/13] docs: remove internal platform detail from LLM guidance --- fern/products/home/pages/welcome.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fern/products/home/pages/welcome.mdx b/fern/products/home/pages/welcome.mdx index 53d96aad37..15acf0f46e 100644 --- a/fern/products/home/pages/welcome.mdx +++ b/fern/products/home/pages/welcome.mdx @@ -126,7 +126,7 @@ Start with the navigation and platform sections, then choose the path that match ## Navigate the documentation -- Treat the [root `llms.txt`](https://signalwire.com/docs/llms.txt) as an orientation page and directory, not an exhaustive list of documentation pages. Fern automatically lists the available documentation indexes under `## Products` at the bottom of the file. +- Treat the [root `llms.txt`](https://signalwire.com/docs/llms.txt) as an orientation page and directory, not an exhaustive list of documentation pages. The available documentation indexes appear under `## Products` at the bottom of the file. - Fetch a documentation page as clean Markdown by appending `.md` to its URL or requesting it with the HTTP header `Accept: text/markdown`. - Open the relevant documentation index to discover its page links. - If a page is not listed in the root index, check the relevant documentation index before concluding that the documentation does not contain it. From 99de3e191a543118fc8b5aaeb2d61d0bd327a7d4 Mon Sep 17 00:00:00 2001 From: Devon <86693904+Devon-White@users.noreply.github.com> Date: Tue, 8 Sep 2026 10:45:03 -0400 Subject: [PATCH 13/13] Update fern/products/home/pages/welcome.mdx Co-authored-by: August Lindgren-Ruby <112662403+hey-august@users.noreply.github.com> --- fern/products/home/pages/welcome.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fern/products/home/pages/welcome.mdx b/fern/products/home/pages/welcome.mdx index 15acf0f46e..f4d128bb28 100644 --- a/fern/products/home/pages/welcome.mdx +++ b/fern/products/home/pages/welcome.mdx @@ -133,7 +133,7 @@ Start with the navigation and platform sections, then choose the path that match ## Choose a path -Each heading below describes a setup task or a way an application can handle communication. Links go to a landing or getting-started page; choose the option that matches your architecture. +Each heading below describes a setup task or a way an application can handle communication. ### Learn the platform and get started