Modern, high-performance SIP library for Python — httpx-inspired API, async-native, IVR, RTP, DTMF
Project description
sipx
A modern, high-performance SIP library for Python inspired by httpx.
Build voice automation scripts, IVR systems, AI-powered call handlers, and SIP testing tools with a clean, Pythonic API.
Features
- httpx-inspired API —
client.invite(),client.register(), sync and async - FastAPI-style server —
@server.invite,AnnotatedDI extractors - Response builders —
request.ok(),request.trying(),request.error(code) - Dialog tracking —
client.ack()/client.bye()without passing response - Declarative events —
@on('INVITE', status=200)decorators - Full SIP support — 14 methods, auto 100 Trying, auto DNS SRV resolution
- Digest authentication — MD5, SHA-256, auto-retry on 401/407
- RTP + DTMF — RFC 4733 + SIP INFO + Inband, sync and async
- SDP with ICE/SRTP — RFC 4566, offer/answer, ICE candidates, DTLS
- Media pipeline — RTP, tone generators, TTS/STT adapters, IVR builder
- SIP-I — Real ISUP binary encoding (ITU-T Q.763), Brazilian SIP-I (ANATEL)
- Multiple transports — UDP, TCP, TLS, WebSocket (sync + async)
- State management — Transaction FSMs with Timer A/E retransmission
- Extensible — ABC-based transports, body parsers, custom DI extractors
Quick Start
import sipx
# One-liners
sipx.options("sip:pbx.example.com")
sipx.register("sip:alice@pbx.com", auth=("alice", "secret"))
sipx.send("sip:bob@pbx.com", "Hello!", auth=("alice", "secret"))
# Full call with dialog tracking
from sipx import SIPClient
with SIPClient(local_port=5061) as client:
client.auth = ("alice", "secret")
client.register("sip:alice@pbx.com")
sdp = client.create_sdp(port=8000)
r = client.invite("sip:bob@pbx.com", body=sdp.to_string())
if r.status_code == 200:
client.ack() # uses tracked dialog
# call is active...
client.bye() # uses tracked dialog
Installation
# With uv (recommended)
uv add sipx
# With pip
pip install sipx
# From source
git clone https://github.com/oornnery/sipx.git
cd sipx
uv sync
Requirements
Python 3.12+
Usage Examples
SIP Server (FastAPI-style decorators + DI)
from typing import Annotated
from sipx import SIPServer, Request, FromHeader, Header
server = SIPServer(local_host="127.0.0.1", local_port=5060)
@server.register()
def on_register(request: Request, caller: Annotated[str, FromHeader]):
print(f"REGISTER from {caller}")
return request.ok()
@server.message()
def on_message(request: Request, caller: Annotated[str, FromHeader]):
body = request.content.decode() if request.content else ""
print(f"MESSAGE from {caller}: {body}")
return request.ok()
server.start()
Async SIPClient
import asyncio
from sipx import AsyncSIPClient
async def main():
async with AsyncSIPClient(local_port=5061) as client:
client.auth = ("alice", "secret")
r = await client.register("sip:alice@pbx.com")
print(f"REGISTER: {r.status_code}")
sdp = client.create_sdp(port=8000)
r = await client.invite("sip:bob@pbx.com", body=sdp.to_string())
if r and r.status_code == 200:
await client.ack()
await asyncio.sleep(5)
await client.bye()
asyncio.run(main())
FastAPI + SIP Integration
Run a REST API that controls SIP operations:
# Terminal 1: SIP server
uv run python examples/server.py
# Terminal 2: FastAPI
uv run python examples/fastapi_sip.py
# Terminal 3: test via curl
curl -X POST http://localhost:8000/message \
-H "Content-Type: application/json" \
-d '{"uri":"sip:test@127.0.0.1:15090","content":"Hello from FastAPI!"}' | jq
{
"status_code": 200,
"reason": "OK"
}
Full request/response trace with retransmission timers, DI resolution, and FSM state transitions:
# FastAPI (client side)
INFO UDP bound to 0.0.0.0:40703
DEBUG Transaction 592b5fee created: MESSAGE (type=NON_INVITE)
DEBUG Timer E started (0.5s) # retransmission armed
DEBUG Timer F started (32.0s) # timeout armed
DEBUG UDP send 335 bytes -> 127.0.0.1:15090
DEBUG UDP recv 245 bytes <- 127.0.0.1:15090
DEBUG <<< 200 OK
DEBUG Transaction 592b5fee: TRYING -> COMPLETED
DEBUG Timer E cancelled # retransmission stopped
DEBUG Timer F cancelled # timeout stopped
# SIP Server (server side)
DEBUG UDP recv 335 bytes <- 127.0.0.1:40703
DEBUG Resolving handler on_message
DEBUG Extracting caller via FromHeader()
MESSAGE from <sip:user@0.0.0.0>: Hello from FastAPI!
DEBUG >>> SENDING 200 OK to 127.0.0.1:40703
DEBUG UDP send 245 bytes -> 127.0.0.1:40703
Event Handlers
from sipx import Events, on
class CallEvents(Events):
@on('INVITE', status=200)
def on_call_ok(self, request, response, context):
print("Call accepted!")
@on('INVITE', status=(180, 183))
def on_ringing(self, request, response, context):
print(f"Ringing... ({response.status_code})")
@on(status=(401, 407))
def on_auth(self, request, response, context):
print("Auth challenge received")
Client Decorators
from sipx import SIPClient
with SIPClient() as client:
@client.invite(status=200)
def on_invite_ok(response, context):
print("INVITE accepted:", response.status_code)
@client.message(phase="request")
def stamp_message(request, context):
request.headers["X-Debug"] = "1"
return request
Authentication
# Tuple auth (auto-retry on 401/407)
client.auth = ("alice", "secret")
# Or explicit Auth object
client.auth = Auth.Digest("alice", "secret")
# Per-request override
r = client.register("sip:alice@pbx.com")
if r.status_code == 401:
r = client.retry_with_auth(r, auth=Auth.Digest("alice", "other"))
Supported Methods
| Method | Description | Example |
|---|---|---|
| INVITE | Establish a call | client.invite('sip:bob@ex.com', body=sdp) |
| ACK | Acknowledge INVITE | client.ack() (auto dialog) |
| BYE | Terminate a call | client.bye() (auto dialog) |
| CANCEL | Cancel pending INVITE | client.cancel(response=r) |
| REGISTER | Register location | client.register('sip:alice@ex.com') |
| OPTIONS | Query capabilities | client.options('sip:ex.com') |
| MESSAGE | Instant message | client.message('sip:bob@ex.com', content='Hi') |
| SUBSCRIBE | Subscribe to events | client.subscribe('sip:bob@ex.com') |
| NOTIFY | Event notification | client.notify('sip:bob@ex.com') |
| REFER | Call transfer | client.refer('sip:bob@ex.com', refer_to='sip:carol@ex.com') |
| INFO | Mid-dialog info (DTMF) | client.info('sip:bob@ex.com', content=dtmf) |
| UPDATE | Update session | client.update('sip:bob@ex.com', sdp_content=sdp) |
| PRACK | Provisional ACK | client.prack(response=r) |
| PUBLISH | Publish state | client.publish('sip:bob@ex.com') |
RFC Compliance
| RFC | Title | Status |
|---|---|---|
| 3261 | SIP: Session Initiation Protocol | Complete (auto 100 Trying, retransmission) |
| 2617 | HTTP Digest Authentication | Complete (MD5, SHA-256) |
| 7616 | HTTP Digest (SHA-256) | Complete |
| 4566 | SDP: Session Description Protocol | Complete (ICE, SRTP, DTLS) |
| 3264 | Offer/Answer Model with SDP | Complete |
| 3550 | RTP: Real-time Transport Protocol | Complete (sync + async) |
| 4733 | DTMF via RTP (telephone-event) | Complete |
| 3263 | DNS SRV Resolution | Complete (auto in SIPClient) |
| 4028 | Session Timers | Complete |
Documentation
| Document | Description |
|---|---|
| docs/SDD.md | Software Design Document (full spec, diagrams, roadmap) |
| examples/README.md | Examples catalog (22 examples) |
| docker/asterisk/ | Asterisk test environment |
Testing with Asterisk
cd docker/asterisk
docker-compose up -d
uv run python examples/asterisk.py
Examples
See examples/README.md for the full list. Highlights:
| Example | What it shows |
|---|---|
| quickstart.py | One-liner register, options, call, send |
| call.py | REGISTER -> INVITE -> ACK -> BYE |
| server.py | SIPServer with decorators + DI |
| ivr.py | Async IVR with RTP + DTMF |
| response_builders.py | request.ok/trying/error, dialog tracking |
| asterisk.py | Comprehensive integration test |
Roadmap
See docs/SDD.md for the full roadmap. Key upcoming work:
- SRTP — packet encryption (RFC 3711)
- RTCP — control protocol (RFC 3550 Section 6)
- Jitter buffer — for smooth RTP playback
- Coverage >80% — currently 607 tests, ~60%
- PyPI publishing — package ready, CI pipeline configured
Development
git clone https://github.com/oornnery/sipx.git
cd sipx
uv sync
uv run ruff check sipx
uv run pytest
Inspirations
API design:
- httpx — Pythonic API, sync/async, extensible
Reference implementations:
- sipd — SIP daemon implementation
- sipmessage — SIP message parsing
- sip-parser — SIP parser
- PySipIvr — SIP IVR implementation
- sip-resources — Curated SIP resources and documentation
Other inspirations:
- pyVoIP — VoIP functionality
- aiosip — Async SIP
- b2bua — Back-to-back user agent
- pysipp — SIP testing
- PySIPio — SIP I/O
- katariSIP — SIP library
- SIP-Auth-helper — SIP auth tools
- callsip.py — Simple SIP caller
License
MIT License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sipx-0.0.7.tar.gz.
File metadata
- Download URL: sipx-0.0.7.tar.gz
- Upload date:
- Size: 175.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0947e0fe0ac9855356ad2f8c12b95a2da459931c81b21511625c8e777762a63b
|
|
| MD5 |
ee2440f708a26792ae28e48df4cffbc7
|
|
| BLAKE2b-256 |
075ac85795a44f250a100711e228a7f820a31eb3a8247f6e7eb1b82c5a730ce5
|
Provenance
The following attestation bundles were made for sipx-0.0.7.tar.gz:
Publisher:
release.yml on oornnery/sipx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sipx-0.0.7.tar.gz -
Subject digest:
0947e0fe0ac9855356ad2f8c12b95a2da459931c81b21511625c8e777762a63b - Sigstore transparency entry: 1219478293
- Sigstore integration time:
-
Permalink:
oornnery/sipx@7b398ff07c444b2de5bf0dba32fb84deafed0dd4 -
Branch / Tag:
refs/tags/v0.0.7 - Owner: https://github.com/oornnery
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7b398ff07c444b2de5bf0dba32fb84deafed0dd4 -
Trigger Event:
release
-
Statement type:
File details
Details for the file sipx-0.0.7-py3-none-any.whl.
File metadata
- Download URL: sipx-0.0.7-py3-none-any.whl
- Upload date:
- Size: 184.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a827ab2af71bc550c76a71e8b473a119badd22af271332b696be5a1f8849519c
|
|
| MD5 |
ca49e299a6f8bd1f2891a5408c2be098
|
|
| BLAKE2b-256 |
6fb6ebee7a29b2d51a65990a7a8d98d8ca7c821a587d69d5d451ea228174c722
|
Provenance
The following attestation bundles were made for sipx-0.0.7-py3-none-any.whl:
Publisher:
release.yml on oornnery/sipx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sipx-0.0.7-py3-none-any.whl -
Subject digest:
a827ab2af71bc550c76a71e8b473a119badd22af271332b696be5a1f8849519c - Sigstore transparency entry: 1219478336
- Sigstore integration time:
-
Permalink:
oornnery/sipx@7b398ff07c444b2de5bf0dba32fb84deafed0dd4 -
Branch / Tag:
refs/tags/v0.0.7 - Owner: https://github.com/oornnery
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7b398ff07c444b2de5bf0dba32fb84deafed0dd4 -
Trigger Event:
release
-
Statement type: