vobiz-python 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vobiz_python-0.1.0/LICENSE.txt +19 -0
- vobiz_python-0.1.0/MANIFEST.in +2 -0
- vobiz_python-0.1.0/PKG-INFO +640 -0
- vobiz_python-0.1.0/README.md +594 -0
- vobiz_python-0.1.0/pyproject.toml +3 -0
- vobiz_python-0.1.0/setup.cfg +10 -0
- vobiz_python-0.1.0/setup.py +45 -0
- vobiz_python-0.1.0/vobiz/__init__.py +4 -0
- vobiz_python-0.1.0/vobiz/base.py +237 -0
- vobiz_python-0.1.0/vobiz/exceptions.py +34 -0
- vobiz_python-0.1.0/vobiz/resources/__init__.py +12 -0
- vobiz_python-0.1.0/vobiz/resources/accounts.py +59 -0
- vobiz_python-0.1.0/vobiz/resources/applications.py +138 -0
- vobiz_python-0.1.0/vobiz/resources/calls_vobiz.py +206 -0
- vobiz_python-0.1.0/vobiz/resources/cdrs.py +46 -0
- vobiz_python-0.1.0/vobiz/resources/credentials.py +104 -0
- vobiz_python-0.1.0/vobiz/resources/endpoints.py +101 -0
- vobiz_python-0.1.0/vobiz/resources/ip_access_control_lists.py +100 -0
- vobiz_python-0.1.0/vobiz/resources/numbers.py +134 -0
- vobiz_python-0.1.0/vobiz/resources/origination_uris.py +109 -0
- vobiz_python-0.1.0/vobiz/resources/recordings.py +91 -0
- vobiz_python-0.1.0/vobiz/resources/sip_trunks.py +99 -0
- vobiz_python-0.1.0/vobiz/resources/subaccounts.py +101 -0
- vobiz_python-0.1.0/vobiz/rest/__init__.py +2 -0
- vobiz_python-0.1.0/vobiz/rest/client.py +277 -0
- vobiz_python-0.1.0/vobiz/utils/__init__.py +72 -0
- vobiz_python-0.1.0/vobiz/utils/interactive.py +50 -0
- vobiz_python-0.1.0/vobiz/utils/jwt.py +97 -0
- vobiz_python-0.1.0/vobiz/utils/location.py +6 -0
- vobiz_python-0.1.0/vobiz/utils/signature_v3.py +111 -0
- vobiz_python-0.1.0/vobiz/utils/template.py +50 -0
- vobiz_python-0.1.0/vobiz/utils/validators.py +280 -0
- vobiz_python-0.1.0/vobiz/version.py +2 -0
- vobiz_python-0.1.0/vobiz/xml/ConferenceElement.py +485 -0
- vobiz_python-0.1.0/vobiz/xml/DTMFElement.py +41 -0
- vobiz_python-0.1.0/vobiz/xml/DialElement.py +371 -0
- vobiz_python-0.1.0/vobiz/xml/MultiPartyCallElement.py +711 -0
- vobiz_python-0.1.0/vobiz/xml/ResponseElement.py +414 -0
- vobiz_python-0.1.0/vobiz/xml/VobizXMLElement.py +48 -0
- vobiz_python-0.1.0/vobiz/xml/__init__.py +31 -0
- vobiz_python-0.1.0/vobiz/xml/breakElement.py +62 -0
- vobiz_python-0.1.0/vobiz/xml/contElement.py +190 -0
- vobiz_python-0.1.0/vobiz/xml/emphasisElement.py +174 -0
- vobiz_python-0.1.0/vobiz/xml/getDigitsElement.py +294 -0
- vobiz_python-0.1.0/vobiz/xml/getInputElement.py +369 -0
- vobiz_python-0.1.0/vobiz/xml/hangupElement.py +57 -0
- vobiz_python-0.1.0/vobiz/xml/langElement.py +197 -0
- vobiz_python-0.1.0/vobiz/xml/messageElement.py +115 -0
- vobiz_python-0.1.0/vobiz/xml/numberElement.py +77 -0
- vobiz_python-0.1.0/vobiz/xml/pElement.py +164 -0
- vobiz_python-0.1.0/vobiz/xml/phonemeElement.py +62 -0
- vobiz_python-0.1.0/vobiz/xml/playElement.py +41 -0
- vobiz_python-0.1.0/vobiz/xml/preAnswerElement.py +148 -0
- vobiz_python-0.1.0/vobiz/xml/prosodyElement.py +227 -0
- vobiz_python-0.1.0/vobiz/xml/recordElement.py +337 -0
- vobiz_python-0.1.0/vobiz/xml/redirectElement.py +42 -0
- vobiz_python-0.1.0/vobiz/xml/sElement.py +154 -0
- vobiz_python-0.1.0/vobiz/xml/sayAsElement.py +61 -0
- vobiz_python-0.1.0/vobiz/xml/speakElement.py +249 -0
- vobiz_python-0.1.0/vobiz/xml/streamElement.py +123 -0
- vobiz_python-0.1.0/vobiz/xml/subElement.py +43 -0
- vobiz_python-0.1.0/vobiz/xml/userElement.py +79 -0
- vobiz_python-0.1.0/vobiz/xml/wElement.py +144 -0
- vobiz_python-0.1.0/vobiz/xml/waitElement.py +93 -0
- vobiz_python-0.1.0/vobiz/xml/xmlUtils.py +6 -0
- vobiz_python-0.1.0/vobiz_python.egg-info/PKG-INFO +640 -0
- vobiz_python-0.1.0/vobiz_python.egg-info/SOURCES.txt +69 -0
- vobiz_python-0.1.0/vobiz_python.egg-info/dependency_links.txt +1 -0
- vobiz_python-0.1.0/vobiz_python.egg-info/requires.txt +5 -0
- vobiz_python-0.1.0/vobiz_python.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (C) 2024, Vobiz Inc.
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
5
|
+
the Software without restriction, including without limitation the rights to
|
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
8
|
+
so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
@@ -0,0 +1,640 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vobiz-python
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Vobiz Python SDK for voice, trunks, phone numbers, endpoints, and XML.
|
|
5
|
+
Home-page: https://github.com/Piyush-sahoo/Vobiz-Python-SDK
|
|
6
|
+
Author: Vobiz
|
|
7
|
+
Author-email: support@vobiz.ai
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: vobiz,vobiz xml,voice calls,sip trunking,telephony
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Telecommunications Industry
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Classifier: Topic :: Communications :: Telephony
|
|
26
|
+
Requires-Python: >=3.7
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE.txt
|
|
29
|
+
Requires-Dist: requests<3,>=2
|
|
30
|
+
Requires-Dist: decorator>=5
|
|
31
|
+
Requires-Dist: lxml>=3
|
|
32
|
+
Requires-Dist: PyJWT
|
|
33
|
+
Requires-Dist: python-dotenv
|
|
34
|
+
Dynamic: author
|
|
35
|
+
Dynamic: author-email
|
|
36
|
+
Dynamic: classifier
|
|
37
|
+
Dynamic: description
|
|
38
|
+
Dynamic: description-content-type
|
|
39
|
+
Dynamic: home-page
|
|
40
|
+
Dynamic: keywords
|
|
41
|
+
Dynamic: license
|
|
42
|
+
Dynamic: license-file
|
|
43
|
+
Dynamic: requires-dist
|
|
44
|
+
Dynamic: requires-python
|
|
45
|
+
Dynamic: summary
|
|
46
|
+
|
|
47
|
+
# Vobiz Python SDK
|
|
48
|
+
|
|
49
|
+
The official Python SDK for the [Vobiz](https://vobiz.ai) voice & telephony platform.
|
|
50
|
+
|
|
51
|
+
Make outbound calls, manage SIP trunks, phone numbers, endpoints, and build dynamic call flows with VobizXML — all from Python.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Table of Contents
|
|
56
|
+
|
|
57
|
+
- [Installation](#installation)
|
|
58
|
+
- [Authentication](#authentication)
|
|
59
|
+
- [Making Your First Call](#making-your-first-call)
|
|
60
|
+
- [Receiving Calls — Answer Server](#receiving-calls--answer-server)
|
|
61
|
+
- [Local Development with ngrok](#local-development-with-ngrok)
|
|
62
|
+
- [Resources](#resources)
|
|
63
|
+
- [Account](#account)
|
|
64
|
+
- [Calls](#calls)
|
|
65
|
+
- [Applications](#applications)
|
|
66
|
+
- [Phone Numbers](#phone-numbers)
|
|
67
|
+
- [SIP Endpoints](#sip-endpoints)
|
|
68
|
+
- [SIP Trunks](#sip-trunks)
|
|
69
|
+
- [Credentials](#credentials)
|
|
70
|
+
- [IP Access Control Lists](#ip-access-control-lists)
|
|
71
|
+
- [Origination URIs](#origination-uris)
|
|
72
|
+
- [Recordings](#recordings)
|
|
73
|
+
- [CDRs](#cdrs)
|
|
74
|
+
- [Subaccounts](#subaccounts)
|
|
75
|
+
- [VobizXML — Call Flow Control](#vobizxml--call-flow-control)
|
|
76
|
+
- [Environment Variables](#environment-variables)
|
|
77
|
+
- [Running Tests](#running-tests)
|
|
78
|
+
- [License](#license)
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Installation
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pip install vobiz
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Or install from source:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
git clone https://github.com/Piyush-sahoo/Vobiz-Python-SDK.git
|
|
92
|
+
cd Vobiz-Python-SDK
|
|
93
|
+
pip install -e .
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Requires Python 3.7+**
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Authentication
|
|
101
|
+
|
|
102
|
+
Vobiz uses `Auth ID` + `Auth Token` for all REST API calls. Find yours in the [Vobiz Console](https://console.vobiz.ai).
|
|
103
|
+
|
|
104
|
+
**Recommended:** store credentials in a `.env` file (never commit it to git):
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
cp .env.example .env
|
|
108
|
+
# Edit .env and fill in your real values
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
import vobiz
|
|
113
|
+
|
|
114
|
+
# Reads VOBIZ_AUTH_ID and VOBIZ_AUTH_TOKEN from .env automatically
|
|
115
|
+
client = vobiz.RestClient()
|
|
116
|
+
|
|
117
|
+
# Or pass explicitly
|
|
118
|
+
client = vobiz.RestClient(auth_id="MA_XXXXXXXXXX", auth_token="your_token")
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Making Your First Call
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
import vobiz
|
|
127
|
+
|
|
128
|
+
client = vobiz.RestClient()
|
|
129
|
+
|
|
130
|
+
response = client.calls.create(
|
|
131
|
+
from_="+911234567890", # Your Vobiz DID number
|
|
132
|
+
to_="+919876543210", # Destination number
|
|
133
|
+
answer_url="https://your-server.com/answer", # Returns VobizXML
|
|
134
|
+
answer_method="GET",
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
print("Call UUID:", response.request_uuid)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
When the call is answered, Vobiz makes an HTTP request to your `answer_url`. That URL must return **VobizXML** telling Vobiz what to do — speak text, play audio, record, collect digits, etc.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Receiving Calls — Answer Server
|
|
145
|
+
|
|
146
|
+
Create a simple Flask server that returns VobizXML:
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
# answer_server.py
|
|
150
|
+
from flask import Flask, request, Response
|
|
151
|
+
from vobiz import vobizxml
|
|
152
|
+
|
|
153
|
+
app = Flask(__name__)
|
|
154
|
+
|
|
155
|
+
@app.route('/answer', methods=['GET', 'POST'])
|
|
156
|
+
def answer():
|
|
157
|
+
# Vobiz sends call details as query/form params
|
|
158
|
+
call_uuid = request.values.get('CallUUID')
|
|
159
|
+
from_num = request.values.get('From')
|
|
160
|
+
to_num = request.values.get('To')
|
|
161
|
+
|
|
162
|
+
print(f"Incoming call {call_uuid}: {from_num} → {to_num}")
|
|
163
|
+
|
|
164
|
+
# Build XML response using the SDK
|
|
165
|
+
response = vobizxml.ResponseElement()
|
|
166
|
+
response.add_speak(
|
|
167
|
+
"Hello! You have reached our service. Thank you for calling.",
|
|
168
|
+
voice="WOMAN",
|
|
169
|
+
language="en-US",
|
|
170
|
+
)
|
|
171
|
+
response.add_hangup()
|
|
172
|
+
|
|
173
|
+
return Response(response.to_string(), status=200, mimetype='application/xml')
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@app.route('/hangup', methods=['GET', 'POST'])
|
|
177
|
+
def hangup():
|
|
178
|
+
print("Call ended:", request.values.get('CallUUID'))
|
|
179
|
+
print("Duration:", request.values.get('Duration'), "seconds")
|
|
180
|
+
return Response('OK', status=200)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
if __name__ == '__main__':
|
|
184
|
+
# NOTE: macOS users — port 5000 is reserved by AirPlay Receiver.
|
|
185
|
+
# Use port 5001 or higher.
|
|
186
|
+
app.run(port=5001)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Run it:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
python answer_server.py
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Local Development with ngrok
|
|
198
|
+
|
|
199
|
+
Your answer server must be reachable over the internet. Use [ngrok](https://ngrok.com) to expose your local server:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
# Install ngrok: https://ngrok.com/download
|
|
203
|
+
ngrok http 5001
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
ngrok will print a public HTTPS URL like:
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
Forwarding https://abc123.ngrok-free.app -> http://localhost:5001
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Copy that URL and set it in your `.env`:
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
ANSWER_URL=https://abc123.ngrok-free.app/answer
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Now trigger the call using `client.calls.create(...)` with the ngrok URL as your `answer_url`.
|
|
219
|
+
|
|
220
|
+
**Full flow:**
|
|
221
|
+
1. Your code fires an outbound call via `client.calls.create(...)`
|
|
222
|
+
2. The called phone rings
|
|
223
|
+
3. When answered, Vobiz calls your `ANSWER_URL` (via ngrok → your Flask server)
|
|
224
|
+
4. Your server returns VobizXML
|
|
225
|
+
5. Vobiz executes the XML — speaks text, plays audio, etc.
|
|
226
|
+
6. Call ends; Vobiz calls your `hangup_url` with final call details
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Resources
|
|
231
|
+
|
|
232
|
+
### Account
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
# Get your account details
|
|
236
|
+
account = client.accounts.get()
|
|
237
|
+
print(account.auth_id, account.name)
|
|
238
|
+
|
|
239
|
+
# Get balance (currency: "INR", "USD", etc.)
|
|
240
|
+
balance = client.accounts.get_balance(auth_id="MA_XXXXXXXXXX", currency="INR")
|
|
241
|
+
print(f"Balance: {balance.available_balance} {balance.currency}")
|
|
242
|
+
|
|
243
|
+
# Get transaction history
|
|
244
|
+
txns = client.accounts.get_transactions(auth_id="MA_XXXXXXXXXX", limit=20, offset=0)
|
|
245
|
+
|
|
246
|
+
# Get current concurrent call usage
|
|
247
|
+
concurrency = client.accounts.get_concurrency(auth_id="MA_XXXXXXXXXX")
|
|
248
|
+
print(f"{concurrency.concurrent_calls} / {concurrency.max_concurrent} concurrent calls")
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
### Calls
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
# Make an outbound call
|
|
257
|
+
resp = client.calls.create(
|
|
258
|
+
from_="+911234567890",
|
|
259
|
+
to_="+919876543210",
|
|
260
|
+
answer_url="https://your-server.com/answer",
|
|
261
|
+
answer_method="GET",
|
|
262
|
+
hangup_url="https://your-server.com/hangup",
|
|
263
|
+
hangup_method="GET",
|
|
264
|
+
)
|
|
265
|
+
call_uuid = resp.request_uuid
|
|
266
|
+
|
|
267
|
+
# List currently live calls
|
|
268
|
+
live = client.calls.list_live()
|
|
269
|
+
|
|
270
|
+
# List queued calls
|
|
271
|
+
queued = client.calls.list_queued()
|
|
272
|
+
|
|
273
|
+
# Transfer an active call to a new XML URL
|
|
274
|
+
client.calls.transfer(
|
|
275
|
+
call_uuid,
|
|
276
|
+
legs="aleg",
|
|
277
|
+
aleg_url="https://your-server.com/transfer",
|
|
278
|
+
aleg_method="GET",
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
# Send DTMF digits to an active call
|
|
282
|
+
client.calls.send_digits(call_uuid, digits="1", leg="aleg")
|
|
283
|
+
|
|
284
|
+
# Hang up a call
|
|
285
|
+
client.calls.hangup(call_uuid)
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
### Applications
|
|
291
|
+
|
|
292
|
+
Applications define the default XML URL for inbound calls to a number.
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
# Create an application
|
|
296
|
+
app = client.applications.create(
|
|
297
|
+
name="Customer Support",
|
|
298
|
+
answer_url="https://your-server.com/answer",
|
|
299
|
+
hangup_url="https://your-server.com/hangup",
|
|
300
|
+
application_type="XML", # "XML" or "Siptrunk"
|
|
301
|
+
)
|
|
302
|
+
app_id = app.app_id
|
|
303
|
+
|
|
304
|
+
# List all applications
|
|
305
|
+
apps = client.applications.list()
|
|
306
|
+
|
|
307
|
+
# Get a specific application
|
|
308
|
+
app = client.applications.get(app_id)
|
|
309
|
+
|
|
310
|
+
# Update an application
|
|
311
|
+
client.applications.update(app_id, name="Support Line", answer_url="https://new-url.com/answer")
|
|
312
|
+
|
|
313
|
+
# Delete an application
|
|
314
|
+
client.applications.delete(app_id)
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
### Phone Numbers
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
# Browse available numbers in inventory
|
|
323
|
+
inventory = client.phone_numbers.list_inventory(country="IN", page=1, per_page=20)
|
|
324
|
+
for num in inventory.items:
|
|
325
|
+
print(num['e164'], num['monthly_rate'])
|
|
326
|
+
|
|
327
|
+
# Purchase a number
|
|
328
|
+
client.phone_numbers.purchase_from_inventory(e164="+911234567890", currency="INR")
|
|
329
|
+
|
|
330
|
+
# Release a number back to inventory
|
|
331
|
+
client.phone_numbers.release(e164_number="+911234567890")
|
|
332
|
+
|
|
333
|
+
# Assign a number to a SIP trunk
|
|
334
|
+
client.phone_numbers.assign_to_trunk(e164_number="+911234567890", trunk_group_id="trunk-uuid")
|
|
335
|
+
|
|
336
|
+
# Unassign a number from its trunk
|
|
337
|
+
client.phone_numbers.unassign_from_trunk(e164_number="+911234567890")
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
### SIP Endpoints
|
|
343
|
+
|
|
344
|
+
SIP endpoints are devices or softphones that register with Vobiz via SIP.
|
|
345
|
+
|
|
346
|
+
```python
|
|
347
|
+
# Create an endpoint
|
|
348
|
+
ep = client.endpoints.create(
|
|
349
|
+
username="agent_jane",
|
|
350
|
+
password="SecurePass123!",
|
|
351
|
+
alias="Jane - Support Desk",
|
|
352
|
+
)
|
|
353
|
+
endpoint_id = ep.endpoint_id
|
|
354
|
+
print(f"SIP URI: sip:{ep.username}@sip.vobiz.ai")
|
|
355
|
+
|
|
356
|
+
# List all endpoints
|
|
357
|
+
endpoints = client.endpoints.list()
|
|
358
|
+
|
|
359
|
+
# Get a specific endpoint (includes registration status)
|
|
360
|
+
ep = client.endpoints.get(endpoint_id)
|
|
361
|
+
print("Registered:", ep.sip_registered)
|
|
362
|
+
|
|
363
|
+
# Update an endpoint
|
|
364
|
+
client.endpoints.update(endpoint_id, alias="Jane - Sales")
|
|
365
|
+
|
|
366
|
+
# Delete an endpoint
|
|
367
|
+
client.endpoints.delete(endpoint_id)
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
**Connect your SIP client:**
|
|
371
|
+
|
|
372
|
+
| Setting | Value |
|
|
373
|
+
|---------|-------|
|
|
374
|
+
| SIP Server | `sip.vobiz.ai` |
|
|
375
|
+
| Port | `5060` (UDP/TCP) / `5061` (TLS) |
|
|
376
|
+
| Username | your endpoint username |
|
|
377
|
+
| Password | your endpoint password |
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
### SIP Trunks
|
|
382
|
+
|
|
383
|
+
```python
|
|
384
|
+
# List all SIP trunks
|
|
385
|
+
trunks = client.sip_trunks.list()
|
|
386
|
+
for trunk in trunks.objects:
|
|
387
|
+
print(trunk.id, trunk.name)
|
|
388
|
+
|
|
389
|
+
# Get a specific trunk
|
|
390
|
+
trunk = client.sip_trunks.get("trunk-uuid")
|
|
391
|
+
print(trunk.cps_limit, trunk.concurrent_calls_limit)
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
### Credentials
|
|
397
|
+
|
|
398
|
+
SIP digest credentials for outbound trunk authentication.
|
|
399
|
+
|
|
400
|
+
```python
|
|
401
|
+
# List all credentials
|
|
402
|
+
creds = client.credentials.list()
|
|
403
|
+
for cred in creds.objects:
|
|
404
|
+
print(cred.username)
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
### IP Access Control Lists
|
|
410
|
+
|
|
411
|
+
Whitelist IP addresses for inbound SIP traffic.
|
|
412
|
+
|
|
413
|
+
```python
|
|
414
|
+
# Create an IP ACL entry
|
|
415
|
+
acl = client.ip_access_control_lists.create(
|
|
416
|
+
ip_address="203.0.113.10",
|
|
417
|
+
description="Office static IP",
|
|
418
|
+
)
|
|
419
|
+
acl_id = acl.id
|
|
420
|
+
|
|
421
|
+
# List all ACL entries
|
|
422
|
+
acls = client.ip_access_control_lists.list()
|
|
423
|
+
|
|
424
|
+
# Delete an ACL entry
|
|
425
|
+
client.ip_access_control_lists.delete(acl_id)
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
### Origination URIs
|
|
431
|
+
|
|
432
|
+
Define where inbound SIP calls are sent.
|
|
433
|
+
|
|
434
|
+
```python
|
|
435
|
+
# List all origination URIs
|
|
436
|
+
uris = client.origination_uris.list()
|
|
437
|
+
|
|
438
|
+
# List origination URIs for a specific trunk
|
|
439
|
+
uris = client.origination_uris.list(trunk_id="trunk-uuid")
|
|
440
|
+
for uri in uris.objects:
|
|
441
|
+
print(uri.uri, uri.priority)
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
---
|
|
445
|
+
|
|
446
|
+
### Recordings
|
|
447
|
+
|
|
448
|
+
```python
|
|
449
|
+
# List recordings (paginated)
|
|
450
|
+
recordings = client.recordings.list(limit=20, offset=0)
|
|
451
|
+
for rec in recordings.objects:
|
|
452
|
+
print(rec.recording_id, rec.recording_url)
|
|
453
|
+
|
|
454
|
+
# Filter by recording type
|
|
455
|
+
trunk_recs = client.recordings.list(recording_type="trunk")
|
|
456
|
+
|
|
457
|
+
# Get a specific recording
|
|
458
|
+
rec = client.recordings.get("recording-uuid")
|
|
459
|
+
print("Duration:", rec.recording_duration_ms)
|
|
460
|
+
print("URL:", rec.recording_url)
|
|
461
|
+
|
|
462
|
+
# Delete a recording
|
|
463
|
+
client.recordings.delete("recording-uuid")
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
---
|
|
467
|
+
|
|
468
|
+
### CDRs (Call Detail Records)
|
|
469
|
+
|
|
470
|
+
```python
|
|
471
|
+
# List CDRs (most recent first)
|
|
472
|
+
cdrs = client.cdrs.list()
|
|
473
|
+
|
|
474
|
+
# Filter by date range and paginate
|
|
475
|
+
cdrs = client.cdrs.list(
|
|
476
|
+
start_date="2026-01-01",
|
|
477
|
+
end_date="2026-01-31",
|
|
478
|
+
page=1,
|
|
479
|
+
per_page=20,
|
|
480
|
+
)
|
|
481
|
+
for cdr in cdrs.data:
|
|
482
|
+
print(cdr['call_id'], cdr['duration'], cdr['status'])
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
### Subaccounts
|
|
488
|
+
|
|
489
|
+
Isolate resources per customer, department, or environment.
|
|
490
|
+
|
|
491
|
+
```python
|
|
492
|
+
# Create a subaccount
|
|
493
|
+
result = client.subaccounts.create(
|
|
494
|
+
name="Support Team",
|
|
495
|
+
email="support@example.com",
|
|
496
|
+
rate_limit=500,
|
|
497
|
+
permissions={"calls": True, "cdr": True},
|
|
498
|
+
password="SecurePass123!",
|
|
499
|
+
)
|
|
500
|
+
sub_id = result.sub_account.id
|
|
501
|
+
sub_auth_id = result.auth_credentials.auth_id
|
|
502
|
+
sub_auth_token = result.auth_credentials.auth_token
|
|
503
|
+
# ⚠️ Save the auth_token — it is only returned once at creation
|
|
504
|
+
|
|
505
|
+
# List all subaccounts
|
|
506
|
+
subs = client.subaccounts.list(page=1, size=25)
|
|
507
|
+
|
|
508
|
+
# Get a specific subaccount
|
|
509
|
+
sub = client.subaccounts.get(sub_id)
|
|
510
|
+
|
|
511
|
+
# Update a subaccount
|
|
512
|
+
client.subaccounts.update(sub_id, description="Updated team", rate_limit=750)
|
|
513
|
+
|
|
514
|
+
# Delete a subaccount
|
|
515
|
+
client.subaccounts.delete(sub_id)
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
---
|
|
519
|
+
|
|
520
|
+
## VobizXML — Call Flow Control
|
|
521
|
+
|
|
522
|
+
VobizXML is returned from your answer URL to tell Vobiz what to do with a call.
|
|
523
|
+
|
|
524
|
+
```python
|
|
525
|
+
from vobiz import vobizxml
|
|
526
|
+
|
|
527
|
+
response = vobizxml.ResponseElement()
|
|
528
|
+
|
|
529
|
+
# Speak text (TTS)
|
|
530
|
+
response.add_speak("Welcome to Acme Corp.", voice="WOMAN", language="en-US")
|
|
531
|
+
|
|
532
|
+
# Play an audio file
|
|
533
|
+
response.add_play("https://your-server.com/audio/hold-music.mp3")
|
|
534
|
+
|
|
535
|
+
# Collect DTMF digits (IVR menu)
|
|
536
|
+
get_digits = response.add_get_digits(
|
|
537
|
+
action="https://your-server.com/menu",
|
|
538
|
+
method="GET",
|
|
539
|
+
num_digits=1,
|
|
540
|
+
timeout=5,
|
|
541
|
+
)
|
|
542
|
+
get_digits.add_speak("Press 1 for sales. Press 2 for support.")
|
|
543
|
+
|
|
544
|
+
# Record the call
|
|
545
|
+
response.add_record(
|
|
546
|
+
action="https://your-server.com/recording",
|
|
547
|
+
max_length=300,
|
|
548
|
+
play_beep=True,
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
# Dial / transfer to another number
|
|
552
|
+
dial = response.add_dial(timeout=30, caller_id="+911234567890")
|
|
553
|
+
dial.add_number("+919876543210")
|
|
554
|
+
|
|
555
|
+
# Wait / hold
|
|
556
|
+
response.add_wait(length=5)
|
|
557
|
+
|
|
558
|
+
# Redirect to another URL
|
|
559
|
+
response.add_redirect("https://your-server.com/next-step")
|
|
560
|
+
|
|
561
|
+
# Hang up
|
|
562
|
+
response.add_hangup()
|
|
563
|
+
|
|
564
|
+
# Render to string
|
|
565
|
+
xml_string = response.to_string(pretty=True)
|
|
566
|
+
print(xml_string)
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
**Example output:**
|
|
570
|
+
|
|
571
|
+
```xml
|
|
572
|
+
<Response>
|
|
573
|
+
<Speak voice="WOMAN" language="en-US">Welcome to Acme Corp.</Speak>
|
|
574
|
+
<Hangup/>
|
|
575
|
+
</Response>
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
## Environment Variables
|
|
581
|
+
|
|
582
|
+
Copy `.env.example` to `.env` and fill in your values:
|
|
583
|
+
|
|
584
|
+
```bash
|
|
585
|
+
cp .env.example .env
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
| Variable | Required | Description |
|
|
589
|
+
|----------|----------|-------------|
|
|
590
|
+
| `VOBIZ_AUTH_ID` | **Yes** | Your account Auth ID (e.g. `MA_XXXXXXXXXX`) |
|
|
591
|
+
| `VOBIZ_AUTH_TOKEN` | **Yes** | Your account Auth Token |
|
|
592
|
+
| `FROM_PHONE_NUMBER` | For call demos | Your Vobiz DID in E.164 format (e.g. `+911234567890`) |
|
|
593
|
+
| `TO_PHONE_NUMBER` | For call demos | Destination number in E.164 format |
|
|
594
|
+
| `ANSWER_URL` | For call demos | Public HTTPS URL for the answer webhook |
|
|
595
|
+
|
|
596
|
+
> **Security:** `.env` is in `.gitignore`. Never commit real credentials to git. Use `.env.example` as the template for your team.
|
|
597
|
+
|
|
598
|
+
---
|
|
599
|
+
|
|
600
|
+
## Running Tests
|
|
601
|
+
|
|
602
|
+
**End-to-end live API tests** (requires valid credentials in `.env`):
|
|
603
|
+
|
|
604
|
+
```bash
|
|
605
|
+
python testing.py
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
Expected output: `40 PASS, 1 SKIP` across 11 sections:
|
|
609
|
+
1. Account (get, balance, transactions, concurrency)
|
|
610
|
+
2. Calls (list live, list queued)
|
|
611
|
+
3. Applications (create, list, get, update, delete)
|
|
612
|
+
4. Phone Numbers (list inventory)
|
|
613
|
+
5. SIP Endpoints (create, list, get, update, delete)
|
|
614
|
+
6. SIP Trunks (list, get)
|
|
615
|
+
7. Credentials (list)
|
|
616
|
+
8. IP ACLs (create, list, delete)
|
|
617
|
+
9. Origination URIs (list)
|
|
618
|
+
10. Recordings (list)
|
|
619
|
+
11. CDRs (list)
|
|
620
|
+
|
|
621
|
+
**Unit tests:**
|
|
622
|
+
|
|
623
|
+
```bash
|
|
624
|
+
pip install pytest
|
|
625
|
+
pytest -q
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
---
|
|
629
|
+
|
|
630
|
+
## License
|
|
631
|
+
|
|
632
|
+
MIT — see [LICENSE.txt](LICENSE.txt)
|
|
633
|
+
|
|
634
|
+
---
|
|
635
|
+
|
|
636
|
+
## Support
|
|
637
|
+
|
|
638
|
+
- Documentation: [https://vobiz.ai/docs](https://vobiz.ai)
|
|
639
|
+
- Console: [https://console.vobiz.ai](https://console.vobiz.ai)
|
|
640
|
+
- Email: support@vobiz.ai
|