Run your own
Standing up a deployment from nothing: the service, a project, and a paired number. Roughly ten minutes, most of it waiting for a scan.
Most readers want the other page
Start here instead. This page is for operators.1. Run it#
Requires Docker. The engine and Postgres come up together.
git clone https://github.com/atrixdigital/atrix-switchboard cd atrix-switchboard/engine/run export GLOBAL_API_KEY=$(openssl rand -hex 32) docker compose up -d curl https://switchboard.atrix.dev/v1/ready # {"status":"ready"}
Never run docker compose down -v
down/up is safe.2. Create a project#
An operator does this once, with the root key. The webhook secret is shown once and never readable again.
curl -X POST https://switchboard.atrix.dev/v1/projects \ -H "apikey: $GLOBAL_API_KEY" \ -H 'content-type: application/json' \ -d '{"name":"my-project","webhookUrl":"https://my-project.example/api/webhooks/switchboard"}' # -> { project: { id }, webhookSecret } shown ONCE
Then mint it a key — also shown once.
curl -X POST https://switchboard.atrix.dev/v1/projects/<id>/keys \ -H "apikey: $GLOBAL_API_KEY" \ -H 'content-type: application/json' -d '{"label":"production"}' # -> { apiKey }
The webhook URL is checked when you set it
WEBHOOK_ALLOW_PRIVATE; cloud metadata is refused either way. The URL is tenant-supplied and the service POSTs to it unattended, so an unguarded one is an SSRF into the host’s own network.3. Stand up a number#
From here the project uses its own key. No root key is involved again.
curl -X POST https://switchboard.atrix.dev/v1/instances -H "apikey: <projectKey>" \ -H 'content-type: application/json' \ -d '{"name":"sender-01","phone":"+971500000000","owner":"Acme Ltd","device":"Pixel 6a #3"}' curl -X POST https://switchboard.atrix.dev/v1/instances/<id>/connect -H "apikey: <projectKey>" curl https://switchboard.atrix.dev/v1/instances/<id>/qr -H "apikey: <projectKey>" # scan it
Stop polling the QR the moment it reports paired
qr returns one of three outcomes — paired, pending, or a code — precisely so this is branchable rather than an error.Always supply the phone, even though it is optional
4. Install the client#
While the registry is unavailable the SDK ships as a vendored tarball. Copy it in and depend on the path.
"@atrixdigital/switchboard": "file:vendor/atrixdigital-switchboard-0.4.0.tgz"
Do not use a relative path to a sibling checkout. It works on a laptop with both repos present and does not exist inside a Docker build or on a CI runner, which is where installs actually happen.
import { Switchboard } from "@atrixdigital/switchboard" const wa = new Switchboard({ url: process.env.SWITCHBOARD_URL!, projectKey: process.env.SWITCHBOARD_KEY!, }) const { id } = await wa.outbound.send({ to: "+971500000001", message: { text: "hello" }, })