Copyright 2024-2026 Puzzel AS
- Contents
- Overview
- Prerequisites
- Installation
- Quick Start
- Authentication
- Usage Examples
- API Reference
- Error Handling
- Testing
- Migration from v2.x
- Publishing
- Additional Documentation
- Support
- License
This is a Python client for the Puzzel SMS Gateway API. It provides a type-safe, fluent interface for sending SMS messages and managing message batches.
The client is generated from the SMS Gateway OpenAPI specification and uses the Kiota abstractions for HTTP communication, serialization, and authentication.
- Python 3.10 or higher
- pip or uv package manager
The package automatically installs all required dependencies when you install it from PyPI:
microsoft-kiota-abstractions>=1.0.0- Core Kiota abstractionsmicrosoft-kiota-http>=1.0.0- HTTP request handlingmicrosoft-kiota-serialization-json>=1.0.0- JSON serializationmicrosoft-kiota-serialization-text>=1.0.0- Text serializationmicrosoft-kiota-serialization-form>=1.0.0- Form serializationmicrosoft-kiota-serialization-multipart>=1.0.0- Multipart serializationhttpx>=0.24.0- Async HTTP client
You don't need to install these manually - they're included automatically. See pyproject.toml for the complete list.
uv is a fast Python package installer and resolver.
# Install uv if you haven't already
curl -LsSf https://astral.sh/uv/install.sh | sh# Install the SMS Gateway client from PyPI
uv pip install puzzel-sms-gateway-client# Install uv if you haven't already
curl -LsSf https://astral.sh/uv/install.sh | sh# Create a new project
uv init <projectname> --app --python 3.13.1
cd<projectname># Create virtual environment
uv venv --python 3.13.1 --seed# Activate virtual environmentsource .venv/bin/activate # On macOS/Linux# or
.venv\Scripts\activate # On Windows# Verify installation
python -V
uv pip list
# Install the SMS Gateway client from PyPI
uv pip install puzzel-sms-gateway-clientNote:
uv inittypically creates.venv/automatically, but you can use--seedto explicitly ensure that pip, setuptools, and wheel are installed.
pip install puzzel-sms-gateway-clientimportasynciofromkiota_abstractions.authenticationimportAnonymousAuthenticationProviderfromkiota_http.httpx_request_adapterimportHttpxRequestAdapterfromsrc.mt_http_clientimportMtHttpClientfromsrc.models.gateway_requestimportGatewayRequestfromsrc.models.messageimportMessageasyncdefsend_sms():
# Set up authentication and HTTP adapterauth_provider=AnonymousAuthenticationProvider()
request_adapter=HttpxRequestAdapter(auth_provider)
request_adapter.base_url="https://smsgw.puzzel.com"# Create the clientclient=MtHttpClient(request_adapter)
# Create a messagegateway_request=GatewayRequest(
service_id=YOUR_SERVICE_ID, # Your Puzzel service IDusername="YOUR_USERNAME", # Your Puzzel usernamepassword="YOUR_PASSWORD", # Your Puzzel passwordmessage=[
Message(
recipient="+47xxxxxxxxx", # Phone number with country codecontent="Hello World!"
)
]
)
# Send the messageresponse=awaitclient.gw.rs.send_messages.post(gateway_request)
print(f"Batch Reference: {response.batch_reference}")
forstatusinresponse.message_status:
print(f"Message ID: {status.message_id}")
print(f"Status: {status.status_message}")
# Run the async functionasyncio.run(send_sms())The SMS Gateway uses Basic Authentication passed through the request body. You need to provide:
service_id: Your service ID (integer)username: Your gateway username (string)password: Your gateway password (string)
These credentials are included in the GatewayRequest object for each API call.
The Puzzel SMS Gateway base URL is: https://smsgw.puzzel.com
Important: Set only the base domain, not including any path:
# ✅ Correctrequest_adapter.base_url="https://smsgw.puzzel.com"# ❌ Wrong - don't include the pathrequest_adapter.base_url="https://smsgw.puzzel.com/gw/rs"The client automatically constructs the full URL path based on the API method you call.
fromkiota_abstractions.authenticationimportAnonymousAuthenticationProviderfromkiota_http.httpx_request_adapterimportHttpxRequestAdapter# Use AnonymousAuthenticationProvider since credentials are in the request bodyauth_provider=AnonymousAuthenticationProvider()
request_adapter=HttpxRequestAdapter(auth_provider)
request_adapter.base_url="https://smsgw.puzzel.com"Send a simple SMS to a single recipient:
importasynciofromkiota_abstractions.authenticationimportAnonymousAuthenticationProviderfromkiota_http.httpx_request_adapterimportHttpxRequestAdapterfromsrc.mt_http_clientimportMtHttpClientfromsrc.models.gateway_requestimportGatewayRequestfromsrc.models.messageimportMessageasyncdefsend_basic_sms():
auth_provider=AnonymousAuthenticationProvider()
request_adapter=HttpxRequestAdapter(auth_provider)
request_adapter.base_url="https://smsgw.puzzel.com"client=MtHttpClient(request_adapter)
request=GatewayRequest(
service_id=12345,
username="your_username",
password="your_password",
message=[
Message(
recipient="+47xxxxxxxxx",
content="Hello from Puzzel SMS Gateway!"
)
]
)
response=awaitclient.gw.rs.send_messages.post(request)
returnresponseasyncio.run(send_basic_sms())Send an SMS with advanced settings like send window, originator, and priority:
importasynciofromsrc.mt_http_clientimportMtHttpClientfromsrc.models.gateway_requestimportGatewayRequestfromsrc.models.messageimportMessagefromsrc.models.settingsimportSettingsfromsrc.models.send_windowimportSendWindowfromsrc.models.originator_settingsimportOriginatorSettingsfromsrc.models.originator_typeimportOriginatorTypeasyncdefsend_scheduled_sms():
fromkiota_abstractions.authenticationimportAnonymousAuthenticationProviderfromkiota_http.httpx_request_adapterimportHttpxRequestAdapterfromsrc.mt_http_clientimportMtHttpClientauth_provider=AnonymousAuthenticationProvider()
request_adapter=HttpxRequestAdapter(auth_provider)
request_adapter.base_url="https://smsgw.puzzel.com"client=MtHttpClient(request_adapter)
request=GatewayRequest(
service_id=12345,
username="your_username",
password="your_password",
message=[
Message(
recipient="+47xxxxxxxxx",
content="Scheduled message",
settings=Settings(
priority=1,
validity=173,
originator_settings=OriginatorSettings(
originator="1960",
originator_type=OriginatorType.Network
),
send_window=SendWindow(
start_date="2026-03-21",
start_time="09:00:00",
stop_date="2026-03-21",
stop_time="17:00:00"
)
)
)
]
)
response=awaitclient.gw.rs.send_messages.post(request)
returnresponseasyncio.run(send_scheduled_sms())Send an SMS with all available options:
importasynciofromsrc.models.gateway_requestimportGatewayRequestfromsrc.models.messageimportMessagefromsrc.models.settingsimportSettingsfromsrc.models.gas_settingsimportGasSettingsfromsrc.models.parameterimportParameterasyncdefsend_advanced_sms():
fromkiota_abstractions.authenticationimportAnonymousAuthenticationProviderfromkiota_http.httpx_request_adapterimportHttpxRequestAdapterfromsrc.mt_http_clientimportMtHttpClientauth_provider=AnonymousAuthenticationProvider()
request_adapter=HttpxRequestAdapter(auth_provider)
request_adapter.base_url="https://smsgw.puzzel.com"client=MtHttpClient(request_adapter)
request=GatewayRequest(
service_id=12345,
username="your_username",
password="your_password",
batch_reference="my-batch-001",
message=[
Message(
recipient="+47xxxxxxxxx",
content="Advanced message",
price=100,
client_reference="msg-ref-001",
settings=Settings(
priority=1,
validity=173,
differentiator="campaign-spring-2026",
invoice_node="marketing-dept",
age=18,
new_session=True,
session_id="session-12345",
auto_detect_encoding=True,
gas_settings=GasSettings(
service_code="02001",
description="Premium SMS"
),
parameter=[
Parameter(key="custom_key", value="custom_value")
]
)
)
]
)
response=awaitclient.gw.rs.send_messages.post(request)
returnresponseasyncio.run(send_advanced_sms())List, retrieve, and stop message batches:
importasyncioasyncdefmanage_batches():
fromkiota_abstractions.authenticationimportAnonymousAuthenticationProviderfromkiota_http.httpx_request_adapterimportHttpxRequestAdapterfromsrc.mt_http_clientimportMtHttpClientauth_provider=AnonymousAuthenticationProvider()
request_adapter=HttpxRequestAdapter(auth_provider)
request_adapter.base_url="https://smsgw.puzzel.com"client=MtHttpClient(request_adapter)
service_id=12345batch_reference="my-batch-001"# List all batches for a servicebatch_list=awaitclient.mgmt.rs.service.by_service_id(service_id).batch.get()
print(f"Total batches: {len(batch_list.message_batch)}")
forbatchinbatch_list.message_batch:
print(f"Batch: {batch.client_batch_reference}, Total: {batch.total_size}, On Hold: {batch.on_hold}")
# Get details of a specific batchbatch_details=awaitclient.mgmt.rs.service.by_service_id(service_id).batch.by_client_batch_reference(batch_reference).get()
print(f"Batch details: {batch_details.message_batch}")
# Stop a batchstop_response=awaitclient.mgmt.rs.service.by_service_id(service_id).batch.by_client_batch_reference(batch_reference).delete()
print(f"Stopped {len(stop_response.stopped_messages)} messages")
asyncio.run(manage_batches())MtHttpClient: The main entry point for the SDK
The client provides access to different API versions and endpoints:
client.gw.rs.send_messages: Send messages (recommended endpoint)client.chimera.send_messages: Alternative send messages endpointclient.mgmt.rs.service: Batch management operationsclient.management: Alternative batch management endpoint
All models are located in the src/models/ directory:
GatewayRequest: The main request object for sending messagesGatewayResponse: Response containing batch reference and message statusesMessage: Individual SMS messageSettings: Message-level settingsMessageStatus: Status information for sent messagesSendWindow: Schedule messages for specific time windowsOriginatorSettings: Configure message sender informationGasSettings: GAS (Gateway API Service) configurationParameter: Custom key-value parametersBatchListResponse: List of message batchesBatchSingleResponse: Single batch detailsStopBatchResponse: Response when stopping a batch
Request builders provide a fluent API for constructing requests:
SendMessagesRequestBuilder: Build and execute send message requestsBatchRequestBuilder: Build batch management requests- Various service-specific builders in the
src/directory structure
The client uses Kiota's error handling mechanisms. HTTP errors are mapped to specific exception types:
importasynciofromsrc.models.problem_detailsimportProblemDetailsasyncdefsend_with_error_handling():
try:
response=awaitclient.gw.rs.send_messages.post(request)
print(f"Success! Batch: {response.batch_reference}")
exceptExceptionase:
ifhasattr(e, 'error') andisinstance(e.error, ProblemDetails):
print(f"API Error: {e.error.title}")
print(f"Status: {e.error.status}")
print(f"Detail: {e.error.detail}")
else:
print(f"Unexpected error: {e}")
asyncio.run(send_with_error_handling())Common error status codes:
401 Unauthorized: Invalid credentials404 Not Found: Batch or resource not found400 Bad Request: Invalid request parameters
Install dev dependencies and run the full test suite:
uv sync
uv run pytestRun with coverage:
uv run pytest --cov=src --cov-report=term-missingThe tests live in tests/unit/ and cover client initialization, all data models (field access, serialization, deserialization keys), and the request builders. See docs/07-testing.md for the full testing guide, including how to run specific tests, use fixtures, and write new tests.
If you're migrating from the previous puzzel_sms_gateway_client v2.x, here are the key differences:
The Kiota client is fully asynchronous:
Old (v2.x):
response=client.send(messages=[message])New (Kiota):
response=awaitclient.gw.rs.send_messages.post(request)Old (v2.x):
importpuzzel_sms_gateway_clientassmsgwclient=smsgw.Client(
service_id=SERVICE_ID,
username=USERNAME,
password=PASSWORD,
base_address=BASE_ADDRESS,
)New (Kiota):
fromkiota_abstractions.authenticationimportAnonymousAuthenticationProviderfromkiota_http.httpx_request_adapterimportHttpxRequestAdapterfromsrc.mt_http_clientimportMtHttpClientauth_provider=AnonymousAuthenticationProvider()
request_adapter=HttpxRequestAdapter(auth_provider)
request_adapter.base_url=BASE_ADDRESSclient=MtHttpClient(request_adapter)Credentials are now part of each request rather than the client:
Old (v2.x):
client=smsgw.Client(service_id=..., username=..., password=...)
client.send(messages=[...])New (Kiota):
request=GatewayRequest(
service_id=SERVICE_ID,
username=USERNAME,
password=PASSWORD,
message=[...]
)
awaitclient.gw.rs.send_messages.post(request)Models are now dataclasses with explicit typing:
Old (v2.x):
message=smsgw.Message(
recipient="+47...",
content="Hello",
settings=smsgw.MessageSettings(...)
)New (Kiota):
fromsrc.models.messageimportMessagefromsrc.models.settingsimportSettingsmessage=Message(
recipient="+47...",
content="Hello",
settings=Settings(...)
)The new client uses a fluent, path-based API:
# Send messagesawaitclient.gw.rs.send_messages.post(request)
# Manage batchesawaitclient.mgmt.rs.service.by_service_id(123).batch.get()
awaitclient.mgmt.rs.service.by_service_id(123).batch.by_client_batch_reference("ref").get()
awaitclient.mgmt.rs.service.by_service_id(123).batch.by_client_batch_reference("ref").delete()To build and publish the package to PyPI using uv:
# 1. Bump the version in pyproject.toml, then build
uv build
# 2. Publish to TestPyPI first (optional but recommended)
uv publish \
--publish-url https://test.pypi.org/legacy/ \
--token pypi-your-test-token-here
# 3. Publish to PyPI
uv publish --token pypi-your-production-token-hereFor full details — credential storage, TestPyPI validation, version conventions, and ~/.pypirc setup — see docs/08-publishing.md.
For more detailed information, see the documentation in the docs/ folder:
- Documentation Index - Complete documentation index and navigation
- uv Setup Guide - Complete guide for setting up with uv (recommended)
- Getting Started Guide - Step-by-step guide for beginners
- Quick Reference - Cheat sheet for common operations
- Complete Usage Examples - More code examples and use cases
- API Models Reference - Detailed model documentation
- Advanced Topics - Configuration, customization, and best practices
- Testing Guide - Running tests, fixtures, and writing new tests
- Publishing Guide - Building and publishing to PyPI with uv
- Example Code Files - Runnable example scripts
For issues, questions, or contributions, please contact Puzzel support or refer to the main SMS Gateway documentation.
Released under the MIT license. See LICENSE for details.