SWML messaging — integration#317
Open
Devon-White wants to merge 32 commits into
Open
Conversation
Contributor
The README's prior single-line description of the spec workspace was accurate but thin — readers couldn't tell that `specs/` actually houses three sibling TypeSpec projects with different downstream artifacts. Adds a table covering each project (signalwire-rest, compatibility-api, SWML), what it compiles to (OpenAPI 3.1 for the REST projects, JSON Schema for SWML), and which surface that artifact powers. Calls out `specs/_shared/` as the home of repo-wide scalars and the custom `@webhook(...)` decorator, and pins down the don't-hand-edit rule for generated artifacts in `fern/apis/` and `specs/swml/**/tsp-output/`. This commit seeds the SWML messaging integration branch so the integration → main PR can be opened. The two child PRs (Devon/swml-schema, Devon/swml-docs) target this integration branch and merge into it independently.
1724fd5 to
7bdbf82
Compare
…saging
Splits the SWML TypeSpec schema into two sibling namespaces:
* `SWML.Calling` — the existing schema, relocated under `specs/swml/calling/`.
Every method TSP file moves from `specs/swml/Methods/<method>/` to
`specs/swml/calling/Methods/<method>/` (139 renames). Behavior unchanged.
* `SWML.Messaging` — new namespace under `specs/swml/messaging/` covering
inbound SMS/MMS handlers. Methods: execute, goto, label, receive, reply,
request, return, switch, transfer. `SWMLObject` is a `@oneOf` union of a
full document (top-level `sections`) and a light document (single `reply`
or `receive`, including the empty `{}` shorthand).
`specs/package.json` replaces the single `build:swml` script with
`build:swml-calling` and `build:swml-messaging` (both wired into
`build:schema`). The legacy `specs/swml/tsp-output/.../SWMLObject.json` file
is kept in place — consumers may have hardcoded that URL path; the new
calling-only schema lives at `specs/swml/calling/tsp-output/...`.
REST consumers that embed SWML documents are updated to use the new
namespaced types:
* `signalwire-rest/calling-api/calls/models/{requests,examples}.tsp` —
`SWML.Calling.SWMLObject` for call-control SWML payloads.
* `signalwire-rest/fabric-api/swml-scripts/models/{core,requests}.tsp` —
splits the `contents` field across `SWML.Calling.SWMLObject` and
`SWML.Messaging.SWMLObject` based on script type.
* `signalwire-rest/fabric-api/ai-agent/models/{core,ai/main}.tsp` — pulls
AI method types from `SWML.Calling`.
Adds the second SWML inbound webhook to the SWML Webhooks namespace:
`@webhook("inboundMessageWebhook", InboundMessageWebhookPayload, ...)` with
companion `InboundMessageMediaItem`, `InboundMessageContext`, and
`InboundMessageWebhookPayload` models in
`signalwire-rest/fabric-api/swml-webhook/models/webhooks.tsp`. The Calling
sibling `@webhook("inboundCallWebhook", ...)` already shipped with the
decorator PR; this PR completes the pair. `fern/products/apis/apis.yml`
gains the matching nav entry.
Drive-along refactors that landed naturally with this work:
* `uuid` and `jwt` scalars moved from
`specs/signalwire-rest/types/scalar-types/main.tsp` up to
`specs/_shared/types/main.tsp` so compatibility-api can reach them.
`@format("uuid") + string` pairs across ~135 REST + compat spec files
collapse to the bare `uuid` scalar (TypeSpec carries the format).
* `UrlMethodType` (which actually only describes request URLs) renames to
`RequestUrlMethodType`; `cxml-webhooks/models/requests.tsp` propagates
the new name.
* `signalwire-rest/fabric-api/_shared/const.tsp` adds tuple-literal
`SWML_CONTENTS_EXAMPLE` and `SWML_MESSAGING_CONTENTS_EXAMPLE` constants
for use in `@example(...)` decorators.
* `signalwire-rest/message-api/messages/models/webhooks.tsp` clarifies in
prose that the message status callback shape is also fired by SWML
messaging `reply.status_url`.
Targets the SWML messaging integration branch (`Devon/swml-messaging`).
The matching SWML reference MDX docs land as a sibling PR.
Verified by `yarn build:all` from `specs/`: TypeSpec 1.11.0 compiles
signalwire-rest, compatibility-api, swml-calling, and swml-messaging
without errors. Generated `fern/apis/signalwire-rest/openapi.yaml`
exposes both `inboundCallWebhook` and `inboundMessageWebhook` under
`webhooks:`.
Splits the SWML reference docs tree into two top-level sections, mirroring the upcoming TypeSpec namespace split (SWML.Calling, SWML.Messaging): * `pages/reference/methods/*` renamed under `methods/calling/*` (54 files; light prose edits where method docs already referenced "calling-only" behavior — those are tightened up). * `pages/reference/methods/messaging/*.mdx` (10 new pages): overview, execute, goto, label, receive, reply, request, return, switch, transfer. * `pages/reference/errors.mdx` (new — consolidated SWML error reference). * `pages/reference/variables.mdx` (updated to clarify which variables apply to calling vs messaging contexts). Updates `swml.yml` nav to expose Calling and Messaging as sibling folders under Methods, and adds an Errors entry under the reference tab. Adds 30+ redirects in `fern/docs.yml` so existing `/docs/swml/reference/...` URLs continue to resolve. The folder-shape methods (ai, amazon-bedrock) get wildcard redirects covering every nested child page; top-level method slugs get one redirect each (a blanket `:slug` wildcard would incorrectly catch `/messaging/*` too). Targets the SWML messaging integration branch (`Devon/swml-messaging`). The matching TypeSpec schema work lands as a sibling PR.
InboundMessageWebhookPayload - Add optional `vars` field carrying propagated runtime variables on transfer-driven fetches; absent on the initial inbound fetch - `body` typed as nullable (provider intakes leave it null on media-only MMS with no carrier-supplied text) - Loosen from/to descriptions to forward-compatible wording - Tighten timestamp/media descriptions Reply method - ReplyInlineSwitch.case and default accept `string | ReplyPlan` so each branch can carry its own body/media/to/from/status_url - Require `body` be non-empty (@minlength(1)) on both ReplyWithBody and ReplyWithMedia, with matching doc text - status_url cross-links to the message status callback payload Request method - Rewrite description with the four request_result values, the soft vs hard failure semantics, and the response variables (request_response, request_response_code, request_response_body) Transfer method - Description now references the inbound message webhook payload and explains what message/params/vars carry on the transfer fetch SwmlScriptCreate/UpdateRequest - script_type optional with per-branch default; structural oneOf still discriminates via the contents schema. Update docs to call out the recommended-but-optional path.
Move the two receive-shape types out of main.tsp into receive/main.tsp so all receive-related definitions live together. SWMLLightDocument stays in main.tsp since it's a document-level union; it still composes LightReceive via the Methods import. Generated JSON Schema is unchanged.
Response side — both fields are emitted as parsed JSON objects by Rails: - CallFlowSerializer / CallFlowVersionSerializer call RelayBins::ParseJsonOrYaml.call(raw_contents).as_json for relayml - flow_data is a jsonb column that the serializer passes through directly Spec previously typed both as `string`, which would reject the real responses. Updated CallFlow (core), CallFlowVersion, CallFlowVersionDeployResponse, and the dead-but-present CallFlowVersionResponse: - relayml: SWML.Calling.SWMLObject - flow_data: opaque object (Record<unknown>) Request side — CallFlowCreateRequest and CallFlowUpdateRequest were missing the relayml and flow_data fields entirely. Rails contracts require both (with create allowing both to be omitted together to get SignalWire defaults; update requires both present along with document_version). Added the missing fields with required/optional flags matching the Rails contracts. Generated REST OpenAPI regenerated.
- reply.mdx: inline switch case/default values are now string OR full reply object (body/media/to/from/status_url). Updated the ParamField descriptions and the trailing constraint paragraph, and added a per-case-routing example. - transfer.mdx: payload section now lists the three keys (message/params/vars) and embeds the inboundMessageWebhook payload snippet. Removed the inaccurate 'same shape as the initial fetch' note. - overview.mdx: tighten the reply summary (inline switch branches the reply, not just the body); replace the over-simplified 'request failures are soft' blurb with the precise hard-vs-soft split, surfacing the four request_result values.
The Fern URL slug for the message-logs operation is built from its OpenAPI tag "Message Logs" -> kebab "message-logs", not the parent "Logs" nav section (which uses `skip-slug: true`). Updated both occurrences to point at /docs/apis/rest/message-logs/list-message-logs. The inbound-message-webhook links in variables.mdx are left alone; that target page is generated by the schema PR (#315), and the links will resolve once both PRs land on the Devon/swml-messaging integration branch.
- Add a short paragraph up top introducing the two flavors and noting they share document structure / variables / execution semantics. - Reframe the "Methods" section text to call out that calling and messaging each have their own method set, with direct links to each reference. - Document-fetching webhook section now opens with a calling-vs-messaging payload split (two bullets linking to the inbound call/message webhook pages), retitles the example as "Calling request body", and notes that the `join_conference.wait_url` / `enter_queue.wait_url` / `connect.confirm` re-fetches are calling-only. - Replace the generic "Methods reference" card with two cards — Calling reference and Messaging reference — preserving the Quickstart and AI guides cards alongside.
The TypeSpec @doc strings emit to JSON Schema descriptions and ultimately into customer-facing surfaces (REST API reference, SDK docs). They should say the same thing as the MDX reference docs and stay focused on purpose and behavior — type/required/default info already lives in the type signature and ParamField attributes. Changes per method (eight files, ~17 description updates): * execute — clarify that completion happens via `return` or end-of-section, and that URLs/inline documents are not accepted in the messaging context (matches the doc intro that already calls this out). * goto — promote `goto.max` description to "section ends without running further steps" (matches Rails behavior in section_executor.rb; the prior "stopping execution" was vague). Add a goto-scoped @doc on the `label` field instead of inheriting Label.label's @doc which describes labels from the label's perspective, not from goto's. * label — note that label names must be unique within the section. * reply — append "reply does not end execution; subsequent steps continue" to the method @doc, swap the Ruby-resource framing on `from` ("PhoneRoute or ShortCode") for user-facing language ("owned by your project and have messaging capability"), and document the inline-switch no-match failure behavior on `default`. * request — drop redundant "Default X" prose from `method`, `timeout`, and `save_variables` @docs (already declared via `= default` on the type), swap "Hashes" → "Objects" on `request.body` for non-Ruby-flavored docs, add the publicly-reachable URL constraint to `request.url`, and add the "map of header name to value" clarification to `request.headers`. * return — note the `return: null` shorthand for returning without a value. * switch — extend the method @doc with use-case framing ("useful for keyword-driven inbound message handling"), document key/value semantics on `case`, and document the no-match failure behavior on `default`. * transfer — note the URL-only constraint in the method @doc, document embedded basic-auth syntax on `dest`, drop redundant "Default `POST`" prose on `method`, and append the runtime semantic on `params` ("available as `params.*` in the transferred document"). Regenerated specs/swml/messaging/tsp-output/.../SWMLObject.json is the mechanical byproduct of these source edits. Verified by `yarn build:swml-messaging` — compiles clean under TypeSpec 1.11.0 with no warnings or errors.
…into Devon/swml-schema
SWML messaging reference docs (MDX)
SWML messaging schema (TypeSpec)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Integration PR. Aggregates two child PRs into a single atomic ship to main.
Child PRs (both target this integration branch)
When both children merge in, this PR's diff becomes the union (schema + docs + this README seed). Approve and squash-merge to ship the schema + docs to prod simultaneously.
Why an integration branch?
Schema and docs are reviewable independently (zero file overlap) but must ship together — publishing the messaging schema without docs (or vice versa) leaves the feature half-shipped. The integration branch lets each child PR get its own focused review while preserving atomic delivery to main.
Current diff vs main
Only the README update that seeded this branch — a clearer breakdown of the three TypeSpec projects under
specs/and what each one emits. Real scope lands when the child PRs merge.After both children merge
cd specs && yarn build:allto refresh generated outputs.