Official Siren SDK for Python — affiliate and incentive tracking for any commerce stack.
Project description
Siren SDK for Python
Affiliate, referral, and incentive tracking for any commerce stack — record sales, verify signed webhooks in one call, and reconcile Siren's ledger against your own.
What is Siren?
Siren is an affiliate, referral, and incentive platform. You register commerce events — sales, refunds, referred visits — and Siren attributes them to collaborators, computes rewards, and pays out.
This SDK is the official Python client for the Siren API. It lets you:
- Record sales, refunds, referred visits, and custom events (
events) - Verify signed webhook deliveries in a single, constant-time call (
webhooks) - Manage webhook subscriptions and API keys
- Reconcile Siren's ledger against your own with paginated readers
The full developer surface is described in the OpenAPI specification.
Install
pip install siren-sdk
The distribution is named siren-sdk, but the import name is siren:
import siren
Requires Python 3.9+. The only dependency is httpx.
Quickstart
Record a sale
import siren
client = siren.Siren(api_key="sk_live_...") # Settings -> API Keys in the Siren dashboard
result = client.events.sale(
source="stripe", # your commerce source
external_id="cs_test_a1b2c3", # your order id; used to match refunds later
total=49.99, # major units: 49.99 means $49.99
tracking_id=4021, # opportunity id from the Siren tracking cookie
)
print(result.opportunity_id) # read from the X-Siren-OID response header
With line items (reward pools compute amount x quantity per line; a missing
quantity defaults to 1):
client.events.sale(
source="stripe",
external_id="ord_77",
total=109.97,
tracking_id=4021,
currency="USD",
items=[
{"external_id": "sku_1", "name": "Pro Plan (annual)", "amount": 49.99},
{"name": "Add-on", "quantity": 3, "amount": 19.99},
],
)
Refunds and referred visits:
client.events.refund(source="stripe", external_id="cs_test_a1b2c3")
client.events.site_visited(collaborator_id=88, user_id=12345)
# Custom event types registered on your org:
client.events.ingest("loyalty-points-earned", {"points": 120, "memberId": "m_9"})
Verify a webhook
Siren signs every delivery with X-Siren-Signature: sha256=<hex hmac> — an
HMAC-SHA256 of the raw request body keyed by your subscription's signing
secret. construct_event verifies the signature (constant-time) and parses
the event in one call:
import siren
from flask import Flask, request
app = Flask(__name__)
client = siren.Siren(api_key="sk_live_...")
@app.post("/webhooks/siren")
def handle_webhook():
try:
event = client.webhooks.construct_event(
request.get_data(), # RAW body bytes — critical
request.headers.get("X-Siren-Signature"),
"whsec_...", # your signing secret
)
except siren.SignatureVerificationError:
return "invalid signature", 400
if event.type == siren.WebhookEventType.CONVERSION_APPROVED:
handle_conversion(event.data)
return "", 204
Pass the raw body bytes. The HMAC is computed over the exact bytes Siren sent. If you parse the JSON and re-serialize it, key order and whitespace change and verification will fail. Use
request.get_data()(Flask),request.body(Django),await request.body()(FastAPI/Starlette) — neverjson.dumps(request.json).
To just check a signature without parsing:
if client.webhooks.verify_signature(raw_body, signature_header, secret):
...
Manage subscriptions
subscription = client.webhooks.subscriptions.create(
target_url="https://example.com/webhooks/siren",
events=[
siren.WebhookEventType.CONVERSION_APPROVED,
siren.WebhookEventType.PAYOUT_PAID,
],
# or events=[siren.WebhookEventType.ALL] to subscribe to everything
)
print(subscription.signing_secret) # returned ONCE — store it now
client.webhooks.subscriptions.list()
client.webhooks.subscriptions.delete(subscription.id)
Features
- Event ingestion —
events.sale,events.refund,events.site_visited, andevents.ingestfor custom event types. - One-call webhook verification —
webhooks.construct_eventverifies the HMAC-SHA256 signature in constant time and parses the payload. - Subscription and API-key management — create, list, delete subscriptions; create, list, revoke API keys. One-time secrets are never auto-retried.
- Reconciliation readers — paginated access to conversions, transactions, obligations, and payouts.
- Typed, correct errors — every failure subclasses
siren.SirenErrorwithmessage,code, andstatus_code;ValidationError.field_errorsandRateLimitError.retry_afterwhere relevant. - Automatic retries — exponential backoff on network errors and 429/5xx for idempotent reads and event ingestion (never for secret-minting writes).
- Fully typed — ships
py.typed; works with mypy and Pyright out of the box.
API keys
key = client.api_keys.create(label="Production server")
print(key.raw_key) # sk_live_... — returned ONCE, cannot be retrieved later
client.api_keys.list() # no raw key material
client.api_keys.revoke(key.id)
Reconciliation
Thin paginated readers over Siren's ledger:
page = client.conversions.list(page=1, per_page=50)
for conversion in page:
print(conversion["id"])
print(page.total)
client.transactions.list()
client.obligations.list()
client.payouts.list(status="paid") # extra filters pass through as query args
Errors
All errors subclass siren.SirenError and carry message, code, and
status_code:
| HTTP status | Exception |
|---|---|
| 400 | BadRequestError |
| 401 | AuthenticationError |
| 403 | PermissionError |
| 404 | NotFoundError |
| 409 | ConflictError |
| 422 | ValidationError (.field_errors) |
| 429 | RateLimitError (.retry_after) |
| 5xx | ApiError |
| network/timeout | ConnectionError |
Webhook verification failures raise SignatureVerificationError.
try:
client.events.refund(source="stripe", external_id="unknown")
except siren.NotFoundError:
print("no matching sale")
except siren.RateLimitError as err:
print(f"retry in {err.retry_after}s")
Configuration
client = siren.Siren(
api_key="sk_live_...",
base_url="https://api.sirenaffiliates.com/siren/v1", # default; point at staging/local to test
timeout=30.0, # seconds
max_retries=2, # exponential backoff on network errors and 429/5xx
)
Other SDKs
Siren maintains official SDKs for other stacks:
- Node.js — Novatorius/siren-node
- PHP — Novatorius/siren-php
Links
- Website: sirenaffiliates.com
- API reference: OpenAPI specification
- Issues: github.com/Novatorius/siren-python/issues
Contributing
Contributions are welcome. See CONTRIBUTING.md for how to set up a development environment and run the test suite. By participating you agree to the Code of Conduct.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
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 siren_sdk-0.1.0.tar.gz.
File metadata
- Download URL: siren_sdk-0.1.0.tar.gz
- Upload date:
- Size: 23.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a41db8b0f9d5e33ec48044da376a11ae4a482e94aa4a7aea36f835875e6d6116
|
|
| MD5 |
14c12cc0f903b3a081c0880c3ee3f59f
|
|
| BLAKE2b-256 |
c4b155bde4f72e262d3c47aa38401da95b852ac28459b13ab3d87d5b9f10b420
|
Provenance
The following attestation bundles were made for siren_sdk-0.1.0.tar.gz:
Publisher:
release.yml on Novatorius/siren-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
siren_sdk-0.1.0.tar.gz -
Subject digest:
a41db8b0f9d5e33ec48044da376a11ae4a482e94aa4a7aea36f835875e6d6116 - Sigstore transparency entry: 2146647890
- Sigstore integration time:
-
Permalink:
Novatorius/siren-python@f4f31d1e268ab585bba5f29a9d9f467c406839c2 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Novatorius
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f4f31d1e268ab585bba5f29a9d9f467c406839c2 -
Trigger Event:
release
-
Statement type:
File details
Details for the file siren_sdk-0.1.0-py3-none-any.whl.
File metadata
- Download URL: siren_sdk-0.1.0-py3-none-any.whl
- Upload date:
- Size: 18.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f2cc0dfd2357fb2457e3baf8a82a63e87d870a43ed537abf047f57fc72f95507
|
|
| MD5 |
6c351eb154fb034fb56b25b42e1ecd90
|
|
| BLAKE2b-256 |
5539e3504cdc87147aca5a9a9179d775fb82d43722b8736859d629db1161572e
|
Provenance
The following attestation bundles were made for siren_sdk-0.1.0-py3-none-any.whl:
Publisher:
release.yml on Novatorius/siren-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
siren_sdk-0.1.0-py3-none-any.whl -
Subject digest:
f2cc0dfd2357fb2457e3baf8a82a63e87d870a43ed537abf047f57fc72f95507 - Sigstore transparency entry: 2146647946
- Sigstore integration time:
-
Permalink:
Novatorius/siren-python@f4f31d1e268ab585bba5f29a9d9f467c406839c2 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Novatorius
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f4f31d1e268ab585bba5f29a9d9f467c406839c2 -
Trigger Event:
release
-
Statement type: