Xen Two-Way Voice

● Live · verified end-to-end
Dictate into the iOS Shortcut → Xen processes → Xen's reply comes back in the response and your phone speaks it. Fully hands-free.

How it works

The /api/dictate-inject endpoint used to be one-way — it took your words in and returned {ok:true,injected:true} with no reply. Now it holds the HTTP response open until Xen's next reply fires, then returns it:

{ "ok": true, "injected": true, "reply": "<Xen's answer>", "ts": 1781952233207 }

Triggered when the body has "wait": true (or source contains "shortcut"). wait_ms sets the hold timeout — default 90s, max 110s. On timeout it returns reply:"" so the shortcut never hangs. The Xen: identity prefix is stripped so it speaks naturally. VVSVEI & SMS paths are unchanged — zero regression.

Build the Shortcut — "Send to Xen 2"

  1. Dictate Text
    Language: English (US)
    Stop Listening: After Pause — this is the fix. "On Tap" was capturing empty audio.
    Also confirm Settings → Privacy & Security → Microphone → Shortcuts is ON.
  2. Get Contents of URL
    URL: https://tui.xlrd.org/api/dictate-inject
    Method: POST · Header Content-Type = application/json
    Request Body: JSONsource (Text) = shortcut text (Text) = Dictated Text wait (Boolean) = true wait_ms (Number) = 90000
  3. Get Dictionary Value
    Get Value for reply in Contents of URL
  4. Speak Text
    Text: Dictionary Value (the reply) · Wait Until Finished: on

Tap the shortcut → speak → it POSTs your words → Xen replies → your phone speaks the answer.

Test from any terminal

curl -sS -X POST https://tui.xlrd.org/api/dictate-inject \
  -H "Content-Type: application/json" \
  -d '{"source":"shortcut","text":"what time is it","wait":true,"wait_ms":90000}'
# → {"ok":true,"injected":true,"reply":"...Xen's spoken answer...","ts":...}

Edge cases

First reply wins. The held request returns Xen's next reply after your inject — in practice the answer to what you said. If Xen is mid-narration on other work, that line could return instead (acceptable v1; tightenable with a correlation id later).

Timeout. If Xen takes >90s, the shortcut gets reply:"" and stays silent rather than hanging. Raise wait_ms to 110000 max for longer tasks.

Both legs fire. Xen's reply still plays on the Mac speakers AND returns to your phone. Mute the Mac for phone-only — the webhook leg is independent.