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 environment
source .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-clientimport asyncio
from kiota_abstractions.authentication import AnonymousAuthenticationProvider
from kiota_http.httpx_request_adapter import HttpxRequestAdapter
from src.mt_http_client import MtHttpClient
from src.models.gateway_request import GatewayRequest
from src.models.message import Message
async def send_sms():
# Set up authentication and HTTP adapter
auth_provider = AnonymousAuthenticationProvider()
request_adapter = HttpxRequestAdapter(auth_provider)
request_adapter.base_url = "https://smsgw.puzzel.com"
# Create the client
client = MtHttpClient(request_adapter)
# Create a message
gateway_request = GatewayRequest(
service_id=YOUR_SERVICE_ID, # Your Puzzel service ID
username="YOUR_USERNAME", # Your Puzzel username
password="YOUR_PASSWORD", # Your Puzzel password
message=[
Message(
recipient="+47xxxxxxxxx", # Phone number with country code
content="Hello World!"
)
]
)
# Send the message
response = await client.gw.rs.send_messages.post(gateway_request)
print(f"Batch Reference: {response.batch_reference}")
for status in response.message_status:
print(f"Message ID: {status.message_id}")
print(f"Status: {status.status_message}")
# Run the async function
asyncio.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:
# ✅ Correct
request_adapter.base_url = "https://smsgw.puzzel.com"
# ❌ Wrong - don't include the path
request_adapter.base_url = "https://smsgw.puzzel.com/gw/rs"The client automatically constructs the full URL path based on the API method you call.
from kiota_abstractions.authentication import AnonymousAuthenticationProvider
from kiota_http.httpx_request_adapter import HttpxRequestAdapter
# Use AnonymousAuthenticationProvider since credentials are in the request body
auth_provider = AnonymousAuthenticationProvider()
request_adapter = HttpxRequestAdapter(auth_provider)
request_adapter.base_url = "https://smsgw.puzzel.com"Send a simple SMS to a single recipient:
import asyncio
from kiota_abstractions.authentication import AnonymousAuthenticationProvider
from kiota_http.httpx_request_adapter import HttpxRequestAdapter
from src.mt_http_client import MtHttpClient
from src.models.gateway_request import GatewayRequest
from src.models.message import Message
async def send_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 = await client.gw.rs.send_messages.post(request)
return response
asyncio.run(send_basic_sms())Send an SMS with advanced settings like send window, originator, and priority:
import asyncio
from src.mt_http_client import MtHttpClient
from src.models.gateway_request import GatewayRequest
from src.models.message import Message
from src.models.settings import Settings
from src.models.send_window import SendWindow
from src.models.originator_settings import OriginatorSettings
from src.models.originator_type import OriginatorType
async def send_scheduled_sms():
from kiota_abstractions.authentication import AnonymousAuthenticationProvider
from kiota_http.httpx_request_adapter import HttpxRequestAdapter
from src.mt_http_client import MtHttpClient
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="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 = await client.gw.rs.send_messages.post(request)
return response
asyncio.run(send_scheduled_sms())Send an SMS with all available options:
import asyncio
from src.models.gateway_request import GatewayRequest
from src.models.message import Message
from src.models.settings import Settings
from src.models.gas_settings import GasSettings
from src.models.parameter import Parameter
async def send_advanced_sms():
from kiota_abstractions.authentication import AnonymousAuthenticationProvider
from kiota_http.httpx_request_adapter import HttpxRequestAdapter
from src.mt_http_client import MtHttpClient
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",
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 = await client.gw.rs.send_messages.post(request)
return response
asyncio.run(send_advanced_sms())List, retrieve, and stop message batches:
import asyncio
async def manage_batches():
from kiota_abstractions.authentication import AnonymousAuthenticationProvider
from kiota_http.httpx_request_adapter import HttpxRequestAdapter
from src.mt_http_client import MtHttpClient
auth_provider = AnonymousAuthenticationProvider()
request_adapter = HttpxRequestAdapter(auth_provider)
request_adapter.base_url = "https://smsgw.puzzel.com"
client = MtHttpClient(request_adapter)
service_id = 12345
batch_reference = "my-batch-001"
# List all batches for a service
batch_list = await client.mgmt.rs.service.by_service_id(service_id).batch.get()
print(f"Total batches: {len(batch_list.message_batch)}")
for batch in batch_list.message_batch:
print(f"Batch: {batch.client_batch_reference}, Total: {batch.total_size}, On Hold: {batch.on_hold}")
# Get details of a specific batch
batch_details = await client.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 batch
stop_response = await client.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:
import asyncio
from src.models.problem_details import ProblemDetails
async def send_with_error_handling():
try:
response = await client.gw.rs.send_messages.post(request)
print(f"Success! Batch: {response.batch_reference}")
except Exception as e:
if hasattr(e, 'error') and isinstance(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 = await client.gw.rs.send_messages.post(request)Old (v2.x):
import puzzel_sms_gateway_client as smsgw
client = smsgw.Client(
service_id=SERVICE_ID,
username=USERNAME,
password=PASSWORD,
base_address=BASE_ADDRESS,
)New (Kiota):
from kiota_abstractions.authentication import AnonymousAuthenticationProvider
from kiota_http.httpx_request_adapter import HttpxRequestAdapter
from src.mt_http_client import MtHttpClient
auth_provider = AnonymousAuthenticationProvider()
request_adapter = HttpxRequestAdapter(auth_provider)
request_adapter.base_url = BASE_ADDRESS
client = 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=[...]
)
await client.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):
from src.models.message import Message
from src.models.settings import Settings
message = Message(
recipient="+47...",
content="Hello",
settings=Settings(...)
)The new client uses a fluent, path-based API:
# Send messages
await client.gw.rs.send_messages.post(request)
# Manage batches
await client.mgmt.rs.service.by_service_id(123).batch.get()
await client.mgmt.rs.service.by_service_id(123).batch.by_client_batch_reference("ref").get()
await client.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.