The Zang PubSub service enables developers to use Zang without exposing their network to outside traffic. This is especially important in a corporate environment.

The idea is that HTTP connections are supposed to be short-lived and that most corporate firewalls allow HTTP out on well know ports (80, 443). HTTP connections are also one-way outbound.

Traditional Zang API Webhook approach

Incoming Call webhook


The webhook approach has the benefit of being efficient. When a call is answered or an SMS is received, the user's webserver can be notified without any added delay. The downside is the user must open the HTTP ports up to the world for Zang to reach the webserver that is receiving the webhooks.

Long-Poll Approach


The user server continuously asks the Zang pubsub server if there are any new events. If there are, they will be returned as a list. If there aren't the connection will be kept alive for a whie by not returning anything until it either times out or new events are passed. Since both systems are going outbound, there are no concerns of blocking incoming traffic.


Get Session Token:

$ curl -u <AccountSid>:<AuthToken>

Before subscribing to topics, you'll need to generate a session token. You'll pass this as either the Authorization header or the session GET parameter. The output is a raw text string.

URL: /auth Method: GET, POST


Name Description Examples
ttl How long the session is valid for 10m, 2h

HTTP Response Codes

Code Meaning
401 Check Account Sid and Auth Token


Subscribe to a topic:

$ curl -v ''
*   Trying ::1...
* Connected to localhost (::1) port 443 (#0)
> GET /AC948550ad81934a1a9bcd33b689c0c236/a/b?session=5ff34c5d-5048-468b-b6c2-384d88efbb4d&timeout=5m HTTP/1.1
> Host:
> User-Agent: curl/7.43.0
> Accept: */*
< HTTP/1.1 200 OK
< Access-Control-Allow-Origin: *
< Cache-Control: no-cache
< Connection: keep-alive
< Content-Type: application/json
< Date: Tue, 06 Dec 2016 16:52:13 GMT
< Transfer-Encoding: chunked
    "Hostname": "",
    "AccountSid": "",
    "Url": "/<AccountSid>/a/b",
    "FormParams": {
      "Custom": "123",
      "Param": "abc"
    "Created": "2016-12-06T11:52:13.114436515-05:00"

URL: /<AccountSid>/<QueueName>/ Method: GET

Any valid path may be used as the QueueName. Examples are: /Calls/ or /Call/Dial/Action or /node-1/critical/. The subscriber will only see messages sent to this topic. Up to 10 messages can be received. The response is a JSON list.

If the client is capable of streaming chunked transfers, it will benefit from reduced latency and be able to parse JSON messages as they are streamed.

The messages are generally FIFO but order is not guaranteed. Messages are generally deduplicated, but the application should be idempotent in the event a message is re-delivered.


Send the session token in the Authorization header.


URL: /<AccountSid>/<QueueName>/ Method: POST

The queue name needs to match the name of the queue that the subscriber is listening on. The same rules apply.

If there are no subscribers available immediately, the message is put into an unbound queue which will retain messages for up to 7 days.

SMS Example

Step 1:

Set the URL on your Zang Phone Number

Set Url

Step 2:

Generate Session Token

 $ curl -u AC948550ad81934a1a9bcd33b689c0c236:$ZANG_AUTH_TOKEN

Step 3:

Subscribe to the Topic

$ until curl '' | grep Sid; 
    echo "No messages yet.";

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:15 --:--:--     0

No messages yet.

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   411    0   411    0     0     86      0 --:--:--  0:00:04 --:--:--    86

  "Hostname": "",
  "AccountSid": "AC948550ad81934a1a9bcd33b689c0c236",
  "Url": "/AC948550ad81934a1a9bcd33b689c0c236/SMS",
  "FormParams": {
    "AccountSid": "AC948550ad81934a1a9bcd33b689c0c236",
    "ApiVersion": "v2",
    "Body": "Hello from my cell phone",
    "From": "+18457026652",
    "SmsSid": "SM8f889084f5aa1b237844487f86ff699d",
    "SmsStatus": "received",
    "To": "+14083511240"
  "Created": "2016-12-06T13:26:19.042258199-05:00"

The Message sent from Zang to PubSub:

User-Agent: zang/2.0.1
Content-Length: 172
Accept: application/xml,text/html,application/xhtml+xml,text/plain
Accept: application/xml
Agent: zang/3.0.0
Content-Type: application/x-www-form-urlencoded
X-Zang-Attempt: 5
X-Zang-Failover: false
X-Zang-Signature: Z2DCRjSFNnVSUsVaAeuZEAzYeII=
Accept-Encoding: gzip
X-Forwarded-Proto: https


Call XML Example: Hello World

When the Publish URL sees the extension .xml and there is a CallSid parameter POSTed to it, the server will interpret it as a call control request. This means it will not respond right away, but instead it will publish the event on the queue and then long-poll on the queue named /<AccountSid>/call.xml/<CallSid>.

While the Call XML URL is being long-polled, another process can then POST XML to the Call's queue while will be sent to Zang.

If the long-poll times out, the long poller will return XML such as the following to keep the call alive:


Hello World Call Control

Set a phone number's voice URL to point to$ZANG_ACCOUNT_SID/call.xml in your dashboard. Then run the following and call it.

# Answer URL =$ZANG_ACCOUNT_SID/call.xml
req=`curl "$ZANG_ACCOUNT_SID/call.xml?session=$session&timeout=10m"`
call_sid=`echo "$req" | grep -oh "CA\w*" | head -n 1`
curl "$ZANG_ACCOUNT_SID/call.xml/$call_sid" \
  -H "Content-Type: application/xml" \
  -d "<Response>
    <Say>Unit test successful</Say>

Call XML Example: Gather

Set a phone number to pint to$ZANG_ACCOUNT_SID/call.xml in your dashboard. Then run the following and call it.

#!/usr/bin/env sh

echo "Getting the auth token..."
session=`curl -s -u $ZANG_ACCOUNT_SID:$ZANG_AUTH_TOKEN`

echo "Waiting for the call to be answered..."
req=`curl -s "$ZANG_ACCOUNT_SID/call.xml?session=$session&timeout=10m"`
call_sid=`echo "$req" | grep -oh "CA\w*" | head -n 1`
echo "Call Call Answered: $call_sid"
echo "================"

echo "Executing Gather XML on the call..."
cat <<EOF | curl -s "$ZANG_ACCOUNT_SID/call.xml/$call_sid" -H "Content-Type: application/xml" -d @-
  <Gather action="$ZANG_ACCOUNT_SID/calls/events/NodeId-11/$call_sid.xml?event-type=gather-action&amp;callId=callID&amp;routeId=routeId&amp;mediaRequestID=requestId" timeout="10" finishOnKey="#" numDigits="2">
    <Say loop="2" voice="woman">hello hello I am here</Say>
  <Say>No digits detected</Say>
  <Hangup />
echo "================"

echo "Waiting for digits to be pressed..."
req=`curl -s "$ZANG_ACCOUNT_SID/calls/events/NodeId-11/$call_sid.xml?session=$session&timeout=30s"`
digits=`echo "$req" | grep -oh "\"Digits\":\"\w*" | cut -d '"' -f 4`
echo "Got Digits: $digits"
echo "================"

echo "Executing Thank You XML on the call..."
cat <<EOF | curl -s "$ZANG_ACCOUNT_SID/calls/events/NodeId-11/$call_sid.xml/$call_sid" -H "Content-Type: application/xml" -d @-
  <Say>You pressed $digits. Good bye.</Say>
  <Hangup />
echo "================"