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.
Files changed (70) hide show
  1. vobiz_python-0.1.0/LICENSE.txt +19 -0
  2. vobiz_python-0.1.0/MANIFEST.in +2 -0
  3. vobiz_python-0.1.0/PKG-INFO +640 -0
  4. vobiz_python-0.1.0/README.md +594 -0
  5. vobiz_python-0.1.0/pyproject.toml +3 -0
  6. vobiz_python-0.1.0/setup.cfg +10 -0
  7. vobiz_python-0.1.0/setup.py +45 -0
  8. vobiz_python-0.1.0/vobiz/__init__.py +4 -0
  9. vobiz_python-0.1.0/vobiz/base.py +237 -0
  10. vobiz_python-0.1.0/vobiz/exceptions.py +34 -0
  11. vobiz_python-0.1.0/vobiz/resources/__init__.py +12 -0
  12. vobiz_python-0.1.0/vobiz/resources/accounts.py +59 -0
  13. vobiz_python-0.1.0/vobiz/resources/applications.py +138 -0
  14. vobiz_python-0.1.0/vobiz/resources/calls_vobiz.py +206 -0
  15. vobiz_python-0.1.0/vobiz/resources/cdrs.py +46 -0
  16. vobiz_python-0.1.0/vobiz/resources/credentials.py +104 -0
  17. vobiz_python-0.1.0/vobiz/resources/endpoints.py +101 -0
  18. vobiz_python-0.1.0/vobiz/resources/ip_access_control_lists.py +100 -0
  19. vobiz_python-0.1.0/vobiz/resources/numbers.py +134 -0
  20. vobiz_python-0.1.0/vobiz/resources/origination_uris.py +109 -0
  21. vobiz_python-0.1.0/vobiz/resources/recordings.py +91 -0
  22. vobiz_python-0.1.0/vobiz/resources/sip_trunks.py +99 -0
  23. vobiz_python-0.1.0/vobiz/resources/subaccounts.py +101 -0
  24. vobiz_python-0.1.0/vobiz/rest/__init__.py +2 -0
  25. vobiz_python-0.1.0/vobiz/rest/client.py +277 -0
  26. vobiz_python-0.1.0/vobiz/utils/__init__.py +72 -0
  27. vobiz_python-0.1.0/vobiz/utils/interactive.py +50 -0
  28. vobiz_python-0.1.0/vobiz/utils/jwt.py +97 -0
  29. vobiz_python-0.1.0/vobiz/utils/location.py +6 -0
  30. vobiz_python-0.1.0/vobiz/utils/signature_v3.py +111 -0
  31. vobiz_python-0.1.0/vobiz/utils/template.py +50 -0
  32. vobiz_python-0.1.0/vobiz/utils/validators.py +280 -0
  33. vobiz_python-0.1.0/vobiz/version.py +2 -0
  34. vobiz_python-0.1.0/vobiz/xml/ConferenceElement.py +485 -0
  35. vobiz_python-0.1.0/vobiz/xml/DTMFElement.py +41 -0
  36. vobiz_python-0.1.0/vobiz/xml/DialElement.py +371 -0
  37. vobiz_python-0.1.0/vobiz/xml/MultiPartyCallElement.py +711 -0
  38. vobiz_python-0.1.0/vobiz/xml/ResponseElement.py +414 -0
  39. vobiz_python-0.1.0/vobiz/xml/VobizXMLElement.py +48 -0
  40. vobiz_python-0.1.0/vobiz/xml/__init__.py +31 -0
  41. vobiz_python-0.1.0/vobiz/xml/breakElement.py +62 -0
  42. vobiz_python-0.1.0/vobiz/xml/contElement.py +190 -0
  43. vobiz_python-0.1.0/vobiz/xml/emphasisElement.py +174 -0
  44. vobiz_python-0.1.0/vobiz/xml/getDigitsElement.py +294 -0
  45. vobiz_python-0.1.0/vobiz/xml/getInputElement.py +369 -0
  46. vobiz_python-0.1.0/vobiz/xml/hangupElement.py +57 -0
  47. vobiz_python-0.1.0/vobiz/xml/langElement.py +197 -0
  48. vobiz_python-0.1.0/vobiz/xml/messageElement.py +115 -0
  49. vobiz_python-0.1.0/vobiz/xml/numberElement.py +77 -0
  50. vobiz_python-0.1.0/vobiz/xml/pElement.py +164 -0
  51. vobiz_python-0.1.0/vobiz/xml/phonemeElement.py +62 -0
  52. vobiz_python-0.1.0/vobiz/xml/playElement.py +41 -0
  53. vobiz_python-0.1.0/vobiz/xml/preAnswerElement.py +148 -0
  54. vobiz_python-0.1.0/vobiz/xml/prosodyElement.py +227 -0
  55. vobiz_python-0.1.0/vobiz/xml/recordElement.py +337 -0
  56. vobiz_python-0.1.0/vobiz/xml/redirectElement.py +42 -0
  57. vobiz_python-0.1.0/vobiz/xml/sElement.py +154 -0
  58. vobiz_python-0.1.0/vobiz/xml/sayAsElement.py +61 -0
  59. vobiz_python-0.1.0/vobiz/xml/speakElement.py +249 -0
  60. vobiz_python-0.1.0/vobiz/xml/streamElement.py +123 -0
  61. vobiz_python-0.1.0/vobiz/xml/subElement.py +43 -0
  62. vobiz_python-0.1.0/vobiz/xml/userElement.py +79 -0
  63. vobiz_python-0.1.0/vobiz/xml/wElement.py +144 -0
  64. vobiz_python-0.1.0/vobiz/xml/waitElement.py +93 -0
  65. vobiz_python-0.1.0/vobiz/xml/xmlUtils.py +6 -0
  66. vobiz_python-0.1.0/vobiz_python.egg-info/PKG-INFO +640 -0
  67. vobiz_python-0.1.0/vobiz_python.egg-info/SOURCES.txt +69 -0
  68. vobiz_python-0.1.0/vobiz_python.egg-info/dependency_links.txt +1 -0
  69. vobiz_python-0.1.0/vobiz_python.egg-info/requires.txt +5 -0
  70. 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,2 @@
1
+ # Include the license file
2
+ include LICENSE.txt
@@ -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