├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── something-else.md └── workflows │ ├── build.yml │ ├── mutation-test.yml │ └── release.yml ├── .gitignore ├── .mend ├── .pre-commit-config.yaml ├── .scripts └── update_vonage_versions.py ├── BUILD ├── CODE_OF_CONDUCT.md ├── LICENSE ├── Makefile ├── README.md ├── V3_TO_V4_SDK_MIGRATION_GUIDE.md ├── account ├── BUILD ├── CHANGES.md ├── README.md ├── pyproject.toml ├── src │ └── vonage_account │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── _version.py │ │ ├── account.py │ │ ├── errors.py │ │ ├── requests.py │ │ └── responses.py └── tests │ ├── BUILD │ ├── data │ ├── create_secret_error_max_number.json │ ├── get_balance.json │ ├── get_country_pricing.json │ ├── get_multiple_countries_pricing.json │ ├── list_secrets.json │ ├── revoke_secret_error.json │ ├── secret.json │ ├── top_up.json │ └── update_default_sms_webhook.json │ └── test_account.py ├── application ├── BUILD ├── CHANGES.md ├── README.md ├── pyproject.toml ├── src │ └── vonage_application │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── _version.py │ │ ├── application.py │ │ ├── common.py │ │ ├── enums.py │ │ ├── errors.py │ │ ├── requests.py │ │ └── responses.py └── tests │ ├── BUILD │ ├── data │ ├── create_application_basic.json │ ├── create_application_options.json │ ├── get_application.json │ ├── list_applications_basic.json │ ├── list_applications_multiple_pages.json │ └── update_application.json │ └── test_application.py ├── http_client ├── BUILD ├── CHANGES.md ├── README.md ├── pyproject.toml ├── src │ └── vonage_http_client │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── _version.py │ │ ├── auth.py │ │ ├── errors.py │ │ └── http_client.py └── tests │ ├── BUILD │ ├── data │ ├── 400.json │ ├── 400.txt │ ├── 401.json │ ├── 403.json │ ├── 404.json │ ├── 429.json │ ├── 500.json │ ├── dummy_private_key.txt │ ├── dummy_public_key.txt │ ├── example_get.json │ ├── example_post.json │ └── file_stream.mp3 │ ├── test_auth.py │ └── test_http_client.py ├── jwt ├── BUILD ├── CHANGES.md ├── README.md ├── pyproject.toml ├── src │ └── vonage_jwt │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── _version.py │ │ ├── errors.py │ │ ├── jwt.py │ │ └── verify_jwt.py └── tests │ ├── BUILD │ ├── data │ ├── private_key.txt │ └── public_key.txt │ ├── test_jwt_generator.py │ └── test_verify_jwt.py ├── messages ├── BUILD ├── CHANGES.md ├── README.md ├── pyproject.toml ├── src │ └── vonage_messages │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── _version.py │ │ ├── messages.py │ │ ├── models │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── base_message.py │ │ ├── enums.py │ │ ├── messenger.py │ │ ├── mms.py │ │ ├── rcs.py │ │ ├── sms.py │ │ ├── viber.py │ │ └── whatsapp.py │ │ └── responses.py └── tests │ ├── BUILD │ ├── data │ ├── invalid_error.json │ ├── low_balance_error.json │ ├── not_found.json │ └── send_message.json │ ├── test_messages.py │ ├── test_messenger_models.py │ ├── test_mms_models.py │ ├── test_rcs_models.py │ ├── test_sms_models.py │ ├── test_viber_models.py │ └── test_whatsapp_models.py ├── network_auth ├── BUILD ├── CHANGES.md ├── README.md ├── pyproject.toml ├── src │ └── vonage_network_auth │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── _version.py │ │ ├── network_auth.py │ │ ├── requests.py │ │ └── responses.py └── tests │ ├── BUILD │ ├── data │ ├── oidc_request.json │ ├── oidc_request_permissions_error.json │ └── token_request.json │ └── test_network_auth.py ├── network_number_verification ├── BUILD ├── CHANGES.md ├── README.md ├── pyproject.toml ├── src │ └── vonage_network_number_verification │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── _version.py │ │ ├── errors.py │ │ ├── number_verification.py │ │ ├── requests.py │ │ └── responses.py └── tests │ ├── BUILD │ ├── data │ ├── token_request.json │ └── verify_number.json │ └── test_number_verification.py ├── network_sim_swap ├── BUILD ├── CHANGES.md ├── README.md ├── pyproject.toml ├── src │ └── vonage_network_sim_swap │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── _version.py │ │ ├── requests.py │ │ ├── responses.py │ │ └── sim_swap.py └── tests │ ├── BUILD │ ├── data │ ├── check_sim_swap.json │ └── get_swap_date.json │ └── test_sim_swap.py ├── number_insight ├── BUILD ├── CHANGES.md ├── README.md ├── pyproject.toml ├── src │ └── vonage_number_insight │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── _version.py │ │ ├── errors.py │ │ ├── number_insight.py │ │ ├── requests.py │ │ └── responses.py └── tests │ ├── BUILD │ ├── data │ ├── advanced_async_insight.json │ ├── advanced_async_insight_error.json │ ├── advanced_async_insight_partial_error.json │ ├── advanced_sync_insight.json │ ├── basic_insight.json │ ├── basic_insight_error.json │ └── standard_insight.json │ └── test_number_insight.py ├── number_management ├── BUILD ├── CHANGES.md ├── README.md ├── pyproject.toml ├── src │ └── vonage_numbers │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── _version.py │ │ ├── enums.py │ │ ├── errors.py │ │ ├── number_management.py │ │ ├── requests.py │ │ └── responses.py └── tests │ ├── BUILD │ ├── data │ ├── list_owned_numbers_basic.json │ ├── list_owned_numbers_filter.json │ ├── list_owned_numbers_subset.json │ ├── no_number.json │ ├── nothing.json │ ├── number.json │ ├── search_available_numbers_basic.json │ ├── search_available_numbers_end_of_list.json │ └── search_available_numbers_filter.json │ └── test_numbers.py ├── pants.ci.toml ├── pants.toml ├── pyproject.toml ├── requirements.txt ├── sms ├── BUILD ├── CHANGES.md ├── README.md ├── pyproject.toml ├── src │ └── vonage_sms │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── _version.py │ │ ├── errors.py │ │ ├── requests.py │ │ ├── responses.py │ │ └── sms.py └── tests │ ├── BUILD │ ├── data │ ├── conversion_not_enabled.html │ ├── null │ ├── send_long_sms.json │ ├── send_sms.json │ ├── send_sms_error.json │ └── send_sms_partial_error.json │ └── test_sms.py ├── subaccounts ├── BUILD ├── CHANGES.md ├── README.md ├── pyproject.toml ├── src │ └── vonage_subaccounts │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── _version.py │ │ ├── errors.py │ │ ├── requests.py │ │ ├── responses.py │ │ └── subaccounts.py └── tests │ ├── BUILD │ ├── data │ ├── create_subaccount.json │ ├── get_subaccount.json │ ├── list_balance_transfers.json │ ├── list_credit_transfers.json │ ├── list_subaccounts.json │ ├── modify_subaccount.json │ ├── transfer.json │ ├── transfer_number.json │ └── transfer_number_error_suspended_account.json │ └── test_subaccounts.py ├── testutils ├── BUILD ├── __init__.py ├── data │ └── fake_private_key.txt ├── mock_auth.py └── testutils.py ├── users ├── BUILD ├── CHANGES.md ├── README.md ├── pyproject.toml ├── src │ └── vonage_users │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── _version.py │ │ ├── common.py │ │ ├── requests.py │ │ ├── responses.py │ │ └── users.py └── tests │ ├── BUILD │ ├── data │ ├── list_users.json │ ├── list_users_options.json │ ├── updated_user.json │ ├── user.json │ └── user_not_found.json │ └── test_users.py ├── verify ├── BUILD ├── CHANGES.md ├── README.md ├── pyproject.toml ├── src │ └── vonage_verify │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── _version.py │ │ ├── enums.py │ │ ├── errors.py │ │ ├── requests.py │ │ ├── responses.py │ │ └── verify.py └── tests │ ├── BUILD │ ├── data │ ├── check_code.json │ ├── check_code_400.json │ ├── check_code_410.json │ ├── trigger_next_workflow_error.json │ ├── verify_request.json │ └── verify_request_error.json │ ├── test_models.py │ └── test_verify.py ├── verify_legacy ├── BUILD ├── CHANGES.md ├── README.md ├── pyproject.toml ├── src │ └── vonage_verify_legacy │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── _version.py │ │ ├── errors.py │ │ ├── language_codes.py │ │ ├── requests.py │ │ ├── responses.py │ │ └── verify_legacy.py └── tests │ ├── BUILD │ ├── data │ ├── cancel_verification.json │ ├── cancel_verification_error.json │ ├── check_code.json │ ├── check_code_error.json │ ├── network_unblock.json │ ├── network_unblock_error.json │ ├── search_request.json │ ├── search_request_error.json │ ├── search_request_list.json │ ├── trigger_next_event.json │ ├── trigger_next_event_error.json │ ├── verify_request.json │ ├── verify_request_error.json │ └── verify_request_error_with_network.json │ └── test_verify_legacy.py ├── video ├── BUILD ├── CHANGES.md ├── OPENTOK_TO_VONAGE_MIGRATION.md ├── README.md ├── pyproject.toml ├── src │ └── vonage_video │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── _version.py │ │ ├── errors.py │ │ ├── models │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── archive.py │ │ ├── audio_connector.py │ │ ├── broadcast.py │ │ ├── captions.py │ │ ├── common.py │ │ ├── enums.py │ │ ├── experience_composer.py │ │ ├── session.py │ │ ├── signal.py │ │ ├── sip.py │ │ ├── stream.py │ │ └── token.py │ │ └── video.py └── tests │ ├── BUILD │ ├── data │ ├── archive.json │ ├── audio_connector.json │ ├── broadcast.json │ ├── captions_error_already_enabled.json │ ├── change_stream_layout.json │ ├── create_session.json │ ├── delete_archive_error.json │ ├── get_experience_composer.json │ ├── get_stream.json │ ├── initiate_sip_call.json │ ├── list_archives.json │ ├── list_broadcasts.json │ ├── list_broadcasts_next_page.json │ ├── list_experience_composers.json │ ├── list_streams.json │ ├── nothing.json │ ├── start_broadcast_error.json │ ├── start_captions.json │ ├── start_experience_composer.json │ ├── stop_archive.json │ ├── stop_archive_error.json │ ├── stop_broadcast.json │ └── stop_broadcast_timeout_error.json │ ├── test_archive.py │ ├── test_audio_connector.py │ ├── test_broadcast.py │ ├── test_captions.py │ ├── test_experience_composer.py │ ├── test_moderation.py │ ├── test_session.py │ ├── test_signal.py │ ├── test_sip.py │ ├── test_stream.py │ └── test_token.py ├── voice ├── BUILD ├── CHANGES.md ├── README.md ├── pyproject.toml ├── src │ └── vonage_voice │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── _version.py │ │ ├── errors.py │ │ ├── models │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── common.py │ │ ├── connect_endpoints.py │ │ ├── enums.py │ │ ├── input_types.py │ │ ├── ncco.py │ │ ├── requests.py │ │ └── responses.py │ │ └── voice.py └── tests │ ├── BUILD │ ├── data │ ├── create_call.json │ ├── file_stream.mp3 │ ├── get_call.json │ ├── list_calls.json │ ├── list_calls_filter.json │ ├── play_audio_into_call.json │ ├── play_dtmf_into_call.json │ ├── play_tts_into_call.json │ ├── stop_audio_stream.json │ └── stop_tts.json │ ├── test_ncco_actions.py │ └── test_voice.py ├── vonage ├── BUILD ├── CHANGES.md ├── README.md ├── pyproject.toml ├── src │ └── vonage │ │ ├── BUILD │ │ ├── __init__.py │ │ ├── _version.py │ │ └── vonage.py └── tests │ ├── BUILD │ └── test_vonage.py └── vonage_utils ├── BUILD ├── CHANGES.md ├── README.md ├── pyproject.toml ├── src └── vonage_utils │ ├── BUILD │ ├── __init__.py │ ├── _version.py │ ├── errors.py │ ├── models.py │ ├── types.py │ └── utils.py └── tests ├── BUILD ├── test_format_phone_number.py └── test_remove_none_values.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | --- 11 | name: Bug report 12 | about: Create a report to help us improve 13 | title: '' 14 | labels: '' 15 | assignees: '' 16 | 17 | --- 18 | 19 | 20 | 21 | ## Expected Behavior 22 | 23 | 24 | 25 | ## Current Behavior 26 | 27 | 28 | 29 | ## Possible Solution 30 | 31 | 32 | 33 | ## Steps to Reproduce (for bugs) 34 | 35 | 36 | 1. 37 | 2. 38 | 3. 39 | 4. 40 | 41 | ## Context 42 | 43 | 44 | 45 | ## Your Environment 46 | 47 | * Version used: 48 | * Environment name and version (e.g. language and server version): 49 | * Operating System and version: 50 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/something-else.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Something else 3 | about: Custom template 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Tell us what's up! 11 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [push, pull_request] 3 | 4 | permissions: 5 | actions: write 6 | checks: write 7 | contents: read 8 | deployments: read 9 | issues: write 10 | discussions: write 11 | packages: read 12 | pages: write 13 | pull-requests: write 14 | security-events: write 15 | statuses: write 16 | 17 | env: 18 | PANTS_CONFIG_FILES: "pants.ci.toml" 19 | 20 | jobs: 21 | test: 22 | name: Test 23 | runs-on: ${{ matrix.os }} 24 | strategy: 25 | fail-fast: false 26 | matrix: 27 | python: ["3.9", "3.10", "3.11", "3.12"] 28 | os: ["ubuntu-latest"] 29 | steps: 30 | - name: Clone repo 31 | uses: actions/checkout@v4 32 | - name: Setup python 33 | uses: actions/setup-python@v5 34 | with: 35 | python-version: ${{ matrix.python }} 36 | - name: Initialize pants 37 | uses: pantsbuild/actions/init-pants@main 38 | with: 39 | gha-cache-key: cache0-py${{ matrix.python }} 40 | named-caches-hash: ${{ hashFiles('requirements.txt') }} 41 | - name: Check BUILD files 42 | run: | 43 | pants tailor --check update-build-files --check :: 44 | - name: Lint 45 | run: | 46 | pants lint :: 47 | - name: Run tests 48 | run: | 49 | pants test --use-coverage :: 50 | -------------------------------------------------------------------------------- /.github/workflows/mutation-test.yml: -------------------------------------------------------------------------------- 1 | name: Mutation Test 2 | on: workflow_dispatch 3 | 4 | permissions: 5 | actions: write 6 | checks: write 7 | contents: read 8 | deployments: read 9 | issues: write 10 | discussions: write 11 | packages: read 12 | pages: write 13 | pull-requests: write 14 | security-events: write 15 | statuses: write 16 | 17 | jobs: 18 | mutation: 19 | name: run mutation test 20 | runs-on: ubuntu-latest 21 | strategy: 22 | fail-fast: true 23 | matrix: 24 | python-version: ["3.10"] 25 | 26 | continue-on-error: true 27 | 28 | steps: 29 | - uses: actions/checkout@v4 30 | - name: Set up Python ${{ matrix.python-version }} 31 | uses: actions/setup-python@v5 32 | with: 33 | python-version: ${{ matrix.python-version }} 34 | - name: Install dependencies 35 | run: | 36 | python -m pip install --upgrade pip 37 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 38 | python -m pip install mutmut 39 | - name: Run mutation test 40 | run: | 41 | mutmut run --no-progress --CI 42 | - name: Save HTML output 43 | run: | 44 | mutmut html 45 | - uses: actions/upload-artifact@v3 46 | with: 47 | name: mutation-test-report 48 | path: html/ 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/python 2 | 3 | ### Python ### 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | env/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .coverage 45 | .coverage.* 46 | .coveralls.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *,cover 51 | .hypothesis/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # pyenv 78 | .python-version 79 | 80 | # celery beat schedule file 81 | celerybeat-schedule 82 | 83 | # SageMath parsed files 84 | *.sage.py 85 | 86 | # dotenv 87 | .env 88 | 89 | # virtualenv 90 | .venv* 91 | venv* 92 | ENV* 93 | 94 | # Spyder project settings 95 | .spyderproject 96 | .spyproject 97 | 98 | # Rope project settings 99 | .ropeproject 100 | 101 | # mkdocs documentation 102 | /site 103 | 104 | .requirements.txt 105 | *_quickstart* 106 | 107 | .DS_Store 108 | .vscode 109 | .idea 110 | .pypirc 111 | .pytest_cache 112 | html/ 113 | .mutmut-cache 114 | _test_scripts/ 115 | _dev_scripts/ 116 | 117 | # Pants workspace files 118 | /.pants.* 119 | /dist/ 120 | /.pids 121 | -------------------------------------------------------------------------------- /.mend: -------------------------------------------------------------------------------- 1 | { 2 | "settingsInheritedFrom": "Vonage/whitesource-config@main", 3 | "scanSettings": { 4 | "enableIaC": false 5 | } 6 | } -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.4.0 4 | hooks: 5 | - id: check-yaml 6 | - id: trailing-whitespace -------------------------------------------------------------------------------- /BUILD: -------------------------------------------------------------------------------- 1 | python_requirements( 2 | name="reqs", 3 | ) 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: format test coverage coverage-report install 2 | 3 | format: 4 | pants lint :: 5 | pants fix :: 6 | 7 | test: 8 | pants test :: 9 | 10 | coverage: 11 | pants test --use-coverage :: 12 | 13 | coverage-report: 14 | pants test --use-coverage --open-coverage :: 15 | 16 | install: 17 | pip install -r requirements.txt -------------------------------------------------------------------------------- /account/BUILD: -------------------------------------------------------------------------------- 1 | resource(name='pyproject', source='pyproject.toml') 2 | file(name='readme', source='README.md') 3 | 4 | files(sources=['tests/data/*']) 5 | 6 | python_distribution( 7 | name='vonage-account', 8 | dependencies=[ 9 | ':pyproject', 10 | ':readme', 11 | 'account/src/vonage_account', 12 | ], 13 | provides=python_artifact(), 14 | generate_setup=False, 15 | repositories=['@pypi'], 16 | ) 17 | -------------------------------------------------------------------------------- /account/CHANGES.md: -------------------------------------------------------------------------------- 1 | # 1.1.1 2 | - Update dependency versions 3 | 4 | # 1.1.0 5 | - Add support for the [Vonage Pricing API](https://developer.vonage.com/en/api/pricing) 6 | - Update dependency versions 7 | 8 | # 1.0.2 9 | - Support for Python 3.13, drop support for 3.8 10 | 11 | # 1.0.1 12 | - Add docstrings to data models 13 | 14 | # 1.0.0 15 | - Initial upload 16 | -------------------------------------------------------------------------------- /account/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = 'vonage-account' 3 | dynamic = ["version"] 4 | description = 'Vonage Account API package' 5 | readme = "README.md" 6 | authors = [{ name = "Vonage", email = "devrel@vonage.com" }] 7 | requires-python = ">=3.9" 8 | dependencies = [ 9 | "vonage-http-client>=1.5.0", 10 | "vonage-utils>=1.1.4", 11 | "pydantic>=2.9.2", 12 | ] 13 | classifiers = [ 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.9", 17 | "Programming Language :: Python :: 3.10", 18 | "Programming Language :: Python :: 3.11", 19 | "Programming Language :: Python :: 3.12", 20 | "Programming Language :: Python :: 3.13", 21 | "License :: OSI Approved :: Apache Software License", 22 | ] 23 | 24 | [project.urls] 25 | homepage = "https://github.com/Vonage/vonage-python-sdk" 26 | 27 | [tool.setuptools.dynamic] 28 | version = { attr = "vonage_account._version.__version__" } 29 | 30 | [build-system] 31 | requires = ["setuptools>=61.0", "wheel"] 32 | build-backend = "setuptools.build_meta" 33 | -------------------------------------------------------------------------------- /account/src/vonage_account/BUILD: -------------------------------------------------------------------------------- 1 | python_sources() 2 | -------------------------------------------------------------------------------- /account/src/vonage_account/__init__.py: -------------------------------------------------------------------------------- 1 | from .account import Account 2 | from .errors import InvalidSecretError 3 | from .requests import GetCountryPricingRequest, GetPrefixPricingRequest, ServiceType 4 | from .responses import ( 5 | Balance, 6 | GetMultiplePricingResponse, 7 | GetPricingResponse, 8 | NetworkPricing, 9 | SettingsResponse, 10 | TopUpResponse, 11 | VonageApiSecret, 12 | ) 13 | 14 | __all__ = [ 15 | 'Account', 16 | 'InvalidSecretError', 17 | 'GetCountryPricingRequest', 18 | 'GetPrefixPricingRequest', 19 | 'ServiceType', 20 | 'Balance', 21 | 'GetPricingResponse', 22 | 'GetMultiplePricingResponse', 23 | 'NetworkPricing', 24 | 'SettingsResponse', 25 | 'TopUpResponse', 26 | 'VonageApiSecret', 27 | ] 28 | -------------------------------------------------------------------------------- /account/src/vonage_account/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = '1.1.1' 2 | -------------------------------------------------------------------------------- /account/src/vonage_account/errors.py: -------------------------------------------------------------------------------- 1 | from vonage_utils.errors import VonageError 2 | 3 | 4 | class InvalidSecretError(VonageError): 5 | """Indicates that the secret provided was invalid.""" 6 | -------------------------------------------------------------------------------- /account/src/vonage_account/requests.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | from pydantic import BaseModel 4 | 5 | 6 | class ServiceType(str, Enum): 7 | """The service you wish to retrieve outbound pricing data about. 8 | 9 | Values: 10 | ``` 11 | SMS: SMS 12 | SMS_TRANSIT: SMS transit 13 | VOICE: Voice 14 | ``` 15 | """ 16 | 17 | SMS = 'sms' 18 | SMS_TRANSIT = 'sms-transit' 19 | VOICE = 'voice' 20 | 21 | 22 | class GetCountryPricingRequest(BaseModel): 23 | """The options for getting the pricing for a specific country. 24 | 25 | Args: 26 | country_code (str): The two-letter country code for the country to retrieve 27 | pricing data about. 28 | type (ServiceType, Optional): The type of service to retrieve pricing data about. 29 | """ 30 | 31 | country_code: str 32 | type: ServiceType = ServiceType.SMS 33 | 34 | 35 | class GetPrefixPricingRequest(BaseModel): 36 | """The options for getting the pricing for a specific prefix. 37 | 38 | Args: 39 | prefix (str): The numerical dialing prefix to look up pricing for, e.g. "1", "44". 40 | type (ServiceType, Optional): The type of service to retrieve pricing data about. 41 | """ 42 | 43 | prefix: str 44 | type: ServiceType = ServiceType.SMS 45 | -------------------------------------------------------------------------------- /account/tests/BUILD: -------------------------------------------------------------------------------- 1 | python_tests(dependencies=['account', 'testutils']) 2 | -------------------------------------------------------------------------------- /account/tests/data/create_secret_error_max_number.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "https://developer.nexmo.com/api-errors/account/secret-management#add-excess-secret", 3 | "title": "Secret Addition Forbidden", 4 | "detail": "Account reached maximum number [2] of allowed secrets", 5 | "instance": "48898273-7ae1-4ce4-8125-a71058ca6069" 6 | } -------------------------------------------------------------------------------- /account/tests/data/get_balance.json: -------------------------------------------------------------------------------- 1 | { 2 | "value": 29.18202293, 3 | "autoReload": false 4 | } -------------------------------------------------------------------------------- /account/tests/data/get_multiple_countries_pricing.json: -------------------------------------------------------------------------------- 1 | { 2 | "count": 2, 3 | "countries": [ 4 | { 5 | "dialingPrefix": "39", 6 | "defaultPrice": "0.08270000", 7 | "currency": "EUR", 8 | "countryDisplayName": "Italy", 9 | "countryCode": "IT", 10 | "countryName": "Italy", 11 | "networks": [ 12 | { 13 | "type": "mobile", 14 | "price": "0.08270000", 15 | "currency": "EUR", 16 | "mcc": "222", 17 | "mnc": "07", 18 | "networkCode": "22207", 19 | "networkName": "Noverca Italia S.r.l." 20 | }, 21 | { 22 | "type": "mobile", 23 | "price": "0.08270000", 24 | "currency": "EUR", 25 | "mcc": "222", 26 | "mnc": "08", 27 | "networkCode": "22208", 28 | "networkName": "FastWeb S.p.A." 29 | }, 30 | { 31 | "type": "landline_premium", 32 | "price": "0.08270000", 33 | "currency": "EUR", 34 | "networkCode": "IT-PREMIUM", 35 | "networkName": "Italy Premium" 36 | } 37 | ] 38 | }, 39 | { 40 | "dialingPrefix": "39", 41 | "currency": "EUR", 42 | "countryDisplayName": "Vatican City", 43 | "countryCode": "VA", 44 | "countryName": "Vatican City" 45 | } 46 | ] 47 | } -------------------------------------------------------------------------------- /account/tests/data/list_secrets.json: -------------------------------------------------------------------------------- 1 | { 2 | "_links": { 3 | "self": { 4 | "href": "/accounts/test_api_key/secrets" 5 | } 6 | }, 7 | "_embedded": { 8 | "secrets": [ 9 | { 10 | "_links": { 11 | "self": { 12 | "href": "/accounts/test_api_key/secrets/1b1b1b1b-1b1b-1b-1b1b-1b1b1b1b1b1b" 13 | } 14 | }, 15 | "id": "1b1b1b1b-1b1b-1b-1b1b-1b1b1b1b1b1b", 16 | "created_at": "2022-03-28T14:16:56Z" 17 | } 18 | ] 19 | } 20 | } -------------------------------------------------------------------------------- /account/tests/data/revoke_secret_error.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "https://developer.nexmo.com/api-errors/account/secret-management#delete-last-secret", 3 | "title": "Secret Deletion Forbidden", 4 | "detail": "Can not delete the last secret. The account must always have at least 1 secret active at any time", 5 | "instance": "a845d164-5623-4cc1-b7c6-0f95b94c6e53" 6 | } -------------------------------------------------------------------------------- /account/tests/data/secret.json: -------------------------------------------------------------------------------- 1 | { 2 | "_links": { 3 | "self": { 4 | "href": "/accounts/test_api_key/secrets" 5 | } 6 | }, 7 | "id": "ad6dc56f-07b5-46e1-a527-85530e625800", 8 | "created_at": "2017-03-02T16:34:49Z" 9 | } -------------------------------------------------------------------------------- /account/tests/data/top_up.json: -------------------------------------------------------------------------------- 1 | { 2 | "error-code": "200", 3 | "error-code-label": "success" 4 | } -------------------------------------------------------------------------------- /account/tests/data/update_default_sms_webhook.json: -------------------------------------------------------------------------------- 1 | { 2 | "mo-callback-url": "https://example.com/inbound_sms_webhook", 3 | "dr-callback-url": "https://example.com/delivery_receipt_webhook", 4 | "max-outbound-request": 30, 5 | "max-inbound-request": 30, 6 | "max-calls-per-second": 30 7 | } -------------------------------------------------------------------------------- /application/BUILD: -------------------------------------------------------------------------------- 1 | resource(name='pyproject', source='pyproject.toml') 2 | file(name='readme', source='README.md') 3 | 4 | files(sources=['tests/data/*']) 5 | 6 | python_distribution( 7 | name='vonage-application', 8 | dependencies=[ 9 | ':pyproject', 10 | ':readme', 11 | 'application/src/vonage_application', 12 | ], 13 | provides=python_artifact(), 14 | generate_setup=False, 15 | repositories=['@pypi'], 16 | ) 17 | -------------------------------------------------------------------------------- /application/CHANGES.md: -------------------------------------------------------------------------------- 1 | # 2.0.1 2 | - Updated dependency versions 3 | 4 | # 2.0.0 5 | - Rename `params` -> `config` in method arguments 6 | - Update dependency versions 7 | 8 | # 1.0.3 9 | - Support for Python 3.13, drop support for 3.8 10 | 11 | # 1.0.2 12 | - Add docstrings to data models 13 | 14 | # 1.0.1 15 | - Update project metadata 16 | 17 | # 1.0.0 18 | - Initial upload 19 | -------------------------------------------------------------------------------- /application/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = 'vonage-application' 3 | dynamic = ["version"] 4 | description = 'Vonage Application API package' 5 | readme = "README.md" 6 | authors = [{ name = "Vonage", email = "devrel@vonage.com" }] 7 | requires-python = ">=3.9" 8 | dependencies = [ 9 | "vonage-http-client>=1.5.0", 10 | "vonage-utils>=1.1.4", 11 | "pydantic>=2.9.2", 12 | ] 13 | classifiers = [ 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.9", 17 | "Programming Language :: Python :: 3.10", 18 | "Programming Language :: Python :: 3.11", 19 | "Programming Language :: Python :: 3.12", 20 | "Programming Language :: Python :: 3.13", 21 | "License :: OSI Approved :: Apache Software License", 22 | ] 23 | 24 | [project.urls] 25 | homepage = "https://github.com/Vonage/vonage-python-sdk" 26 | 27 | [tool.setuptools.dynamic] 28 | version = { attr = "vonage_application._version.__version__" } 29 | 30 | [build-system] 31 | requires = ["setuptools>=61.0", "wheel"] 32 | build-backend = "setuptools.build_meta" 33 | -------------------------------------------------------------------------------- /application/src/vonage_application/BUILD: -------------------------------------------------------------------------------- 1 | python_sources() 2 | -------------------------------------------------------------------------------- /application/src/vonage_application/__init__.py: -------------------------------------------------------------------------------- 1 | from . import errors 2 | from .application import Application 3 | from .common import ( 4 | ApplicationUrl, 5 | Capabilities, 6 | Keys, 7 | Messages, 8 | MessagesWebhooks, 9 | Privacy, 10 | Rtc, 11 | RtcWebhooks, 12 | Vbc, 13 | Verify, 14 | VerifyWebhooks, 15 | Voice, 16 | VoiceUrl, 17 | VoiceWebhooks, 18 | ) 19 | from .enums import Region 20 | from .requests import ApplicationConfig, ListApplicationsFilter 21 | from .responses import ApplicationData, ListApplicationsResponse 22 | 23 | __all__ = [ 24 | 'Application', 25 | 'ApplicationConfig', 26 | 'ApplicationData', 27 | 'ApplicationUrl', 28 | 'Capabilities', 29 | 'Keys', 30 | 'ListApplicationsFilter', 31 | 'ListApplicationsResponse', 32 | 'Messages', 33 | 'MessagesWebhooks', 34 | 'Privacy', 35 | 'Region', 36 | 'Rtc', 37 | 'RtcWebhooks', 38 | 'Vbc', 39 | 'Verify', 40 | 'VerifyWebhooks', 41 | 'Voice', 42 | 'VoiceUrl', 43 | 'VoiceWebhooks', 44 | 'errors', 45 | ] 46 | -------------------------------------------------------------------------------- /application/src/vonage_application/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = '2.0.1' 2 | -------------------------------------------------------------------------------- /application/src/vonage_application/enums.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class Region(str, Enum): 5 | """All inbound, programmable SIP and SIP connect voice calls will be sent to the 6 | selected region unless the call itself is sent to a regional endpoint. 7 | 8 | If the call is using a regional endpoint, this will override the application setting. 9 | """ 10 | 11 | NA_EAST = 'na-east' 12 | NA_WEST = 'na-west' 13 | EU_EAST = 'eu-east' 14 | EU_WEST = 'eu-west' 15 | APAC_SNG = 'apac-sng' 16 | APAC_AUSTRALIA = 'apac-australia' 17 | -------------------------------------------------------------------------------- /application/src/vonage_application/errors.py: -------------------------------------------------------------------------------- 1 | from vonage_utils.errors import VonageError 2 | 3 | 4 | class ApplicationError(VonageError): 5 | """Indicates an error with the Application package.""" 6 | -------------------------------------------------------------------------------- /application/src/vonage_application/requests.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from pydantic import BaseModel 4 | 5 | from .common import ApplicationBase 6 | 7 | 8 | class ListApplicationsFilter(BaseModel): 9 | """Request object for filtering applications. 10 | 11 | Args: 12 | page_size (int, Optional): The number of applications to return per page. 13 | page (int, Optional): The page number to return. 14 | """ 15 | 16 | page_size: Optional[int] = 100 17 | page: int = None 18 | 19 | 20 | class ApplicationConfig(ApplicationBase): 21 | """Application object used in requests when communicating with the Vonage Application 22 | API. 23 | 24 | Args: 25 | name (str): The name of the application. 26 | capabilities (Capabilities, Optional): The capabilities of the application. 27 | privacy (Privacy, Optional): The privacy settings for the application. 28 | keys (Keys, Optional): The application keys. 29 | """ 30 | -------------------------------------------------------------------------------- /application/tests/BUILD: -------------------------------------------------------------------------------- 1 | python_tests(dependencies=['application', 'testutils']) 2 | -------------------------------------------------------------------------------- /application/tests/data/create_application_basic.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "ba1a6aa3-8ac6-487d-ac5c-be469e77ddb7", 3 | "name": "My Application", 4 | "keys": { 5 | "private_key": "-----BEGIN PRIVATE KEY-----\nprivate_key_info_goes_here\n-----END PRIVATE KEY-----\n", 6 | "public_key": "-----BEGIN PUBLIC KEY-----\npublic_key_info_goes_here\n-----END PUBLIC KEY-----\n" 7 | }, 8 | "privacy": { 9 | "improve_ai": false 10 | }, 11 | "capabilities": {}, 12 | "_links": { 13 | "self": { 14 | "href": "/v2/applications/ba1a6aa3-8ac6-487d-ac5c-be469e77ddb7" 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /application/tests/data/get_application.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b", 3 | "name": "My Server Demo", 4 | "keys": { 5 | "public_key": "-----BEGIN PUBLIC KEY-----\npublic_key_info_goes_here\n-----END PUBLIC KEY-----\n" 6 | }, 7 | "privacy": { 8 | "improve_ai": false 9 | }, 10 | "capabilities": { 11 | "voice": { 12 | "webhooks": { 13 | "event_url": { 14 | "address": "http://example.ngrok.app/webhooks/events", 15 | "http_method": "POST", 16 | "socket_timeout": 10000, 17 | "connect_timeout": 1000 18 | }, 19 | "answer_url": { 20 | "address": "http://example.ngrok.app/webhooks/answer", 21 | "http_method": "GET", 22 | "socket_timeout": 5000, 23 | "connect_timeout": 1000 24 | } 25 | }, 26 | "signed_callbacks": true, 27 | "conversations_ttl": 48, 28 | "leg_persistence_time": 7 29 | } 30 | }, 31 | "_links": { 32 | "self": { 33 | "href": "/v2/applications/1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b" 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /application/tests/data/list_applications_basic.json: -------------------------------------------------------------------------------- 1 | { 2 | "page_size": 100, 3 | "page": 1, 4 | "total_items": 1, 5 | "total_pages": 1, 6 | "_embedded": { 7 | "applications": [ 8 | { 9 | "id": "1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b", 10 | "name": "dev-application", 11 | "keys": { 12 | "public_key": "-----BEGIN PUBLIC KEY-----\npublic_key_info_goes_here\n-----END PUBLIC KEY-----\n" 13 | }, 14 | "privacy": { 15 | "improve_ai": true 16 | }, 17 | "capabilities": { 18 | "voice": { 19 | "webhooks": { 20 | "event_url": { 21 | "address": "http://example.com", 22 | "http_method": "POST" 23 | }, 24 | "answer_url": { 25 | "address": "http://example.com", 26 | "http_method": "GET" 27 | } 28 | }, 29 | "signed_callbacks": true, 30 | "conversations_ttl": 9000, 31 | "leg_persistence_time": 7 32 | } 33 | } 34 | } 35 | ] 36 | }, 37 | "_links": { 38 | "self": { 39 | "href": "/v2/applications?page_size=100&page=1" 40 | }, 41 | "first": { 42 | "href": "/v2/applications?page_size=100" 43 | }, 44 | "last": { 45 | "href": "/v2/applications?page_size=100&page=1" 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /application/tests/data/update_application.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b", 3 | "name": "My Updated Application", 4 | "keys": { 5 | "public_key": "-----BEGIN PUBLIC KEY-----\nupdated_public_key_info\n-----END PUBLIC KEY-----\n" 6 | }, 7 | "capabilities": {}, 8 | "_links": { 9 | "self": { 10 | "href": "/v2/applications/1b1b1b1b-1b1b-1b1b-1b1b-1b1b1b1b1b1b" 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /http_client/BUILD: -------------------------------------------------------------------------------- 1 | resource(name='pyproject', source='pyproject.toml') 2 | file(name='readme', source='README.md') 3 | 4 | files(sources=['tests/data/*']) 5 | 6 | python_distribution( 7 | name='vonage-http-client', 8 | dependencies=[ 9 | ':pyproject', 10 | ':readme', 11 | 'http_client/src/vonage_http_client:http_client', 12 | ], 13 | provides=python_artifact(), 14 | generate_setup=False, 15 | repositories=['@pypi'], 16 | ) 17 | -------------------------------------------------------------------------------- /http_client/CHANGES.md: -------------------------------------------------------------------------------- 1 | # 1.5.1 2 | - Remove unnecessary `Content-Type` check on error 3 | 4 | # 1.5.0 5 | - Add new `HttpClient.download_file_stream` method 6 | - Add new `FileStreamingError` exception type 7 | - Add backoff exponential timeout increase for HTTP request retries 8 | - Add retries for `RemoteDisconnected` exceptions 9 | 10 | # 1.4.3 11 | - Update JWT dependency version 12 | 13 | # 1.4.2 14 | - Support for Python 3.13, drop support for 3.8 15 | 16 | # 1.4.1 17 | - Add docstrings to data models 18 | 19 | # 1.4.0 20 | - Add new `oauth2` logic for calling APIs that require Oauth 21 | 22 | # 1.3.1 23 | - Update minimum dependency version 24 | 25 | # 1.3.0 26 | - Add new PUT method 27 | 28 | # 1.2.1 29 | - Expose classes and errors at the package level 30 | 31 | # 1.2.0 32 | - Add `last_request` and `last_response` properties 33 | - Add new `Forbidden` error 34 | 35 | # 1.1.1 36 | - Add new Patch method 37 | - New input fields for different ways to pass data in a request 38 | 39 | # 1.1.0 40 | - Add support for signature authentication 41 | 42 | # 1.0.0 43 | - Initial upload 44 | -------------------------------------------------------------------------------- /http_client/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "vonage-http-client" 3 | dynamic = ["version"] 4 | description = "An HTTP client for making requests to Vonage APIs." 5 | readme = "README.md" 6 | authors = [{ name = "Vonage", email = "devrel@vonage.com" }] 7 | requires-python = ">=3.9" 8 | dependencies = [ 9 | "vonage-utils>=1.1.4", 10 | "vonage-jwt>=1.1.5", 11 | "requests>=2.27.0", 12 | "typing-extensions>=4.9.0", 13 | "pydantic>=2.9.2", 14 | ] 15 | classifiers = [ 16 | "Programming Language :: Python", 17 | "Programming Language :: Python :: 3", 18 | "Programming Language :: Python :: 3.9", 19 | "Programming Language :: Python :: 3.10", 20 | "Programming Language :: Python :: 3.11", 21 | "Programming Language :: Python :: 3.12", 22 | "Programming Language :: Python :: 3.13", 23 | "License :: OSI Approved :: Apache Software License", 24 | ] 25 | 26 | [project.urls] 27 | Homepage = "https://github.com/Vonage/vonage-python-sdk" 28 | 29 | [tool.setuptools.dynamic] 30 | version = { attr = "vonage_http_client._version.__version__" } 31 | 32 | [build-system] 33 | requires = ["setuptools>=61.0", "wheel"] 34 | build-backend = "setuptools.build_meta" 35 | -------------------------------------------------------------------------------- /http_client/src/vonage_http_client/BUILD: -------------------------------------------------------------------------------- 1 | python_sources(name='http_client') 2 | -------------------------------------------------------------------------------- /http_client/src/vonage_http_client/__init__.py: -------------------------------------------------------------------------------- 1 | from .auth import Auth 2 | from .errors import ( 3 | AuthenticationError, 4 | FileStreamingError, 5 | ForbiddenError, 6 | HttpRequestError, 7 | InvalidAuthError, 8 | InvalidHttpClientOptionsError, 9 | JWTGenerationError, 10 | NotFoundError, 11 | RateLimitedError, 12 | ServerError, 13 | ) 14 | from .http_client import HttpClient, HttpClientOptions 15 | 16 | __all__ = [ 17 | 'Auth', 18 | 'AuthenticationError', 19 | 'FileStreamingError', 20 | 'ForbiddenError', 21 | 'HttpRequestError', 22 | 'InvalidAuthError', 23 | 'InvalidHttpClientOptionsError', 24 | 'JWTGenerationError', 25 | 'NotFoundError', 26 | 'RateLimitedError', 27 | 'ServerError', 28 | 'HttpClient', 29 | 'HttpClientOptions', 30 | ] 31 | -------------------------------------------------------------------------------- /http_client/src/vonage_http_client/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = '1.5.1' 2 | -------------------------------------------------------------------------------- /http_client/tests/BUILD: -------------------------------------------------------------------------------- 1 | python_tests(dependencies=['http_client']) 2 | -------------------------------------------------------------------------------- /http_client/tests/data/400.json: -------------------------------------------------------------------------------- 1 | {"Error": "Bad Request"} -------------------------------------------------------------------------------- /http_client/tests/data/400.txt: -------------------------------------------------------------------------------- 1 | Error: Bad Request -------------------------------------------------------------------------------- /http_client/tests/data/401.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "https://developer.nexmo.com/api-errors#unauthorized", 3 | "title": "Unauthorized", 4 | "detail": "You did not provide correct credentials.", 5 | "instance": "a813c536-43f6-4568-acbf-f36ef2db955a" 6 | } -------------------------------------------------------------------------------- /http_client/tests/data/403.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "https://developer.vonage.com/api-errors#forbidden", 3 | "title": "Forbidden", 4 | "detail": "Your account does not have permission to perform this action.", 5 | "instance": "bf0ca0bf927b3b52e3cb03217e1a1ddf" 6 | } -------------------------------------------------------------------------------- /http_client/tests/data/404.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Not found.", 3 | "type": "https://developer.vonage.com/api/conversation#user:error:not-found", 4 | "detail": "User does not exist, or you do not have access.", 5 | "instance": "00a5916655d650e920ccf0daf40ef4ee" 6 | } -------------------------------------------------------------------------------- /http_client/tests/data/429.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Rate Limit Hit", 3 | "type": "https://developer.vonage.com/api-errors#rate-limit", 4 | "detail": "Please wait, then retry your request", 5 | "instance": "bf0ca0bf927b3b52e3cb03217e1a1ddf" 6 | } -------------------------------------------------------------------------------- /http_client/tests/data/500.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "https://developer.vonage.com/api-errors", 3 | "title": "Internal Server Error", 4 | "instance": "272c5fa3-c02a-4451-b33c-d01e8de74023" 5 | } -------------------------------------------------------------------------------- /http_client/tests/data/dummy_private_key.txt: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDQdAHqJHs/a+Ra 3 | 2ubvSd1vz/aWlJ9BqnMUtB7guTlyggdENAbleIkzep6mUHepDJdQh8Qv6zS3lpUe 4 | K0UkDfr1/FvsvxurGw/YYPagUEhP/HxMbs2rnQTiAdWOT+Ux9vPABoyNYvZB90xN 5 | IVhBDRWgkz1HPQBRNjFcm3NOol83h5Uwp5YroGTWx+rpmIiRhQj3mv6luk102d95 6 | 4ulpPpzcYWKIpJNdclJrEkBZaghDZTOpbv79qd+ds9AVp1j8i9cG/owBJpsJWxfw 7 | StMDpNeEZqopeQWmA121sSEsxpAbKJ5DA7F/lmckx74sulKHX1fDWT76cRhloaEQ 8 | VmETdj0VAgMBAAECggEAZ+SBtchz8vKbsBqtAbM/XcR5Iqi1TR2eWMHDJ/65HpSm 9 | +XuyujjerN0e6EZvtT4Uxmq8QaPJNP0kmhI31hXvsB0UVcUUDa4hshb1pIYO3Gq7 10 | Kr8I29EZB2mhndm9Ii9yYhEBiVA66zrNeR225kkWr97iqjhBibhoVr8Vc6oiqcIP 11 | nFy5zSFtQSkhucaPge6rW00JSOD3wg2GM+rgS6r22t8YmqTzAwvwfil5pQfUngal 12 | oywqLOf6CUYXPBleJc1KgaIIP/cSvqh6b/t25o2VXnI4rpRhtleORvYBbH6K6xLa 13 | OWgg6B58T+0/QEqtZIAn4miYtVCkYLB78Ormc7Q9ewKBgQDuSytuYqxdZh/L/RDU 14 | CErFcNO5I1e9fkLAs5dQEBvvdQC74+oA1MsDEVv0xehFa1JwPKSepmvB2UznZg9L 15 | CtR7QKMDZWvS5xx4j0E/b+PiNQ/tlcFZB2UZ0JwviSxdd7omOTscq9c3RIhFHar1 16 | Y38Fixkfm44Ij/K3JqIi2v2QMwKBgQDf8TYOOmAr9UuipUDxMsRSqTGVIY8B+aEJ 17 | W+2aLrqJVkLGTRfrbjzXWYo3+n7kNJjFgNkltDq6HYtufHMYRs/0PPtNR0w0cDPS 18 | Xr7m2LNHTDcBalC/AS4yKZJLNLm+kXA84vkw4qiTjc0LSFxJkouTQzkea0l8EWHt 19 | zRMv/qYVlwKBgBaJOWRJJK/4lo0+M7c5yYh+sSdTNlsPc9Sxp1/FBj9RO26JkXne 20 | pgx2OdIeXWcjTTqcIZ13c71zhZhkyJF6RroZVNFfaCEcBk9IjQ0o0c504jq/7Pc0 21 | gdU9K2g7etykFBDFXNfLUKFDc/fFZIOskzi8/PVGStp4cqXrm23cdBqNAoGBAKtf 22 | A2bP9ViuVjsZCyGJIAPBxlfBXpa8WSe4WZNrvwPqJx9pT6yyp4yE0OkVoJUyStaZ 23 | S5M24NocUd8zDUC+r9TP9d+leAOI+Z87MgumOUuOX2mN2kzQsnFgrrsulhXnZmSx 24 | rNBkI20HTqobrcP/iSAgiU1l/M4c3zwDe3N3A9HxAoGBAM2hYu0Ij6htSNgo/WWr 25 | IEYYXuwf8hPkiuwzlaiWhD3eocgd4S8SsBu/bTCY19hQ2QbBPaYyFlNem+ynQyXx 26 | IOacrgIHCrYnRCxjPfFF/MxgUHJb8ZoiexprP/FME5p0PoRQIEFYa+jVht3hT5wC 27 | 9aedWufq4JJb+akO6MVUjTvs 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /http_client/tests/data/dummy_public_key.txt: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0HQB6iR7P2vkWtrm70nd 3 | b8/2lpSfQapzFLQe4Lk5coIHRDQG5XiJM3qeplB3qQyXUIfEL+s0t5aVHitFJA36 4 | 9fxb7L8bqxsP2GD2oFBIT/x8TG7Nq50E4gHVjk/lMfbzwAaMjWL2QfdMTSFYQQ0V 5 | oJM9Rz0AUTYxXJtzTqJfN4eVMKeWK6Bk1sfq6ZiIkYUI95r+pbpNdNnfeeLpaT6c 6 | 3GFiiKSTXXJSaxJAWWoIQ2UzqW7+/anfnbPQFadY/IvXBv6MASabCVsX8ErTA6TX 7 | hGaqKXkFpgNdtbEhLMaQGyieQwOxf5ZnJMe+LLpSh19Xw1k++nEYZaGhEFZhE3Y9 8 | FQIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /http_client/tests/data/example_get.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "world" 3 | } -------------------------------------------------------------------------------- /http_client/tests/data/example_post.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "world!" 3 | } -------------------------------------------------------------------------------- /http_client/tests/data/file_stream.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vonage/vonage-python-sdk/82a02e48b9793a2aa0bce3fa593de8cf97caf839/http_client/tests/data/file_stream.mp3 -------------------------------------------------------------------------------- /jwt/BUILD: -------------------------------------------------------------------------------- 1 | resource(name='pyproject', source='pyproject.toml') 2 | file(name='readme', source='README.md') 3 | 4 | files(sources=['tests/data/*']) 5 | 6 | python_distribution( 7 | name='vonage-jwt', 8 | dependencies=[ 9 | ':pyproject', 10 | ':readme', 11 | 'jwt/src/vonage_jwt', 12 | ], 13 | provides=python_artifact(), 14 | generate_setup=False, 15 | repositories=['@pypi'], 16 | ) 17 | -------------------------------------------------------------------------------- /jwt/CHANGES.md: -------------------------------------------------------------------------------- 1 | # 1.1.5 2 | - Improve `verify_signature` docstring 3 | 4 | # 1.1.4 5 | - Fix a bug with generating non-default JWTs 6 | 7 | # 1.1.3 8 | - Support for Python 3.13, drop support for 3.8 9 | 10 | # 1.1.2 11 | - Dynamically specify package version 12 | 13 | # 1.1.1 14 | - Exceptions inherit from `VonageError` 15 | - Moving the package into the Vonage Python SDK monorepo 16 | 17 | # 1.1.0 18 | - Add new module with method to verify JWT signatures, `verify_jwt.verify_signature` 19 | 20 | # 1.0.0 21 | - First stable release -------------------------------------------------------------------------------- /jwt/README.md: -------------------------------------------------------------------------------- 1 | # Vonage JWT Generator for Python 2 | 3 | This package (`vonage-jwt`) provides functionality to generate a JWT in Python code. 4 | 5 | It is used by the [Vonage Python SDK](https://github.com/Vonage/vonage-python-sdk), specifically by the `vonage-http-client` package, to generate JWTs for authentication. Thus, it doesn't require manual installation or configuration unless you're using this package independently of an SDK. 6 | 7 | For full API documentation, refer to the [Vonage developer documentation](https://developer.vonage.com). 8 | 9 | - [Installation](#installation) 10 | - [Generating JWTs](#generating-jwts) 11 | - [Verifying a JWT signature](#verifying-a-jwt-signature) 12 | 13 | ## Installation 14 | 15 | Install from the Python Package Index with pip: 16 | 17 | ```bash 18 | pip install vonage-jwt 19 | ``` 20 | 21 | ## Generating JWTs 22 | 23 | This JWT Generator can be used implicitly, just by using the [Vonage Python SDK](https://github.com/Vonage/vonage-python-sdk) to make JWT-authenticated API calls. 24 | 25 | It can also be used as a standalone JWT generator for use with Vonage APIs, like so: 26 | 27 | ### Import the `JwtClient` object 28 | 29 | ```python 30 | from vonage_jwt import JwtClient 31 | ``` 32 | 33 | ### Create a `JwtClient` object 34 | 35 | ```python 36 | jwt_client = JwtClient(application_id, private_key) 37 | ``` 38 | 39 | ### Generate a JWT using the provided application id and private key 40 | 41 | ```python 42 | jwt_client.generate_application_jwt() 43 | ``` 44 | 45 | Optional JWT claims can be provided in a python dictionary: 46 | 47 | ```python 48 | claims = {'jti': 'asdfzxcv1234', 'nbf': now + 100} 49 | jwt_client.generate_application_jwt(claims) 50 | ``` 51 | 52 | ## Verifying a JWT signature 53 | 54 | You can use the `verify_jwt.verify_signature` method to verify a JWT signature is valid. 55 | 56 | ```python 57 | from vonage_jwt import verify_signature 58 | 59 | verify_signature(TOKEN, SIGNATURE_SECRET) # Returns a boolean 60 | ``` 61 | -------------------------------------------------------------------------------- /jwt/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "vonage-jwt" 3 | dynamic = ["version"] 4 | description = "Tooling for working with JWTs for Vonage APIs in Python." 5 | readme = "README.md" 6 | authors = [{ name = "Vonage", email = "devrel@vonage.com" }] 7 | requires-python = ">=3.9" 8 | dependencies = ["vonage-utils>=1.1.4", "pyjwt[crypto]>=1.6.4"] 9 | classifiers = [ 10 | "Programming Language :: Python", 11 | "Programming Language :: Python :: 3", 12 | "Programming Language :: Python :: 3.9", 13 | "Programming Language :: Python :: 3.10", 14 | "Programming Language :: Python :: 3.11", 15 | "Programming Language :: Python :: 3.12", 16 | "Programming Language :: Python :: 3.13", 17 | "License :: OSI Approved :: Apache Software License", 18 | ] 19 | 20 | [project.urls] 21 | Homepage = "https://github.com/Vonage/vonage-python-sdk" 22 | 23 | [tool.setuptools.dynamic] 24 | version = { attr = "vonage_jwt._version.__version__" } 25 | 26 | [build-system] 27 | requires = ["setuptools>=61.0", "wheel"] 28 | build-backend = "setuptools.build_meta" 29 | -------------------------------------------------------------------------------- /jwt/src/vonage_jwt/BUILD: -------------------------------------------------------------------------------- 1 | python_sources() 2 | -------------------------------------------------------------------------------- /jwt/src/vonage_jwt/__init__.py: -------------------------------------------------------------------------------- 1 | from .errors import VonageJwtError, VonageVerifyJwtError 2 | from .jwt import JwtClient 3 | from .verify_jwt import verify_signature 4 | 5 | __all__ = ['JwtClient', 'VonageJwtError', 'VonageVerifyJwtError', 'verify_signature'] 6 | -------------------------------------------------------------------------------- /jwt/src/vonage_jwt/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = '1.1.5' 2 | -------------------------------------------------------------------------------- /jwt/src/vonage_jwt/errors.py: -------------------------------------------------------------------------------- 1 | from vonage_utils import VonageError 2 | 3 | 4 | class VonageJwtError(VonageError): 5 | """An error relating to the Vonage JWT Generator.""" 6 | 7 | 8 | class VonageVerifyJwtError(VonageError): 9 | """The signature could not be verified.""" 10 | -------------------------------------------------------------------------------- /jwt/src/vonage_jwt/verify_jwt.py: -------------------------------------------------------------------------------- 1 | from jwt import InvalidSignatureError, decode 2 | 3 | from .errors import VonageVerifyJwtError 4 | 5 | 6 | def verify_signature(token: str, signature_secret: str = None) -> bool: 7 | """Method to verify that an incoming JWT was sent by Vonage. 8 | 9 | Args: 10 | token (str): The token to verify. 11 | signature_secret (str, optional): The signature to verify the token against. 12 | 13 | Returns: 14 | bool: True if the token is verified, False otherwise. 15 | 16 | Raises: 17 | VonageVerifyJwtError: The signature could not be verified. 18 | """ 19 | 20 | try: 21 | decode(token, signature_secret, algorithms='HS256') 22 | return True 23 | except InvalidSignatureError: 24 | return False 25 | except Exception as e: 26 | raise VonageVerifyJwtError(repr(e)) 27 | -------------------------------------------------------------------------------- /jwt/tests/BUILD: -------------------------------------------------------------------------------- 1 | python_tests(dependencies=['jwt', 'testutils']) 2 | -------------------------------------------------------------------------------- /jwt/tests/data/private_key.txt: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDQdAHqJHs/a+Ra 3 | 2ubvSd1vz/aWlJ9BqnMUtB7guTlyggdENAbleIkzep6mUHepDJdQh8Qv6zS3lpUe 4 | K0UkDfr1/FvsvxurGw/YYPagUEhP/HxMbs2rnQTiAdWOT+Ux9vPABoyNYvZB90xN 5 | IVhBDRWgkz1HPQBRNjFcm3NOol83h5Uwp5YroGTWx+rpmIiRhQj3mv6luk102d95 6 | 4ulpPpzcYWKIpJNdclJrEkBZaghDZTOpbv79qd+ds9AVp1j8i9cG/owBJpsJWxfw 7 | StMDpNeEZqopeQWmA121sSEsxpAbKJ5DA7F/lmckx74sulKHX1fDWT76cRhloaEQ 8 | VmETdj0VAgMBAAECggEAZ+SBtchz8vKbsBqtAbM/XcR5Iqi1TR2eWMHDJ/65HpSm 9 | +XuyujjerN0e6EZvtT4Uxmq8QaPJNP0kmhI31hXvsB0UVcUUDa4hshb1pIYO3Gq7 10 | Kr8I29EZB2mhndm9Ii9yYhEBiVA66zrNeR225kkWr97iqjhBibhoVr8Vc6oiqcIP 11 | nFy5zSFtQSkhucaPge6rW00JSOD3wg2GM+rgS6r22t8YmqTzAwvwfil5pQfUngal 12 | oywqLOf6CUYXPBleJc1KgaIIP/cSvqh6b/t25o2VXnI4rpRhtleORvYBbH6K6xLa 13 | OWgg6B58T+0/QEqtZIAn4miYtVCkYLB78Ormc7Q9ewKBgQDuSytuYqxdZh/L/RDU 14 | CErFcNO5I1e9fkLAs5dQEBvvdQC74+oA1MsDEVv0xehFa1JwPKSepmvB2UznZg9L 15 | CtR7QKMDZWvS5xx4j0E/b+PiNQ/tlcFZB2UZ0JwviSxdd7omOTscq9c3RIhFHar1 16 | Y38Fixkfm44Ij/K3JqIi2v2QMwKBgQDf8TYOOmAr9UuipUDxMsRSqTGVIY8B+aEJ 17 | W+2aLrqJVkLGTRfrbjzXWYo3+n7kNJjFgNkltDq6HYtufHMYRs/0PPtNR0w0cDPS 18 | Xr7m2LNHTDcBalC/AS4yKZJLNLm+kXA84vkw4qiTjc0LSFxJkouTQzkea0l8EWHt 19 | zRMv/qYVlwKBgBaJOWRJJK/4lo0+M7c5yYh+sSdTNlsPc9Sxp1/FBj9RO26JkXne 20 | pgx2OdIeXWcjTTqcIZ13c71zhZhkyJF6RroZVNFfaCEcBk9IjQ0o0c504jq/7Pc0 21 | gdU9K2g7etykFBDFXNfLUKFDc/fFZIOskzi8/PVGStp4cqXrm23cdBqNAoGBAKtf 22 | A2bP9ViuVjsZCyGJIAPBxlfBXpa8WSe4WZNrvwPqJx9pT6yyp4yE0OkVoJUyStaZ 23 | S5M24NocUd8zDUC+r9TP9d+leAOI+Z87MgumOUuOX2mN2kzQsnFgrrsulhXnZmSx 24 | rNBkI20HTqobrcP/iSAgiU1l/M4c3zwDe3N3A9HxAoGBAM2hYu0Ij6htSNgo/WWr 25 | IEYYXuwf8hPkiuwzlaiWhD3eocgd4S8SsBu/bTCY19hQ2QbBPaYyFlNem+ynQyXx 26 | IOacrgIHCrYnRCxjPfFF/MxgUHJb8ZoiexprP/FME5p0PoRQIEFYa+jVht3hT5wC 27 | 9aedWufq4JJb+akO6MVUjTvs 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /jwt/tests/data/public_key.txt: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0HQB6iR7P2vkWtrm70nd 3 | b8/2lpSfQapzFLQe4Lk5coIHRDQG5XiJM3qeplB3qQyXUIfEL+s0t5aVHitFJA36 4 | 9fxb7L8bqxsP2GD2oFBIT/x8TG7Nq50E4gHVjk/lMfbzwAaMjWL2QfdMTSFYQQ0V 5 | oJM9Rz0AUTYxXJtzTqJfN4eVMKeWK6Bk1sfq6ZiIkYUI95r+pbpNdNnfeeLpaT6c 6 | 3GFiiKSTXXJSaxJAWWoIQ2UzqW7+/anfnbPQFadY/IvXBv6MASabCVsX8ErTA6TX 7 | hGaqKXkFpgNdtbEhLMaQGyieQwOxf5ZnJMe+LLpSh19Xw1k++nEYZaGhEFZhE3Y9 8 | FQIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /jwt/tests/test_verify_jwt.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from vonage_jwt.errors import VonageVerifyJwtError 3 | from vonage_jwt.verify_jwt import verify_signature 4 | 5 | token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2OTc2MzQ2ODAsImV4cCI6MzMyNTQ1NDA4MjgsImF1ZCI6IiIsInN1YiI6IiJ9.88vJc3I2HhuqEDixHXVhc9R30tA6U_HQHZTC29y6CGM' 6 | valid_signature = "qwertyuiopasdfghjklzxcvbnm123456" 7 | invalid_signature = 'asdf' 8 | 9 | 10 | def test_verify_signature_valid(): 11 | assert verify_signature(token, valid_signature) is True 12 | 13 | 14 | def test_verify_signature_invalid(): 15 | assert verify_signature(token, invalid_signature) is False 16 | 17 | 18 | def test_verify_signature_error(): 19 | with pytest.raises(VonageVerifyJwtError) as e: 20 | verify_signature('asdf', valid_signature) 21 | assert 'DecodeError' in str(e.value) 22 | -------------------------------------------------------------------------------- /messages/BUILD: -------------------------------------------------------------------------------- 1 | resource(name='pyproject', source='pyproject.toml') 2 | file(name='readme', source='README.md') 3 | 4 | files(sources=['tests/data/*']) 5 | 6 | python_distribution( 7 | name='vonage-messages', 8 | dependencies=[ 9 | ':pyproject', 10 | ':readme', 11 | 'messages/src/vonage_messages', 12 | ], 13 | provides=python_artifact(), 14 | generate_setup=False, 15 | repositories=['@pypi'], 16 | ) 17 | -------------------------------------------------------------------------------- /messages/CHANGES.md: -------------------------------------------------------------------------------- 1 | # 1.4.0 2 | - Make all models originally accessed by `vonage_messages.models.***` available at the top level of the package, i.e. `vonage_messages.***` 3 | 4 | # 1.3.0 5 | - Add support for API key/secret header authentication 6 | - Updated dependency versions 7 | 8 | # 1.2.3 9 | - Update dependency versions 10 | 11 | # 1.2.2 12 | - Support for Python 3.13, drop support for 3.8 13 | 14 | # 1.2.1 15 | - Add docstrings to data models 16 | 17 | # 1.2.0 18 | - Add RCS channel support 19 | - Add methods to revoke an RCS message and mark a WhatsApp message as read 20 | 21 | # 1.1.1 22 | - Update minimum dependency version 23 | 24 | # 1.1.0 25 | - Add `http_client` property 26 | 27 | # 1.0.0 28 | - Initial upload 29 | -------------------------------------------------------------------------------- /messages/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = 'vonage-messages' 3 | dynamic = ["version"] 4 | description = 'Vonage messages package' 5 | readme = "README.md" 6 | authors = [{ name = "Vonage", email = "devrel@vonage.com" }] 7 | requires-python = ">=3.9" 8 | dependencies = [ 9 | "vonage-http-client>=1.5.0", 10 | "vonage-utils>=1.1.4", 11 | "pydantic>=2.9.2", 12 | ] 13 | classifiers = [ 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.8", 17 | "Programming Language :: Python :: 3.9", 18 | "Programming Language :: Python :: 3.10", 19 | "Programming Language :: Python :: 3.11", 20 | "Programming Language :: Python :: 3.12", 21 | "License :: OSI Approved :: Apache Software License", 22 | ] 23 | 24 | [project.urls] 25 | homepage = "https://github.com/Vonage/vonage-python-sdk" 26 | 27 | [tool.setuptools.dynamic] 28 | version = { attr = "vonage_messages._version.__version__" } 29 | 30 | [build-system] 31 | requires = ["setuptools>=61.0", "wheel"] 32 | build-backend = "setuptools.build_meta" 33 | -------------------------------------------------------------------------------- /messages/src/vonage_messages/BUILD: -------------------------------------------------------------------------------- 1 | python_sources() 2 | -------------------------------------------------------------------------------- /messages/src/vonage_messages/__init__.py: -------------------------------------------------------------------------------- 1 | from . import models # Import models to access the module directly 2 | from .messages import Messages 3 | from .models import * # Need this to directly expose data models 4 | from .responses import SendMessageResponse 5 | 6 | __all__ = ['models', 'Messages', 'SendMessageResponse'] 7 | -------------------------------------------------------------------------------- /messages/src/vonage_messages/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = '1.4.0' 2 | -------------------------------------------------------------------------------- /messages/src/vonage_messages/models/BUILD: -------------------------------------------------------------------------------- 1 | python_sources(name='models') 2 | -------------------------------------------------------------------------------- /messages/src/vonage_messages/models/base_message.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from pydantic import BaseModel, Field 4 | from vonage_utils.types import PhoneNumber 5 | 6 | from .enums import WebhookVersion 7 | 8 | 9 | class BaseMessage(BaseModel): 10 | """Model with base properties for a message. 11 | 12 | Args: 13 | to (PhoneNumber): The recipient's phone number in E.164 format. Don't use a leading plus sign. 14 | client_ref (str, Optional): An optional client reference. 15 | webhook_url (str, Optional): The URL to which Status Webhook messages will be sent for this particular message. 16 | webhook_version (WebhookVersion, Optional): Which version of the Messages API will be used to send Status Webhook messages for this particular message. 17 | """ 18 | 19 | to: PhoneNumber 20 | client_ref: Optional[str] = Field(None, max_length=100) 21 | webhook_url: Optional[str] = None 22 | webhook_version: Optional[WebhookVersion] = None 23 | -------------------------------------------------------------------------------- /messages/src/vonage_messages/models/enums.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class MessageType(str, Enum): 5 | """The type of message.""" 6 | 7 | TEXT = 'text' 8 | IMAGE = 'image' 9 | AUDIO = 'audio' 10 | VIDEO = 'video' 11 | FILE = 'file' 12 | TEMPLATE = 'template' 13 | STICKER = 'sticker' 14 | CUSTOM = 'custom' 15 | VCARD = 'vcard' 16 | 17 | 18 | class ChannelType(str, Enum): 19 | """The channel used to send a message.""" 20 | 21 | SMS = 'sms' 22 | MMS = 'mms' 23 | RCS = 'rcs' 24 | WHATSAPP = 'whatsapp' 25 | MESSENGER = 'messenger' 26 | VIBER = 'viber_service' 27 | 28 | 29 | class WebhookVersion(str, Enum): 30 | """Which version of the Messages API will be used to send Status Webhook messages.""" 31 | 32 | V0_1 = 'v0.1' 33 | V1 = 'v1' 34 | 35 | 36 | class EncodingType(str, Enum): 37 | TEXT = 'text' 38 | UNICODE = 'unicode' 39 | AUTO = 'auto' 40 | -------------------------------------------------------------------------------- /messages/src/vonage_messages/responses.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel 2 | 3 | 4 | class SendMessageResponse(BaseModel): 5 | """Response from Vonage's Messages API. 6 | 7 | Attributes: 8 | message_uuid (str): The UUID of the sent message. 9 | """ 10 | 11 | message_uuid: str 12 | -------------------------------------------------------------------------------- /messages/tests/BUILD: -------------------------------------------------------------------------------- 1 | python_tests(dependencies=['messages', 'testutils']) 2 | -------------------------------------------------------------------------------- /messages/tests/data/invalid_error.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "https://developer.vonage.com/api-errors/messages#1150", 3 | "title": "Invalid params", 4 | "detail": "The value of one or more parameters is invalid.", 5 | "instance": "bf0ca0bf927b3b52e3cb03217e1a1ddf", 6 | "invalid_parameters": [ 7 | { 8 | "name": "messenger.tag", 9 | "reason": "invalid value" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /messages/tests/data/low_balance_error.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "https://developer.nexmo.com/api-errors/#low-balance", 3 | "title": "Low balance", 4 | "detail": "This request could not be performed due to your account balance being low.", 5 | "instance": "bf0ca0bf927b3b52e3cb03217e1a1ddf" 6 | } -------------------------------------------------------------------------------- /messages/tests/data/not_found.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "https://developer.vonage.com/api-errors#not-found", 3 | "title": "Not Found", 4 | "detail": "Message with ID asdf not found", 5 | "instance": "617431f2-06b7-4798-af36-1b8151df8359" 6 | } -------------------------------------------------------------------------------- /messages/tests/data/send_message.json: -------------------------------------------------------------------------------- 1 | { 2 | "message_uuid": "d8f86df1-dec6-442f-870a-2241be27d721" 3 | } -------------------------------------------------------------------------------- /messages/tests/test_sms_models.py: -------------------------------------------------------------------------------- 1 | from vonage_messages.models import Sms, SmsOptions 2 | from vonage_messages.models.enums import EncodingType, WebhookVersion 3 | 4 | 5 | def test_create_sms(): 6 | sms_model = Sms( 7 | to='1234567890', 8 | from_='1234567890', 9 | text='Hello, World!', 10 | ) 11 | sms_dict = { 12 | 'to': '1234567890', 13 | 'from': '1234567890', 14 | 'text': 'Hello, World!', 15 | 'channel': 'sms', 16 | 'message_type': 'text', 17 | } 18 | 19 | assert sms_model.model_dump(by_alias=True, exclude_none=True) == sms_dict 20 | 21 | 22 | def test_create_sms_all_fields(): 23 | sms_model = Sms( 24 | to='1234567890', 25 | from_='1234567890', 26 | text='Hello, World!', 27 | sms=SmsOptions( 28 | encoding_type=EncodingType.TEXT, 29 | content_id='content-id', 30 | entity_id='entity-id', 31 | ), 32 | client_ref='client-ref', 33 | webhook_url='https://example.com', 34 | webhook_version=WebhookVersion.V1, 35 | ttl=600, 36 | ) 37 | sms_dict = { 38 | 'to': '1234567890', 39 | 'from': '1234567890', 40 | 'text': 'Hello, World!', 41 | 'sms': { 42 | 'encoding_type': 'text', 43 | 'content_id': 'content-id', 44 | 'entity_id': 'entity-id', 45 | }, 46 | 'client_ref': 'client-ref', 47 | 'webhook_url': 'https://example.com', 48 | 'webhook_version': 'v1', 49 | 'ttl': 600, 50 | 'channel': 'sms', 51 | 'message_type': 'text', 52 | } 53 | 54 | assert sms_model.model_dump(by_alias=True) == sms_dict 55 | -------------------------------------------------------------------------------- /network_auth/BUILD: -------------------------------------------------------------------------------- 1 | resource(name='pyproject', source='pyproject.toml') 2 | file(name='readme', source='README.md') 3 | 4 | files(sources=['tests/data/*']) 5 | 6 | python_distribution( 7 | name='vonage-network-auth', 8 | dependencies=[ 9 | ':pyproject', 10 | ':readme', 11 | 'network_auth/src/vonage_network_auth', 12 | ], 13 | provides=python_artifact(), 14 | generate_setup=False, 15 | repositories=['@pypi'], 16 | ) 17 | -------------------------------------------------------------------------------- /network_auth/CHANGES.md: -------------------------------------------------------------------------------- 1 | # 1.0.2 2 | - Updated dependency versions 3 | 4 | # 1.0.1 5 | - Update dependency versions 6 | 7 | # 1.0.0 8 | - Add methods to work with the Vonage Number Verification API 9 | - Internal refactoring 10 | 11 | # 0.1.1b0 12 | - Add docstrings to data models 13 | 14 | # 0.1.0b0 15 | - Initial upload -------------------------------------------------------------------------------- /network_auth/README.md: -------------------------------------------------------------------------------- 1 | # Vonage Network API Authentication Client 2 | 3 | This package (`vonage-network-auth`) provides a client for authenticating Network APIs that require Oauth2 authentication. Using it, it is possible to generate authenticated JWTs for use with Vonage Network APIs, e.g. Sim Swap, Number Verification. 4 | 5 | This package is intended to be used as part of the `vonage` SDK package, accessing required methods through the SDK instead of directly. Thus, it doesn't require manual installation or configuration unless you're using this package independently of an SDK. 6 | 7 | For full API documentation, refer to the [Vonage developer documentation](https://developer.vonage.com). 8 | 9 | ## Installation 10 | 11 | Install from the Python Package Index with pip: 12 | 13 | ```bash 14 | pip install vonage-network-auth 15 | ``` 16 | 17 | ## Usage 18 | 19 | ### Create a `NetworkAuth` Object 20 | 21 | ```python 22 | from vonage_network_auth import NetworkAuth 23 | from vonage_http_client import HttpClient, Auth 24 | 25 | network_auth = NetworkAuth(HttpClient(Auth(application_id='application-id', private_key='private-key'))) 26 | ``` 27 | 28 | -------------------------------------------------------------------------------- /network_auth/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "vonage-network-auth" 3 | dynamic = ["version"] 4 | description = "Package for working with Network APIs that require Oauth2 in Python." 5 | readme = "README.md" 6 | authors = [{ name = "Vonage", email = "devrel@vonage.com" }] 7 | requires-python = ">=3.9" 8 | dependencies = [ 9 | "vonage-http-client>=1.5.0", 10 | "vonage-utils>=1.1.4", 11 | "pydantic>=2.9.2", 12 | ] 13 | classifiers = [ 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.9", 17 | "Programming Language :: Python :: 3.10", 18 | "Programming Language :: Python :: 3.11", 19 | "Programming Language :: Python :: 3.12", 20 | "Programming Language :: Python :: 3.13", 21 | "License :: OSI Approved :: Apache Software License", 22 | ] 23 | 24 | [project.urls] 25 | Homepage = "https://github.com/Vonage/vonage-python-sdk" 26 | 27 | [tool.setuptools.dynamic] 28 | version = { attr = "vonage_network_auth._version.__version__" } 29 | 30 | [build-system] 31 | requires = ["setuptools>=61.0", "wheel"] 32 | build-backend = "setuptools.build_meta" 33 | -------------------------------------------------------------------------------- /network_auth/src/vonage_network_auth/BUILD: -------------------------------------------------------------------------------- 1 | python_sources() 2 | -------------------------------------------------------------------------------- /network_auth/src/vonage_network_auth/__init__.py: -------------------------------------------------------------------------------- 1 | from .network_auth import NetworkAuth 2 | from .requests import CreateOidcUrl 3 | from .responses import OidcResponse, TokenResponse 4 | 5 | __all__ = [ 6 | 'NetworkAuth', 7 | 'CreateOidcUrl', 8 | 'OidcResponse', 9 | 'TokenResponse', 10 | ] 11 | -------------------------------------------------------------------------------- /network_auth/src/vonage_network_auth/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = '1.0.2' 2 | -------------------------------------------------------------------------------- /network_auth/src/vonage_network_auth/requests.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from pydantic import BaseModel 4 | 5 | 6 | class CreateOidcUrl(BaseModel): 7 | """Model to craft a URL for OIDC authentication. 8 | 9 | Args: 10 | redirect_uri (str): The URI to redirect to after authentication. 11 | state (str): A unique identifier for the request. Can be any string. 12 | login_hint (str): The phone number to use for the request. 13 | """ 14 | 15 | redirect_uri: str 16 | state: str 17 | login_hint: str 18 | scope: Optional[str] = ( 19 | 'openid dpv:FraudPreventionAndDetection#number-verification-verify-read' 20 | ) 21 | -------------------------------------------------------------------------------- /network_auth/src/vonage_network_auth/responses.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from pydantic import BaseModel 4 | 5 | 6 | class OidcResponse(BaseModel): 7 | """Model for an OpenID Connect response. 8 | 9 | Args: 10 | auth_req_id (str): The authentication request ID. 11 | expires_in (int): The time in seconds until the authentication code expires. 12 | interval (int, Optional): The time in seconds until the next request can be made. 13 | """ 14 | 15 | auth_req_id: str 16 | expires_in: int 17 | interval: Optional[int] = None 18 | 19 | 20 | class TokenResponse(BaseModel): 21 | """Model for a token response. 22 | 23 | Args: 24 | access_token (str): The access token. 25 | token_type (str, Optional): The token type. 26 | refresh_token (str, Optional): The refresh token. 27 | expires_in (int, Optional): The time until the token expires. 28 | """ 29 | 30 | access_token: str 31 | token_type: Optional[str] = None 32 | refresh_token: Optional[str] = None 33 | expires_in: Optional[int] = None 34 | -------------------------------------------------------------------------------- /network_auth/tests/BUILD: -------------------------------------------------------------------------------- 1 | python_tests(dependencies=['network_auth', 'testutils']) 2 | -------------------------------------------------------------------------------- /network_auth/tests/data/oidc_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "auth_req_id": "arid/8b0d35f3-4627-487c-a776-aegtdsf4rsd2", 3 | "expires_in": 300, 4 | "interval": 0 5 | } -------------------------------------------------------------------------------- /network_auth/tests/data/oidc_request_permissions_error.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "https://developer.vonage.com/api-errors#invalid-param", 3 | "title": "Bad Request", 4 | "detail": "No Network Application associated with Vonage Application: 29f760f8-7ce1-46c9-ade3-f2dedee4ed5f", 5 | "instance": "b45ae630-7621-42b0-8ff0-6c1ad98e6e32" 6 | } -------------------------------------------------------------------------------- /network_auth/tests/data/token_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "access_token": "eyJhbGciOiJSUzI1NiIsImprdSI6Imh0dHBzOi8vYW51YmlzLWNlcnRzLWMxLWV1dzEucHJvZC52MS52b25hZ2VuZXR3b3Jrcy5uZXQvandrcyIsImtpZCI6IkNOPVZvbmFnZSAxdmFwaWd3IEludGVybmFsIENBOjoxOTUxODQ2ODA3NDg1NTYwNjYzODY3MTM0NjE2MjU2MTU5MjU2NDkiLCJ0eXAiOiJKV1QiLCJ4NXUiOiJodHRwczovL2FudWJpcy1jZXJ0cy1jMS1ldXcxLnByb2QudjEudm9uYWdlbmV0d29ya3MubmV0L3YxL2NlcnRzLzA4NjliNDMyZTEzZmIyMzcwZTk2ZGI4YmUxMDc4MjJkIn0.eyJwcmluY2lwYWwiOnsiYXBpS2V5IjoiNGI1MmMwMGUiLCJhcHBsaWNhdGlvbklkIjoiMmJlZTViZWQtNmZlZS00ZjM2LTkxNmQtNWUzYjRjZDI1MjQzIiwibWFzdGVyQWNjb3VudElkIjoiNGI1MmMwMGUiLCJjYXBhYmlsaXRpZXMiOlsibmV0d29yay1hcGktZmVhdHVyZXMiXSwiZXh0cmFDb25maWciOnsiY2FtYXJhU3RhdGUiOiJmb0ZyQndnOFNmeGMydnd2S1o5Y3UrMlgrT0s1K2FvOWhJTTVGUGZMQ1dOeUlMTHR3WmY1dFRKbDdUc1p4QnY4QWx3aHM2bFNWcGVvVkhoWngvM3hUenFRWVkwcHpIZE5XL085ZEdRN1RKOE9sU1lDdTFYYXFEcnNFbEF4WEJVcUpGdnZTTkp5a1A5ZDBYWVN4ajZFd0F6UUFsNGluQjE1c3VMRFNsKy82U1FDa29Udnpld0tvcFRZb0F5MVg2dDJVWXdEVWFDNjZuOS9kVWxIemN3V0NGK3QwOGNReGxZVUxKZyt3T0hwV2xvWGx1MGc3REx0SCtHd0pvRGJoYnMyT2hVY3BobGZqajBpeHQ1OTRsSG5sQ1NYNkZrMmhvWEhKUW01S3JtOVBKSmttK0xTRjVsRTd3NUxtWTRvYTFXSGpkY0dwV1VsQlNQY000YnprOGU0bVE9PSJ9fSwiZmVkZXJhdGVkQXNzZXJ0aW9ucyI6e30sImF1ZCI6ImFwaS1ldS52b25hZ2UuY29tIiwiZXhwIjoxNzE3MDkyODY4LCJqdGkiOiJmNDZhYTViOC1hODA2LTRjMzctODQyMS02OGYwMzJjNDlhMWYiLCJpYXQiOjE3MTcwOTE5NzAsImlzcyI6IlZJQU0tSUFQIiwibmJmIjoxNzE3MDkxOTU1fQ.iLUbyDPR1HGLKh29fy6fqK65Q1O7mjWOletAEPJD4eu7gb0E85EL4M9R7ckJq5lIvgedQt3vBheTaON9_u-VYjMqo8ulPoEoGUDHbOzNbs4MmCW0_CRdDPGyxnUhvcbuJhPgnEHxmfHjJBljncUnk-Z7XCgyNajBNXeQQnHkRF_6NMngxJ-qjjhqbYL0VsF_JS7-TXxixNL0KAFl0SeN2DjkfwRBCclP-69CTExDjyOvouAcchqi-6ZYj_tXPCrTADuzUrQrW8C5nHp2-XjWJSFKzyvi48n8V1U6KseV-eYzBzvy7bJf0tRMX7G6gctTYq3DxdC_eXvXlnp1zx16mg", 3 | "token_type": "bearer", 4 | "expires_in": 29 5 | } -------------------------------------------------------------------------------- /network_number_verification/BUILD: -------------------------------------------------------------------------------- 1 | resource(name='pyproject', source='pyproject.toml') 2 | file(name='readme', source='README.md') 3 | 4 | files(sources=['tests/data/*']) 5 | 6 | python_distribution( 7 | name='vonage-network-number-verification', 8 | dependencies=[ 9 | ':pyproject', 10 | ':readme', 11 | 'network_number_verification/src/vonage_network_number_verification', 12 | ], 13 | provides=python_artifact(), 14 | generate_setup=False, 15 | repositories=['@pypi'], 16 | ) 17 | -------------------------------------------------------------------------------- /network_number_verification/CHANGES.md: -------------------------------------------------------------------------------- 1 | # 1.0.2 2 | - Updated dependency versions 3 | 4 | # 1.0.1 5 | - Update dependency versions 6 | 7 | # 1.0.0 8 | - Initial upload -------------------------------------------------------------------------------- /network_number_verification/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "vonage-network-number-verification" 3 | dynamic = ["version"] 4 | description = "Package for working with the Vonage Number Verification Network API." 5 | readme = "README.md" 6 | authors = [{ name = "Vonage", email = "devrel@vonage.com" }] 7 | requires-python = ">=3.9" 8 | dependencies = [ 9 | "vonage-http-client>=1.5.0", 10 | "vonage-network-auth>=1.0.2", 11 | "vonage-utils>=1.1.4", 12 | "pydantic>=2.9.2", 13 | ] 14 | classifiers = [ 15 | "Programming Language :: Python", 16 | "Programming Language :: Python :: 3", 17 | "Programming Language :: Python :: 3.9", 18 | "Programming Language :: Python :: 3.10", 19 | "Programming Language :: Python :: 3.11", 20 | "Programming Language :: Python :: 3.12", 21 | "Programming Language :: Python :: 3.13", 22 | "License :: OSI Approved :: Apache Software License", 23 | ] 24 | 25 | [project.urls] 26 | Homepage = "https://github.com/Vonage/vonage-python-sdk" 27 | 28 | [tool.setuptools.dynamic] 29 | version = { attr = "vonage_network_number_verification._version.__version__" } 30 | 31 | [build-system] 32 | requires = ["setuptools>=61.0", "wheel"] 33 | build-backend = "setuptools.build_meta" 34 | -------------------------------------------------------------------------------- /network_number_verification/src/vonage_network_number_verification/BUILD: -------------------------------------------------------------------------------- 1 | python_sources() 2 | -------------------------------------------------------------------------------- /network_number_verification/src/vonage_network_number_verification/__init__.py: -------------------------------------------------------------------------------- 1 | from .errors import NetworkNumberVerificationError 2 | from .number_verification import CreateOidcUrl, NetworkNumberVerification 3 | from .requests import NumberVerificationRequest 4 | from .responses import NumberVerificationResponse 5 | 6 | __all__ = [ 7 | 'NetworkNumberVerification', 8 | 'CreateOidcUrl', 9 | 'NumberVerificationRequest', 10 | 'NumberVerificationResponse', 11 | 'NetworkNumberVerificationError', 12 | ] 13 | -------------------------------------------------------------------------------- /network_number_verification/src/vonage_network_number_verification/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = '1.0.2' 2 | -------------------------------------------------------------------------------- /network_number_verification/src/vonage_network_number_verification/errors.py: -------------------------------------------------------------------------------- 1 | from vonage_utils import VonageError 2 | 3 | 4 | class NetworkNumberVerificationError(VonageError): 5 | """Base class for Vonage Network Number Verification errors.""" 6 | -------------------------------------------------------------------------------- /network_number_verification/src/vonage_network_number_verification/requests.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field, model_validator 2 | from vonage_network_number_verification.errors import NetworkNumberVerificationError 3 | 4 | 5 | class NumberVerificationRequest(BaseModel): 6 | """Model for the request to verify a phone number. 7 | 8 | Args: 9 | code (str): The code returned from the OIDC redirect. 10 | redirect_uri (str): The URI to redirect to after authentication. 11 | phone_number (str): The phone number to verify. Use the E.164 format with 12 | or without a leading +. 13 | hashed_phone_number (str): The hashed phone number to verify. 14 | """ 15 | 16 | code: str 17 | redirect_uri: str 18 | phone_number: str = Field(None, serialization_alias='phoneNumber') 19 | hashed_phone_number: str = Field(None, serialization_alias='hashedPhoneNumber') 20 | 21 | @model_validator(mode='after') 22 | def check_only_one_phone_number(self): 23 | """Check that only one of `phone_number` and `hashed_phone_number` is set.""" 24 | 25 | if self.phone_number is not None and self.hashed_phone_number is not None: 26 | raise NetworkNumberVerificationError( 27 | 'Only one of `phone_number` and `hashed_phone_number` can be set.' 28 | ) 29 | 30 | if self.phone_number is None and self.hashed_phone_number is None: 31 | raise NetworkNumberVerificationError( 32 | 'One of `phone_number` and `hashed_phone_number` must be set.' 33 | ) 34 | 35 | return self 36 | -------------------------------------------------------------------------------- /network_number_verification/src/vonage_network_number_verification/responses.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class NumberVerificationResponse(BaseModel): 5 | """Model for the response from the Number Verification API. 6 | 7 | Args: 8 | device_phone_number_verified (bool): Whether the phone number has been 9 | successfully verified. 10 | """ 11 | 12 | device_phone_number_verified: bool = Field( 13 | ..., validation_alias='devicePhoneNumberVerified' 14 | ) 15 | -------------------------------------------------------------------------------- /network_number_verification/tests/BUILD: -------------------------------------------------------------------------------- 1 | python_tests(dependencies=['network_number_verification', 'testutils']) 2 | -------------------------------------------------------------------------------- /network_number_verification/tests/data/token_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "access_token": "eyJhbGciOiJSUzI1NiIsImprdSI6Imh0dHBzOi8vYW51YmlzLWNlcnRzLWMxLWV1dzEucHJvZC52MS52b25hZ2VuZXR3b3Jrcy5uZXQvandrcyIsImtpZCI6IkNOPVZvbmFnZSAxdmFwaWd3IEludGVybmFsIENBOjoxOTUxODQ2ODA3NDg1NTYwNjYzODY3MTM0NjE2MjU2MTU5MjU2NDkiLCJ0eXAiOiJKV1QiLCJ4NXUiOiJodHRwczovL2FudWJpcy1jZXJ0cy1jMS1ldXcxLnByb2QudjEudm9uYWdlbmV0d29ya3MubmV0L3YxL2NlcnRzLzA4NjliNDMyZTEzZmIyMzcwZTk2ZGI4YmUxMDc4MjJkIn0.eyJwcmluY2lwYWwiOnsiYXBpS2V5IjoiNGI1MmMwMGUiLCJhcHBsaWNhdGlvbklkIjoiMmJlZTViZWQtNmZlZS00ZjM2LTkxNmQtNWUzYjRjZDI1MjQzIiwibWFzdGVyQWNjb3VudElkIjoiNGI1MmMwMGUiLCJjYXBhYmlsaXRpZXMiOlsibmV0d29yay1hcGktZmVhdHVyZXMiXSwiZXh0cmFDb25maWciOnsiY2FtYXJhU3RhdGUiOiJmb0ZyQndnOFNmeGMydnd2S1o5Y3UrMlgrT0s1K2FvOWhJTTVGUGZMQ1dOeUlMTHR3WmY1dFRKbDdUc1p4QnY4QWx3aHM2bFNWcGVvVkhoWngvM3hUenFRWVkwcHpIZE5XL085ZEdRN1RKOE9sU1lDdTFYYXFEcnNFbEF4WEJVcUpGdnZTTkp5a1A5ZDBYWVN4ajZFd0F6UUFsNGluQjE1c3VMRFNsKy82U1FDa29Udnpld0tvcFRZb0F5MVg2dDJVWXdEVWFDNjZuOS9kVWxIemN3V0NGK3QwOGNReGxZVUxKZyt3T0hwV2xvWGx1MGc3REx0SCtHd0pvRGJoYnMyT2hVY3BobGZqajBpeHQ1OTRsSG5sQ1NYNkZrMmhvWEhKUW01S3JtOVBKSmttK0xTRjVsRTd3NUxtWTRvYTFXSGpkY0dwV1VsQlNQY000YnprOGU0bVE9PSJ9fSwiZmVkZXJhdGVkQXNzZXJ0aW9ucyI6e30sImF1ZCI6ImFwaS1ldS52b25hZ2UuY29tIiwiZXhwIjoxNzE3MDkyODY4LCJqdGkiOiJmNDZhYTViOC1hODA2LTRjMzctODQyMS02OGYwMzJjNDlhMWYiLCJpYXQiOjE3MTcwOTE5NzAsImlzcyI6IlZJQU0tSUFQIiwibmJmIjoxNzE3MDkxOTU1fQ.iLUbyDPR1HGLKh29fy6fqK65Q1O7mjWOletAEPJD4eu7gb0E85EL4M9R7ckJq5lIvgedQt3vBheTaON9_u-VYjMqo8ulPoEoGUDHbOzNbs4MmCW0_CRdDPGyxnUhvcbuJhPgnEHxmfHjJBljncUnk-Z7XCgyNajBNXeQQnHkRF_6NMngxJ-qjjhqbYL0VsF_JS7-TXxixNL0KAFl0SeN2DjkfwRBCclP-69CTExDjyOvouAcchqi-6ZYj_tXPCrTADuzUrQrW8C5nHp2-XjWJSFKzyvi48n8V1U6KseV-eYzBzvy7bJf0tRMX7G6gctTYq3DxdC_eXvXlnp1zx16mg", 3 | "token_type": "bearer", 4 | "expires_in": 29 5 | } -------------------------------------------------------------------------------- /network_number_verification/tests/data/verify_number.json: -------------------------------------------------------------------------------- 1 | { 2 | "devicePhoneNumberVerified": true 3 | } -------------------------------------------------------------------------------- /network_sim_swap/BUILD: -------------------------------------------------------------------------------- 1 | resource(name='pyproject', source='pyproject.toml') 2 | file(name='readme', source='README.md') 3 | 4 | files(sources=['tests/data/*']) 5 | 6 | python_distribution( 7 | name='vonage-network-sim-swap', 8 | dependencies=[ 9 | ':pyproject', 10 | ':readme', 11 | 'network_sim_swap/src/vonage_network_sim_swap', 12 | ], 13 | provides=python_artifact(), 14 | generate_setup=False, 15 | repositories=['@pypi'], 16 | ) 17 | -------------------------------------------------------------------------------- /network_sim_swap/CHANGES.md: -------------------------------------------------------------------------------- 1 | # 1.1.2 2 | - Updated dependency versions 3 | 4 | # 1.1.1 5 | - Update dependency versions 6 | 7 | # 1.1.0 8 | - Add new model `SimSwapCheckRequest` to replace arguments in the `SimSwap.check` method 9 | 10 | # 1.0.0 11 | - Support for Python 3.13, drop support for 3.8 12 | 13 | # 0.1.1b0 14 | - Add docstrings to data models 15 | 16 | # 0.1.0b0 17 | - Initial upload -------------------------------------------------------------------------------- /network_sim_swap/README.md: -------------------------------------------------------------------------------- 1 | # Vonage Sim Swap Network API Client 2 | 3 | This package (`vonage-network-sim-swap`) allows you to check whether a SIM card has been swapped, and the last swap date. 4 | 5 | This package is not intended to be used directly, instead being accessed from an enclosing SDK package. Thus, it doesn't require manual installation or configuration unless you're using this package independently of an SDK. 6 | 7 | For full API documentation, refer to the [Vonage developer documentation](https://developer.vonage.com). 8 | 9 | ## Registering to Use the Sim Swap API 10 | 11 | To use this API, you must first create and register your business profile with the Vonage Network Registry. [This documentation page](https://developer.vonage.com/en/getting-started-network/registration) explains how this can be done. You need to obtain approval for each network and region you want to use the APIs in. 12 | 13 | ## Installation 14 | 15 | Install from the Python Package Index with pip: 16 | 17 | ```bash 18 | pip install vonage-network-sim-swap 19 | ``` 20 | 21 | ## Usage 22 | 23 | It is recommended to use this as part of the `vonage` package. The examples below assume you've created an instance of the `vonage.Vonage` class called `vonage_client`. 24 | 25 | ### Check if a SIM Has Been Swapped 26 | 27 | ```python 28 | from vonage_network_sim_swap import SwapStatus 29 | swap_status: SwapStatus = vonage_client.sim_swap.check(phone_number='MY_NUMBER') 30 | print(swap_status.swapped) 31 | ``` 32 | 33 | ### Get the Date of the Last SIM Swap 34 | 35 | ```python 36 | from vonage_network_sim_swap import LastSwapDate 37 | swap_date: LastSwapDate = vonage_client.sim_swap.get_last_swap_date 38 | print(swap_date.last_swap_date) 39 | ``` -------------------------------------------------------------------------------- /network_sim_swap/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "vonage-network-sim-swap" 3 | dynamic = ["version"] 4 | description = "Package for working with the Vonage Sim Swap Network API." 5 | readme = "README.md" 6 | authors = [{ name = "Vonage", email = "devrel@vonage.com" }] 7 | requires-python = ">=3.9" 8 | dependencies = [ 9 | "vonage-http-client>=1.5.0", 10 | "vonage-network-auth>=1.0.2", 11 | "vonage-utils>=1.1.4", 12 | "pydantic>=2.9.2", 13 | ] 14 | classifiers = [ 15 | "Programming Language :: Python", 16 | "Programming Language :: Python :: 3", 17 | "Programming Language :: Python :: 3.9", 18 | "Programming Language :: Python :: 3.10", 19 | "Programming Language :: Python :: 3.11", 20 | "Programming Language :: Python :: 3.12", 21 | "Programming Language :: Python :: 3.13", 22 | "License :: OSI Approved :: Apache Software License", 23 | ] 24 | 25 | [project.urls] 26 | Homepage = "https://github.com/Vonage/vonage-python-sdk" 27 | 28 | [tool.setuptools.dynamic] 29 | version = { attr = "vonage_network_sim_swap._version.__version__" } 30 | 31 | [build-system] 32 | requires = ["setuptools>=61.0", "wheel"] 33 | build-backend = "setuptools.build_meta" 34 | -------------------------------------------------------------------------------- /network_sim_swap/src/vonage_network_sim_swap/BUILD: -------------------------------------------------------------------------------- 1 | python_sources() 2 | -------------------------------------------------------------------------------- /network_sim_swap/src/vonage_network_sim_swap/__init__.py: -------------------------------------------------------------------------------- 1 | from .requests import SimSwapCheckRequest 2 | from .responses import LastSwapDate, SwapStatus 3 | from .sim_swap import NetworkSimSwap 4 | 5 | __all__ = ['NetworkSimSwap', 'LastSwapDate', 'SimSwapCheckRequest', 'SwapStatus'] 6 | -------------------------------------------------------------------------------- /network_sim_swap/src/vonage_network_sim_swap/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = '1.1.2' 2 | -------------------------------------------------------------------------------- /network_sim_swap/src/vonage_network_sim_swap/requests.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from pydantic import BaseModel, Field 4 | 5 | 6 | class SimSwapCheckRequest(BaseModel): 7 | """Request model to check if a SIM has been swapped using the Vonage Sim Swap Network 8 | API. 9 | 10 | Args: 11 | phone_number (str): The phone number to check. Use the E.164 format with 12 | or without a leading +. 13 | max_age (int, optional): Period in hours to be checked for SIM swap. 14 | """ 15 | 16 | phone_number: str = Field(..., serialization_alias='phoneNumber') 17 | max_age: Optional[int] = Field(None, serialization_alias='maxAge') 18 | -------------------------------------------------------------------------------- /network_sim_swap/src/vonage_network_sim_swap/responses.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class SwapStatus(BaseModel): 5 | """Model for the status of a SIM swap. 6 | 7 | Args: 8 | swapped (str): Indicates whether the SIM card has been swapped during the period 9 | within the `max_age` provided in the request. 10 | """ 11 | 12 | swapped: str 13 | 14 | 15 | class LastSwapDate(BaseModel): 16 | """Model for the last SIM swap date information. 17 | 18 | Args: 19 | last_swap_date (str): The timestamp of the latest SIM swap performed. 20 | """ 21 | 22 | last_swap_date: str = Field(..., validation_alias='latestSimChange') 23 | -------------------------------------------------------------------------------- /network_sim_swap/tests/BUILD: -------------------------------------------------------------------------------- 1 | python_tests(dependencies=['network_sim_swap', 'testutils']) 2 | -------------------------------------------------------------------------------- /network_sim_swap/tests/data/check_sim_swap.json: -------------------------------------------------------------------------------- 1 | { 2 | "swapped": true 3 | } -------------------------------------------------------------------------------- /network_sim_swap/tests/data/get_swap_date.json: -------------------------------------------------------------------------------- 1 | { 2 | "latestSimChange": "2023-12-22T04:00:44.000Z" 3 | } -------------------------------------------------------------------------------- /network_sim_swap/tests/test_sim_swap.py: -------------------------------------------------------------------------------- 1 | from os.path import abspath 2 | from unittest.mock import MagicMock, patch 3 | 4 | import responses 5 | from vonage_http_client.http_client import HttpClient 6 | from vonage_network_sim_swap import NetworkSimSwap 7 | from vonage_network_sim_swap.requests import SimSwapCheckRequest 8 | 9 | from testutils import build_response, get_mock_jwt_auth 10 | 11 | path = abspath(__file__) 12 | 13 | sim_swap = NetworkSimSwap(HttpClient(get_mock_jwt_auth())) 14 | 15 | 16 | def test_http_client_property(): 17 | http_client = sim_swap.http_client 18 | assert isinstance(http_client, HttpClient) 19 | 20 | 21 | @patch('vonage_network_auth.NetworkAuth.get_sim_swap_camara_token') 22 | @responses.activate 23 | def test_check_sim_swap(mock_get_oauth2_user_token: MagicMock): 24 | build_response( 25 | path, 26 | 'POST', 27 | 'https://api-eu.vonage.com/camara/sim-swap/v040/check', 28 | 'check_sim_swap.json', 29 | ) 30 | mock_get_oauth2_user_token.return_value = 'token' 31 | 32 | response = sim_swap.check( 33 | SimSwapCheckRequest(phone_number='447700900000', max_age=24) 34 | ) 35 | 36 | assert response['swapped'] == True 37 | 38 | 39 | @patch('vonage_network_auth.NetworkAuth.get_sim_swap_camara_token') 40 | @responses.activate 41 | def test_get_last_swap_date(mock_get_oauth2_user_token: MagicMock): 42 | build_response( 43 | path, 44 | 'POST', 45 | 'https://api-eu.vonage.com/camara/sim-swap/v040/retrieve-date', 46 | 'get_swap_date.json', 47 | ) 48 | mock_get_oauth2_user_token.return_value = 'token' 49 | 50 | response = sim_swap.get_last_swap_date('447700900000') 51 | 52 | assert response['latestSimChange'] == '2023-12-22T04:00:44.000Z' 53 | -------------------------------------------------------------------------------- /number_insight/BUILD: -------------------------------------------------------------------------------- 1 | resource(name='pyproject', source='pyproject.toml') 2 | file(name='readme', source='README.md') 3 | 4 | files(sources=['tests/data/*']) 5 | 6 | python_distribution( 7 | name='vonage-number-insight', 8 | dependencies=[ 9 | ':pyproject', 10 | ':readme', 11 | 'number_insight/src/vonage_number_insight', 12 | ], 13 | provides=python_artifact(), 14 | generate_setup=False, 15 | repositories=['@pypi'], 16 | ) 17 | -------------------------------------------------------------------------------- /number_insight/CHANGES.md: -------------------------------------------------------------------------------- 1 | # 1.0.7 2 | - Use basic header auth instead of request body auth 3 | 4 | # 1.0.6 5 | - Updated dependency versions 6 | 7 | # 1.0.5 8 | - Fix missed method renaming 9 | - Docstring update 10 | 11 | # 1.0.4 12 | - Update dependency versions 13 | 14 | # 1.0.3 15 | - Rename `basic_number_insight` -> `get_basic_info`, `standard_number_insight` -> `get_standard_info`, `advanced_async_number_insight` -> `get_advanced_info_async`, `advanced_sync_number_insight` -> `get_advanced_info_sync` 16 | 17 | # 1.0.2 18 | - Support for Python 3.13, drop support for 3.8 19 | 20 | # 1.0.1 21 | - Add docstrings to data models 22 | 23 | # 1.0.0 24 | - Initial upload 25 | -------------------------------------------------------------------------------- /number_insight/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = 'vonage-number-insight' 3 | dynamic = ["version"] 4 | description = 'Vonage Number Insight package' 5 | readme = "README.md" 6 | authors = [{ name = "Vonage", email = "devrel@vonage.com" }] 7 | requires-python = ">=3.9" 8 | dependencies = [ 9 | "vonage-http-client>=1.5.0", 10 | "vonage-utils>=1.1.4", 11 | "pydantic>=2.9.2", 12 | ] 13 | classifiers = [ 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.9", 17 | "Programming Language :: Python :: 3.10", 18 | "Programming Language :: Python :: 3.11", 19 | "Programming Language :: Python :: 3.12", 20 | "Programming Language :: Python :: 3.13", 21 | "License :: OSI Approved :: Apache Software License", 22 | ] 23 | 24 | [project.urls] 25 | homepage = "https://github.com/Vonage/vonage-python-sdk" 26 | 27 | [tool.setuptools.dynamic] 28 | version = { attr = "vonage_number_insight._version.__version__" } 29 | 30 | [build-system] 31 | requires = ["setuptools>=61.0", "wheel"] 32 | build-backend = "setuptools.build_meta" 33 | -------------------------------------------------------------------------------- /number_insight/src/vonage_number_insight/BUILD: -------------------------------------------------------------------------------- 1 | python_sources() 2 | -------------------------------------------------------------------------------- /number_insight/src/vonage_number_insight/__init__.py: -------------------------------------------------------------------------------- 1 | from . import errors 2 | from .number_insight import NumberInsight 3 | from .requests import ( 4 | AdvancedAsyncInsightRequest, 5 | AdvancedSyncInsightRequest, 6 | BasicInsightRequest, 7 | StandardInsightRequest, 8 | ) 9 | from .responses import ( 10 | AdvancedAsyncInsightResponse, 11 | AdvancedSyncInsightResponse, 12 | BasicInsightResponse, 13 | CallerIdentity, 14 | Carrier, 15 | RoamingStatus, 16 | StandardInsightResponse, 17 | ) 18 | 19 | __all__ = [ 20 | 'NumberInsight', 21 | 'BasicInsightRequest', 22 | 'StandardInsightRequest', 23 | 'AdvancedAsyncInsightRequest', 24 | 'AdvancedSyncInsightRequest', 25 | 'BasicInsightResponse', 26 | 'CallerIdentity', 27 | 'Carrier', 28 | 'RoamingStatus', 29 | 'StandardInsightResponse', 30 | 'AdvancedSyncInsightResponse', 31 | 'AdvancedAsyncInsightResponse', 32 | 'errors', 33 | ] 34 | -------------------------------------------------------------------------------- /number_insight/src/vonage_number_insight/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = '1.0.7' 2 | -------------------------------------------------------------------------------- /number_insight/src/vonage_number_insight/errors.py: -------------------------------------------------------------------------------- 1 | from vonage_utils.errors import VonageError 2 | 3 | 4 | class NumberInsightError(VonageError): 5 | """Indicates an error when using the Vonage Number Insight API.""" 6 | -------------------------------------------------------------------------------- /number_insight/src/vonage_number_insight/requests.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from pydantic import BaseModel 4 | from vonage_utils.types import PhoneNumber 5 | 6 | 7 | class BasicInsightRequest(BaseModel): 8 | """Model for a basic number insight request. 9 | 10 | Args: 11 | number (PhoneNumber): The phone number to get insight information for. 12 | country (str, Optional): The country code for the phone number. 13 | """ 14 | 15 | number: PhoneNumber 16 | country: Optional[str] = None 17 | 18 | 19 | class StandardInsightRequest(BasicInsightRequest): 20 | """Model for a standard number insight request. 21 | 22 | Args: 23 | number (PhoneNumber): The phone number to get insight information for. 24 | country (str, Optional): The country code for the phone number. 25 | cnam (bool, Optional): Whether to include the Caller ID Name (CNAM) with the response. 26 | """ 27 | 28 | cnam: Optional[bool] = None 29 | 30 | 31 | class AdvancedAsyncInsightRequest(StandardInsightRequest): 32 | """Model for an advanced asynchronous number insight request. 33 | 34 | Args: 35 | number (PhoneNumber): The phone number to get insight information for. 36 | callback (str): The URL to send the asynchronous response to. 37 | country (str, Optional): The country code for the phone number. 38 | cnam (bool, Optional): Whether to include the Caller ID Name (CNAM) with the response. 39 | """ 40 | 41 | callback: str 42 | 43 | 44 | class AdvancedSyncInsightRequest(StandardInsightRequest): 45 | """Model for an advanced synchronous number insight request. 46 | 47 | Args: 48 | number (PhoneNumber): The phone number to get insight information for. 49 | country (str, Optional): The country code for the phone number. 50 | cnam (bool, Optional): Whether to include the Caller ID Name (CNAM) with the response. 51 | """ 52 | -------------------------------------------------------------------------------- /number_insight/tests/BUILD: -------------------------------------------------------------------------------- 1 | python_tests(dependencies=['number_insight', 'testutils']) 2 | -------------------------------------------------------------------------------- /number_insight/tests/data/advanced_async_insight.json: -------------------------------------------------------------------------------- 1 | { 2 | "number": "447700900000", 3 | "remaining_balance": "32.92665294", 4 | "request_id": "434205b5-90ec-4ee2-a337-7b40d9683420", 5 | "request_price": "0.04000000", 6 | "status": 0 7 | } -------------------------------------------------------------------------------- /number_insight/tests/data/advanced_async_insight_error.json: -------------------------------------------------------------------------------- 1 | { 2 | "error_text": "Invalid credentials", 3 | "status": 4 4 | } -------------------------------------------------------------------------------- /number_insight/tests/data/advanced_async_insight_partial_error.json: -------------------------------------------------------------------------------- 1 | { 2 | "error_text": "Live mobile lookup not returned", 3 | "status": 43, 4 | "number": "447700900000", 5 | "remaining_balance": "32.92665294", 6 | "request_id": "434205b5-90ec-4ee2-a337-7b40d9683420", 7 | "request_price": "0.04000000" 8 | } -------------------------------------------------------------------------------- /number_insight/tests/data/advanced_sync_insight.json: -------------------------------------------------------------------------------- 1 | { 2 | "caller_identity": { 3 | "caller_name": "John Smith", 4 | "caller_type": "consumer", 5 | "first_name": "John", 6 | "last_name": "Smith", 7 | "subscription_type": "postpaid" 8 | }, 9 | "caller_name": "John Smith", 10 | "caller_type": "consumer", 11 | "country_code": "US", 12 | "country_code_iso3": "USA", 13 | "country_name": "United States of America", 14 | "country_prefix": "1", 15 | "current_carrier": { 16 | "country": "US", 17 | "name": "AT&T Mobility", 18 | "network_code": "310090", 19 | "network_type": "mobile" 20 | }, 21 | "first_name": "John", 22 | "international_format_number": "12345678900", 23 | "ip_warnings": "unknown", 24 | "last_name": "Smith", 25 | "lookup_outcome": 1, 26 | "lookup_outcome_message": "Partial success - some fields populated", 27 | "national_format_number": "(234) 567-8900", 28 | "original_carrier": { 29 | "country": "US", 30 | "name": "AT&T Mobility", 31 | "network_code": "310090", 32 | "network_type": "mobile" 33 | }, 34 | "ported": "not_ported", 35 | "reachable": "unknown", 36 | "refund_price": "0.01025000", 37 | "remaining_balance": "32.68590294", 38 | "request_id": "97e973e7-2e27-4fd3-9e1a-972ea14dd992", 39 | "request_price": "0.05025000", 40 | "roaming": "unknown", 41 | "status": 44, 42 | "status_message": "Lookup Handler unable to handle request", 43 | "valid_number": "valid" 44 | } -------------------------------------------------------------------------------- /number_insight/tests/data/basic_insight.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": 0, 3 | "status_message": "Success", 4 | "request_id": "7f4a8a16-aa89-4078-b0ae-7743da34aca5", 5 | "international_format_number": "12345678900", 6 | "national_format_number": "(234) 567-8900", 7 | "country_code": "US", 8 | "country_code_iso3": "USA", 9 | "country_name": "United States of America", 10 | "country_prefix": "1" 11 | } -------------------------------------------------------------------------------- /number_insight/tests/data/basic_insight_error.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": 3, 3 | "status_message": "Invalid request :: Not valid number format detected [ 145645562 ]" 4 | } -------------------------------------------------------------------------------- /number_insight/tests/data/standard_insight.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": 0, 3 | "status_message": "Success", 4 | "request_id": "1d56406b-9d52-497a-a023-b3f40b62f9b3", 5 | "international_format_number": "447700900000", 6 | "national_format_number": "07700 900000", 7 | "country_code": "GB", 8 | "country_code_iso3": "GBR", 9 | "country_name": "United Kingdom", 10 | "country_prefix": "44", 11 | "request_price": "0.00500000", 12 | "remaining_balance": "32.98665294", 13 | "current_carrier": { 14 | "network_code": "23415", 15 | "name": "Vodafone Limited", 16 | "country": "GB", 17 | "network_type": "mobile" 18 | }, 19 | "original_carrier": { 20 | "network_code": "23420", 21 | "name": "Hutchison 3G Ltd", 22 | "country": "GB", 23 | "network_type": "mobile" 24 | }, 25 | "ported": "ported", 26 | "caller_identity": { 27 | "caller_type": "consumer", 28 | "caller_name": "John Smith", 29 | "first_name": "John", 30 | "last_name": "Smith" 31 | }, 32 | "caller_name": "John Smith", 33 | "last_name": "Smith", 34 | "first_name": "John", 35 | "caller_type": "consumer" 36 | } -------------------------------------------------------------------------------- /number_management/BUILD: -------------------------------------------------------------------------------- 1 | resource(name='pyproject', source='pyproject.toml') 2 | file(name='readme', source='README.md') 3 | 4 | files(sources=['tests/data/*']) 5 | 6 | python_distribution( 7 | name='vonage-numbers', 8 | dependencies=[ 9 | ':pyproject', 10 | ':readme', 11 | 'number_management/src/vonage_numbers', 12 | ], 13 | provides=python_artifact(), 14 | generate_setup=False, 15 | repositories=['@pypi'], 16 | ) 17 | -------------------------------------------------------------------------------- /number_management/CHANGES.md: -------------------------------------------------------------------------------- 1 | # 1.0.4 2 | - Updated dependency versions 3 | 4 | # 1.0.3 5 | - Update dependency versions 6 | 7 | # 1.0.2 8 | - Support for Python 3.13, drop support for 3.8 9 | 10 | # 1.0.1 11 | - Add docstrings for data models 12 | 13 | # 1.0.0 14 | - Initial upload 15 | -------------------------------------------------------------------------------- /number_management/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = 'vonage-numbers' 3 | dynamic = ["version"] 4 | description = 'Vonage Numbers package' 5 | readme = "README.md" 6 | authors = [{ name = "Vonage", email = "devrel@vonage.com" }] 7 | requires-python = ">=3.9" 8 | dependencies = [ 9 | "vonage-http-client>=1.5.0", 10 | "vonage-utils>=1.1.4", 11 | "pydantic>=2.9.2", 12 | ] 13 | classifiers = [ 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.9", 17 | "Programming Language :: Python :: 3.10", 18 | "Programming Language :: Python :: 3.11", 19 | "Programming Language :: Python :: 3.12", 20 | "Programming Language :: Python :: 3.13", 21 | "License :: OSI Approved :: Apache Software License", 22 | ] 23 | 24 | [project.urls] 25 | homepage = "https://github.com/Vonage/vonage-python-sdk" 26 | 27 | [tool.setuptools.dynamic] 28 | version = { attr = "vonage_numbers._version.__version__" } 29 | 30 | [build-system] 31 | requires = ["setuptools>=61.0", "wheel"] 32 | build-backend = "setuptools.build_meta" 33 | -------------------------------------------------------------------------------- /number_management/src/vonage_numbers/BUILD: -------------------------------------------------------------------------------- 1 | python_sources() 2 | -------------------------------------------------------------------------------- /number_management/src/vonage_numbers/__init__.py: -------------------------------------------------------------------------------- 1 | from .enums import NumberFeatures, NumberType, VoiceCallbackType 2 | from .errors import NumbersError 3 | from .number_management import Numbers 4 | from .requests import ( 5 | ListOwnedNumbersFilter, 6 | NumberParams, 7 | SearchAvailableNumbersFilter, 8 | UpdateNumberParams, 9 | ) 10 | from .responses import AvailableNumber, NumbersStatus, OwnedNumber 11 | 12 | __all__ = [ 13 | 'NumberFeatures', 14 | 'NumberType', 15 | 'VoiceCallbackType', 16 | 'NumbersError', 17 | 'Numbers', 18 | 'ListOwnedNumbersFilter', 19 | 'NumberParams', 20 | 'SearchAvailableNumbersFilter', 21 | 'UpdateNumberParams', 22 | 'AvailableNumber', 23 | 'NumbersStatus', 24 | 'OwnedNumber', 25 | ] 26 | -------------------------------------------------------------------------------- /number_management/src/vonage_numbers/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = '1.0.4' 2 | -------------------------------------------------------------------------------- /number_management/src/vonage_numbers/enums.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class NumberType(str, Enum): 5 | LANDLINE = 'landline' 6 | MOBILE_LVN = 'mobile-lvn' 7 | LANDLINE_TOLL_FREE = 'landline-toll-free' 8 | 9 | 10 | class NumberFeatures(str, Enum): 11 | SMS = 'SMS' 12 | VOICE = 'VOICE' 13 | MMS = 'MMS' 14 | SMS_VOICE = 'SMS,VOICE' 15 | SMS_MMS = 'SMS,MMS' 16 | VOICE_MMS = 'VOICE,MMS' 17 | SMS_VOICE_MMS = 'SMS,VOICE,MMS' 18 | 19 | 20 | class VoiceCallbackType(str, Enum): 21 | SIP = 'sip' 22 | TEL = 'tel' 23 | -------------------------------------------------------------------------------- /number_management/src/vonage_numbers/errors.py: -------------------------------------------------------------------------------- 1 | from vonage_utils.errors import VonageError 2 | 3 | 4 | class NumbersError(VonageError): 5 | """Indicates an error with the Numbers API package.""" 6 | -------------------------------------------------------------------------------- /number_management/tests/BUILD: -------------------------------------------------------------------------------- 1 | python_tests(dependencies=['number_management', 'testutils']) 2 | -------------------------------------------------------------------------------- /number_management/tests/data/list_owned_numbers_basic.json: -------------------------------------------------------------------------------- 1 | { 2 | "count": 2, 3 | "numbers": [ 4 | { 5 | "country": "ES", 6 | "msisdn": "3400000000", 7 | "type": "mobile-lvn", 8 | "features": [ 9 | "SMS" 10 | ] 11 | }, 12 | { 13 | "country": "GB", 14 | "msisdn": "447007000000", 15 | "type": "mobile-lvn", 16 | "features": [ 17 | "VOICE", 18 | "SMS" 19 | ], 20 | "voiceCallbackType": "app", 21 | "voiceCallbackValue": "29f769u7-7ce1-46c9-ade3-f2dedee4fr4t", 22 | "app_id": "29f769u7-7ce1-46c9-ade3-f2dedee4fr4t" 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /number_management/tests/data/list_owned_numbers_filter.json: -------------------------------------------------------------------------------- 1 | { 2 | "count": 1, 3 | "numbers": [ 4 | { 5 | "country": "GB", 6 | "msisdn": "447007000000", 7 | "type": "mobile-lvn", 8 | "features": [ 9 | "VOICE", 10 | "SMS" 11 | ], 12 | "voiceCallbackType": "app", 13 | "voiceCallbackValue": "29f769u7-7ce1-46c9-ade3-f2dedee4fr4t", 14 | "app_id": "29f769u7-7ce1-46c9-ade3-f2dedee4fr4t" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /number_management/tests/data/list_owned_numbers_subset.json: -------------------------------------------------------------------------------- 1 | { 2 | "count": 2, 3 | "numbers": [ 4 | { 5 | "country": "ES", 6 | "msisdn": "3400000000", 7 | "type": "mobile-lvn", 8 | "features": [ 9 | "SMS" 10 | ] 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /number_management/tests/data/no_number.json: -------------------------------------------------------------------------------- 1 | { 2 | "error-code": "420", 3 | "error-code-label": "method failed" 4 | } -------------------------------------------------------------------------------- /number_management/tests/data/nothing.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /number_management/tests/data/number.json: -------------------------------------------------------------------------------- 1 | { 2 | "error-code": "200", 3 | "error-code-label": "success" 4 | } -------------------------------------------------------------------------------- /number_management/tests/data/search_available_numbers_basic.json: -------------------------------------------------------------------------------- 1 | { 2 | "count": 8353, 3 | "numbers": [ 4 | { 5 | "country": "GB", 6 | "msisdn": "442039050911", 7 | "cost": "1.00", 8 | "type": "landline", 9 | "features": [ 10 | "VOICE" 11 | ] 12 | }, 13 | { 14 | "country": "GB", 15 | "msisdn": "442039051911", 16 | "cost": "1.00", 17 | "type": "landline", 18 | "features": [ 19 | "VOICE" 20 | ] 21 | }, 22 | { 23 | "country": "GB", 24 | "msisdn": "442039052911", 25 | "cost": "1.00", 26 | "type": "landline", 27 | "features": [ 28 | "VOICE" 29 | ] 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /number_management/tests/data/search_available_numbers_end_of_list.json: -------------------------------------------------------------------------------- 1 | { 2 | "count": 1, 3 | "numbers": [ 4 | { 5 | "country": "GB", 6 | "msisdn": "442039055555", 7 | "cost": "0.80", 8 | "type": "mobile-lvn", 9 | "features": [ 10 | "VOICE", 11 | "SMS" 12 | ] 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /number_management/tests/data/search_available_numbers_filter.json: -------------------------------------------------------------------------------- 1 | { 2 | "count": 2, 3 | "numbers": [ 4 | { 5 | "country": "GB", 6 | "msisdn": "442039055555", 7 | "cost": "1.00", 8 | "type": "landline", 9 | "features": [ 10 | "VOICE" 11 | ] 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /pants.ci.toml: -------------------------------------------------------------------------------- 1 | [GLOBAL] 2 | colors = true 3 | 4 | [python] 5 | interpreter_constraints = ['>=3.9'] 6 | -------------------------------------------------------------------------------- /pants.toml: -------------------------------------------------------------------------------- 1 | [GLOBAL] 2 | pants_version = '2.26.0' 3 | 4 | backend_packages = [ 5 | 'pants.backend.python', 6 | 'pants.backend.python.lint.autoflake', 7 | 'pants.backend.build_files.fmt.black', 8 | 'pants.backend.python.lint.isort', 9 | 'pants.backend.python.lint.black', 10 | 'pants.backend.python.lint.docformatter', 11 | 'pants.backend.tools.taplo', 12 | "pants.backend.experimental.python", 13 | ] 14 | 15 | pants_ignore.add = ['!_test_scripts/', '!_dev_scripts/'] 16 | 17 | [anonymous-telemetry] 18 | enabled = false 19 | 20 | [source] 21 | root_patterns = ['/', 'src/', 'tests/'] 22 | 23 | [python] 24 | interpreter_constraints = ['==3.13.*'] 25 | 26 | [pytest] 27 | args = ['-vv', '--no-header'] 28 | 29 | [coverage-py] 30 | interpreter_constraints = ['>=3.9'] 31 | report = ['html', 'console'] 32 | 33 | [black] 34 | args = ['--line-length=90', '--skip-string-normalization'] 35 | interpreter_constraints = ['>=3.9'] 36 | 37 | [isort] 38 | args = ['--profile=black', '--line-length=90'] 39 | interpreter_constraints = ['>=3.9'] 40 | 41 | [docformatter] 42 | args = ['--wrap-summaries=90', '--wrap-descriptions=90'] 43 | interpreter_constraints = ['>=3.9'] 44 | 45 | [autoflake] 46 | interpreter_constraints = ['>=3.9'] 47 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.coverage.run] 2 | omit = ['**/tests/*', '**/src/**/_version.py'] 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pytest>=8.0.0 2 | requests>=2.31.0 3 | responses>=0.24.1 4 | pydantic>=2.9.2 5 | typing-extensions>=4.9.0 6 | pyjwt[crypto]>=1.6.4 7 | toml>=0.10.2 8 | urllib3 9 | 10 | -e jwt 11 | -e http_client 12 | -e account 13 | -e application 14 | -e messages 15 | -e network_auth 16 | -e network_number_verification 17 | -e network_sim_swap 18 | -e number_insight 19 | -e number_management 20 | -e sms 21 | -e subaccounts 22 | -e users 23 | -e verify 24 | -e verify_legacy 25 | -e video 26 | -e voice 27 | -e vonage_utils 28 | -e vonage 29 | -------------------------------------------------------------------------------- /sms/BUILD: -------------------------------------------------------------------------------- 1 | resource(name='pyproject', source='pyproject.toml') 2 | file(name='readme', source='README.md') 3 | 4 | files(sources=['tests/data/*']) 5 | 6 | python_distribution( 7 | name='vonage-sms', 8 | dependencies=[ 9 | ':pyproject', 10 | ':readme', 11 | 'sms/src/vonage_sms', 12 | ], 13 | provides=python_artifact(), 14 | generate_setup=False, 15 | repositories=['@pypi'], 16 | ) 17 | -------------------------------------------------------------------------------- /sms/CHANGES.md: -------------------------------------------------------------------------------- 1 | # 1.1.6 2 | - Make returned response fields optional 3 | 4 | # 1.1.5 5 | - Updated dependency versions 6 | 7 | # 1.1.4 8 | - Update dependency versions 9 | 10 | # 1.1.3 11 | - Support for Python 3.13, drop support for 3.8 12 | 13 | # 1.1.2 14 | - Add docstrings to data models 15 | 16 | # 1.1.1 17 | - Update minimum dependency version 18 | 19 | # 1.1.0 20 | - Add `http_client` property 21 | 22 | # 1.0.2 23 | - Internal refactoring 24 | 25 | # 1.0.1 26 | - Internal refactoring 27 | 28 | # 1.0.0 29 | - Initial upload 30 | -------------------------------------------------------------------------------- /sms/README.md: -------------------------------------------------------------------------------- 1 | # Vonage SMS Package 2 | 3 | This package contains the code to use Vonage's SMS API in Python. 4 | 5 | It includes a method for sending SMS messages and returns an `SmsResponse` class to handle the response. 6 | 7 | ## Usage 8 | 9 | It is recommended to use this as part of the main `vonage` package. The examples below assume you've created an instance of the `vonage.Vonage` class called `vonage_client`. 10 | 11 | ### Send an SMS 12 | 13 | Create an `SmsMessage` object, then pass into the `Sms.send` method. 14 | 15 | ```python 16 | from vonage_sms import SmsMessage, SmsResponse 17 | 18 | message = SmsMessage(to='1234567890', from_='Acme Inc.', text='Hello, World!') 19 | response: SmsResponse = vonage_client.sms.send(message) 20 | 21 | print(response.model_dump(exclude_unset=True)) 22 | ``` 23 | 24 | 25 | -------------------------------------------------------------------------------- /sms/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = 'vonage-sms' 3 | dynamic = ["version"] 4 | description = 'Vonage SMS package' 5 | readme = "README.md" 6 | authors = [{ name = "Vonage", email = "devrel@vonage.com" }] 7 | requires-python = ">=3.9" 8 | dependencies = [ 9 | "vonage-http-client>=1.5.0", 10 | "vonage-utils>=1.1.4", 11 | "pydantic>=2.9.2", 12 | ] 13 | classifiers = [ 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.9", 17 | "Programming Language :: Python :: 3.10", 18 | "Programming Language :: Python :: 3.11", 19 | "Programming Language :: Python :: 3.12", 20 | "Programming Language :: Python :: 3.13", 21 | "License :: OSI Approved :: Apache Software License", 22 | ] 23 | 24 | [project.urls] 25 | homepage = "https://github.com/Vonage/vonage-python-sdk" 26 | 27 | [tool.setuptools.dynamic] 28 | version = { attr = "vonage_sms._version.__version__" } 29 | 30 | [build-system] 31 | requires = ["setuptools>=61.0", "wheel"] 32 | build-backend = "setuptools.build_meta" 33 | -------------------------------------------------------------------------------- /sms/src/vonage_sms/BUILD: -------------------------------------------------------------------------------- 1 | python_sources() 2 | -------------------------------------------------------------------------------- /sms/src/vonage_sms/__init__.py: -------------------------------------------------------------------------------- 1 | from .errors import PartialFailureError, SmsError 2 | from .requests import SmsMessage 3 | from .responses import MessageResponse, SmsResponse 4 | from .sms import Sms 5 | 6 | __all__ = [ 7 | 'Sms', 8 | 'SmsMessage', 9 | 'SmsResponse', 10 | 'MessageResponse', 11 | 'SmsError', 12 | 'PartialFailureError', 13 | ] 14 | -------------------------------------------------------------------------------- /sms/src/vonage_sms/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = '1.1.6' 2 | -------------------------------------------------------------------------------- /sms/src/vonage_sms/errors.py: -------------------------------------------------------------------------------- 1 | from requests import Response 2 | from vonage_utils.errors import VonageError 3 | 4 | 5 | class SmsError(VonageError): 6 | """Indicates an error with the Vonage SMS Package.""" 7 | 8 | 9 | class PartialFailureError(SmsError): 10 | """Indicates that a request was partially successful.""" 11 | 12 | def __init__(self, response: Response): 13 | self.message = ( 14 | 'Sms.send_message method partially failed. Not all of the message(s) sent successfully.', 15 | ) 16 | super().__init__(self.message) 17 | self.response = response 18 | -------------------------------------------------------------------------------- /sms/src/vonage_sms/responses.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from pydantic import BaseModel, Field 4 | 5 | 6 | class MessageResponse(BaseModel): 7 | """Individual message response model. 8 | 9 | Args: 10 | to (str): The recipient's phone number in E.164 format. 11 | message_id (str): The message ID. 12 | status (str): The status of the message. 13 | remaining_balance (str): The estimated remaining balance. 14 | message_price (str): The estimated message cost. 15 | network (str): The estimated ID of the network of the recipient 16 | client_ref (str, Optional): If a `client_ref` was included when sending the SMS, 17 | this field will be included and hold the value that was sent. 18 | account_ref (str, Optional): An optional string used to identify separate 19 | accounts using the SMS endpoint for billing purposes. To use this feature, 20 | please email support. 21 | """ 22 | 23 | to: Optional[str] = None 24 | message_id: Optional[str] = Field(None, validation_alias='message-id') 25 | status: Optional[str] = None 26 | remaining_balance: Optional[str] = Field(None, validation_alias='remaining-balance') 27 | message_price: Optional[str] = Field(None, validation_alias='message-price') 28 | network: Optional[str] = None 29 | client_ref: Optional[str] = Field(None, validation_alias='client-ref') 30 | account_ref: Optional[str] = Field(None, validation_alias='account-ref') 31 | 32 | 33 | class SmsResponse(BaseModel): 34 | """Response recieved after sending an SMS. 35 | 36 | Args: 37 | message_count (str): The number of messages sent. 38 | messages (list[MessageResponse]): A list of individual message responses. See 39 | `MessageResponse` for more information. 40 | """ 41 | 42 | message_count: str = Field(..., validation_alias='message-count') 43 | messages: list[MessageResponse] 44 | -------------------------------------------------------------------------------- /sms/tests/BUILD: -------------------------------------------------------------------------------- 1 | python_tests(dependencies=['sms', 'testutils']) 2 | -------------------------------------------------------------------------------- /sms/tests/data/conversion_not_enabled.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |Problem accessing /conversions/sms. Reason: 9 |
Bad Account Credentials10 |