Actions in your systems
Run actions on your server
Texa sends each action request to your server, signed, over a webhook or a socket your server opens. Your credentials never leave you.
When you'd rather keep your credentials and logic on your side, Texa sends each action request to your server and you answer with the result. There are two ways to receive them.
| Signed webhook | Socket | |
|---|---|---|
| Texa connects to | your https URL |
nothing: your server connects to Texa |
| Needs an inbound port | yes | no |
| Good for | a public API server | a server behind a firewall, a laptop while you build |
Both are set up in Texa Desk under Texa AI → Actions → Connectors → Your own system.
Signed webhook
Texa POSTs each request, signed with the connector's secret:
POST https://your-server/texa/actions
X-Texa-Timestamp: 1790000000
X-Texa-Signature: sha256=<hex HMAC-SHA256 of "1790000000.<raw body>">
X-Texa-Request-Id: act_3f9c...
{"request_id": "act_3f9c...", "action": "get_transfer_status",
"inputs": {"reference": "TX-9B2LC"}, "idempotency_key": "idem_act_3f9c...",
"expires_at": "2026-10-19T10:05:00Z", "conversation_id": "cnv_...", "environment": "live",
"customer": {"ref": "cw:12:345", "name": "Ngozi Eze", "phone": "+2348031234567", "email": null},
"reply_url": "https://hooks.texadesk.com/actions/results/..."}
Check the signature, then either:
- answer 200 with
{"status": "succeeded", "data": {...}},{"status": "not_found"}or{"status": "failed", "error": "..."}within the action's timeout, or - answer 202, and later POST the same body to
reply_url, signed the same way.
customer says who is asking as Texa Desk knows them (the phone a WhatsApp message came from), not
that they proved it. Texa AI checks identity first for actions marked for verified customers only.
A request from your website or app chat also says "signed_in": true when your server signed the
customer in.
Socket
Your server opens one WebSocket to Texa and keeps it open; requests come down it.
wss://hooks.texadesk.com/actions/connect
Authorization: Bearer <the connector's socket key, shown once>
| Direction | Message |
|---|---|
| Texa → you | {"type": "ready"} once connected |
| Texa → you | {"type": "action_request", "reply_token", "request_id", "action", "inputs", "idempotency_key", "expires_at", "conversation_id"} |
| you → Texa | {"type": "action_result", "reply_token", "status", "data", "error"} |
| you → Texa | {"type": "ping"}, answered {"type": "pong"} |
# pip install websockets
import asyncio, json, os, websockets
async def main():
async with websockets.connect("wss://hooks.texadesk.com/actions/connect",
additional_headers={"Authorization": f"Bearer {os.environ['TEXA_SOCKET_KEY']}"}) as ws:
async for raw in ws:
msg = json.loads(raw)
if msg["type"] != "action_request":
continue
data = await run(msg["action"], msg["inputs"], msg["idempotency_key"])
await ws.send(json.dumps({"type": "action_result", "reply_token": msg["reply_token"],
"status": "succeeded", "data": data}))
asyncio.run(main())
Reconnect when the socket drops (with a pause that grows). A request with no socket connected, or no
answer before expires_at, fails, and Texa AI tells the customer honestly that it couldn't check.
Or run it from the API's answer
If your own server is already the one calling the Texa API, you don't need either: action
requests come back in the answer's events, and you answer with
POST /v1/conversations/{id}/action-results. See actions.