An example self-hosted server for Inkbox. A single FastAPI process handles both:
POST /webhook— HTTP webhooks for mail events, incoming texts, and incoming calls. Signatures verified via theinkboxSDK; bodies parsed with Pydantic models; raw payloads written topayloads/<ts>.jsonfor inspection.WebSocket /phone/media/ws— live phone-media sessions once a call is answered. Receives platform events from Inkbox (start,transcript,barge_in,stop) and sends outboundtextframes for Inkbox-managed TTS to play back to the caller.
See src/data_models/webhooks.py and src/data_models/phone_media.py for the exact JSON shapes exchanged on each side — the app uses those same Pydantic models to validate incoming requests, so "what hits my endpoint" lives in one place.
Once the steps below are done, your local server will be able to:
- Receive emails — inbound messages (and status notifications like delivered / bounced) land on
POST /webhookand get persisted topayloads/. - Receive SMS and MMS — inbound text messages (with attachments) hit the same
/webhookendpoint. - Accept phone calls — incoming calls are auto-answered and connected to a sample AI agent (
src/phone_agent.py) that talks to the caller live over the phone-media WebSocket, using Inkbox-managed speech-to-text and text-to-speech.
Before running the server, provision an agent identity in the Inkbox console and collect four credentials. Steps:
- Go to console.inkbox.ai and sign in.
- Open Identities → New identity. Pick a name for your agent (e.g.
demo-agent). - During creation (or from the identity's detail page afterward), attach:
- A mailbox — pick an email address on one of the available domains.
- A phone number — provision a number in your region.
- Save. The identity now owns one mailbox and one phone number; this server's bootstrap will patch both on startup.
On the identity's detail page, open API keys → Create key. Copy the key value (you won't see it again). This is your INKBOX_API_KEY — the bootstrap uses it to list + update the identity's phone numbers and mailboxes.
Go to console.inkbox.ai/webhooks → Create signing key. Copy the whsec_... value. This is your INKBOX_SIGNING_KEY — used by this server to verify X-Inkbox-Signature on every inbound webhook and on the phone-media WebSocket handshake.
Create a free account at dashboard.ngrok.com, then open dashboard.ngrok.com/get-started/your-authtoken and copy the token. This is your NGROK_AUTHTOKEN — the bootstrap uses it to open a public HTTPS tunnel to this local server so Inkbox can reach your webhooks.
Create a key at platform.openai.com/api-keys. This is your OPENAI_API_KEY — the sample phone agent (src/phone_agent.py) uses it to generate live call replies.
cp .env.example .env
chmod 600 .envOpen .env and paste in the four values you just collected:
INKBOX_API_KEY=... # step 2
INKBOX_SIGNING_KEY=whsec_... # step 3
NGROK_AUTHTOKEN=... # step 4
OPENAI_API_KEY=sk-... # step 5
That's it — you're ready to run. On startup the server will open the ngrok tunnel, PATCH every phone number + mailbox on your identity to point at the tunnel, and start serving webhooks + the phone-media WebSocket.
Signature verification is on by default. If you're testing locally without a real signing key, set INKBOX_REQUIRE_SIGNATURE=false — but then Inkbox won't be able to call you (the real platform always signs), so this is only useful for curl-based local testing.
When running in Docker you can skip the .env file and pass the same variables via -e flags; env_config.py reads from os.environ and treats .env as optional.
The easiest way is the helper script, which builds and runs in one step:
./scripts/run_docker.py
# or: ./scripts/run_docker.py --host-port 9000 --image-name my-inkbox-serverOr do it by hand:
docker build -t inkbox-sample-client-server .
docker run --rm -p 8080:8080 \
-e INKBOX_SIGNING_KEY=whsec_... \
-v "$PWD/payloads:/app/payloads" \
inkbox-sample-client-serverThe container runs inkbox-server on port 8080 and writes received payloads to /app/payloads (mount a host directory if you want to read them from outside the container).
Linux / macOS:
curl -LsSf https://astral.sh/uv/install.sh | shWindows (PowerShell):
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"uv syncuv run inkbox-server # webhooks + phone media WS on :8080POST /webhook— Inkbox webhook receiver. VerifiesX-Inkbox-Signatureviainkbox.verify_webhook, parses the body into aMailWebhookPayload,PhoneIncomingTextWebhookPayload, orPhoneIncomingCallWebhookPayload, writes the raw payload topayloads/<ts>.json, and returns200 OK(or anIncomingCallActionResponsebody forincoming_callevents).WebSocket /phone/media/ws— Live phone-media session. Inkbox opens this once a call is answered; we optionally verify the handshake signature, accept withX-Use-Inkbox-{Text-To-Speech,Speech-To-Text}: trueheaders, and respond to finaltranscriptframes with outboundtextreplies.GET /health— liveness check.