feat(ovhcloud): hold Riposte's on-call ladder in a VoIP call queue (1.1.0)
Riposte's pager can hand its on-call ladder to a marketplace instance, so that a number published to clients rings whoever is on duty without the call ever crossing the SOC's own trunk — and without spending one of its simultaneous calls, which are sold in blocks and are the reason the PBX-side mode is too expensive for a small deployment. Three commands, and they are a contract rather than this integration's own vocabulary: set_oncall_ladder (ordered numbers + ring_seconds), get_oncall_ladder (what the carrier ACTUALLY holds, so a comparison can be honest) and list_calls (the journal, after the fact). The pager offers whichever instances declare the first one; the README now documents the contract for whoever writes the next router. Extends the existing ovhcloud integration rather than adding a second one: it already carries the signed-request authentication, and making an operator enter the same application key twice would serve nobody. Three telephony_* config fields are added; every existing command is untouched, and an instance that leaves them empty is exactly the OVHcloud reader it was before. set_oncall_ladder RECONCILES rather than rebuilds. Agents are created when a number has none, reordered by position, and anybody who drops off the ladder is LOGGED OUT and removed from the queue — never deleted, because deleting throws away whatever else the operator configured on that agent and they are on the rota again next week. It also enforces the sequential strategy: publishing an ordered list into a queue set to ring everybody at once is a lie that is invisible from Riposte. Two details that would each have cost an evening: OVH accepts no "+" in a phoneNumber field, so E.164 is converted to the 00-prefixed form both ways; and a service holding several queues is REFUSED rather than guessed, because guessing publishes the on-call ladder into whichever queue the API happened to list first. Verified against a stubbed API for the first publication and for a rota handover. The signature, the error codes and the exact number format are only proved by the first real call. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TN1R4EnNFUV2Xwtd9q98tj
This commit is contained in:
@@ -202,6 +202,39 @@ named by field, evaluated against the rule's last captured samples.
|
||||
|
||||
---
|
||||
|
||||
## Well-known commands: the on-call router
|
||||
|
||||
Most command ids are an integration's own business. **Three are a contract**, and
|
||||
declaring them is how an integration becomes something Riposte's pager can hand its
|
||||
on-call ladder to — a telephony carrier that will ring the on-call team when a
|
||||
client calls the number you published to them.
|
||||
|
||||
| Command | Inputs | What it must do |
|
||||
|---|---|---|
|
||||
| `set_oncall_ladder` | `numbers` (ordered, E.164), `ring_seconds` | Ring those numbers, **in that order**, one after another. A carrier that can only hold ONE destination takes `numbers[0]` and says so in its description — a single rung that follows the rota still beats a redirect a human has to edit. Must be idempotent: Riposte republishes on a schedule. |
|
||||
| `get_oncall_ladder` | — | Answer `{ numbers: [...] }`: what the carrier **actually holds**, not what was last sent. Optional, and what makes Riposte's comparison honest. |
|
||||
| `list_calls` | `since`, `limit` | Answer `{ calls: [...] }` with `from_number`, `did`, `started_at`, `duration`. Optional; it fills the pager journal after the fact. |
|
||||
|
||||
**The id is the whole contract.** There is no registry, no manifest field to set and
|
||||
no vendor list anywhere in Riposte: the pager's settings screen offers whichever
|
||||
instances declare `set_oncall_ladder`.
|
||||
|
||||
**Declarative, never reactive.** Riposte says *who* to ring; the carrier decides
|
||||
*when*. A contract shaped the other way round — the carrier calling us as each call
|
||||
arrives — is a webhook, and a self-hosted SOAR on a VM with no inbound path from the
|
||||
internet cannot receive one.
|
||||
|
||||
Two things a router should be honest about in its description, because they decide
|
||||
whether an operator should use it at all: whether the carrier can **ask the answerer
|
||||
to press a key** before connecting the caller (most cannot, so a mobile's voicemail
|
||||
ends the search as though a person had answered), and whether ringing a rung costs
|
||||
anything on the operator's own trunk.
|
||||
|
||||
`integrations/ovhcloud` implements all three against a VoIP call queue; read it
|
||||
before writing another.
|
||||
|
||||
---
|
||||
|
||||
## Adding this catalog to Riposte
|
||||
|
||||
In Riposte → **Integrations → Marketplace → Add source**:
|
||||
|
||||
Reference in New Issue
Block a user