messagebird-sdk 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,8 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .pytest_cache/
7
+ .ruff_cache/
8
+ .mypy_cache/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bird
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,185 @@
1
+ Metadata-Version: 2.4
2
+ Name: messagebird-sdk
3
+ Version: 0.1.0
4
+ Summary: The official Python SDK for the Bird email platform.
5
+ Project-URL: Homepage, https://github.com/messagebird/bird-sdk-python#readme
6
+ Project-URL: Repository, https://github.com/messagebird/bird-sdk-python.git
7
+ Project-URL: Issues, https://github.com/messagebird/bird-sdk-python/issues
8
+ Author: Bird
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: api,bird,email,sdk,webhooks
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Communications :: Email
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: httpx>=0.27
24
+ Requires-Dist: pydantic>=2.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: pyright>=1.1; extra == 'dev'
27
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
28
+ Requires-Dist: pytest>=8; extra == 'dev'
29
+ Requires-Dist: pyyaml>=6; extra == 'dev'
30
+ Requires-Dist: respx>=0.21; extra == 'dev'
31
+ Requires-Dist: ruff>=0.6; extra == 'dev'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # Bird Python SDK
35
+
36
+ The official Python SDK for the [Bird](https://bird.com) email platform.
37
+
38
+ > **Status:** in development. The PyPI distribution name is `messagebird-sdk`; the import package is `bird`.
39
+
40
+ Requires Python 3.10+.
41
+
42
+ ## Install
43
+
44
+ ```bash
45
+ pip install messagebird-sdk # or: uv add messagebird-sdk
46
+ ```
47
+
48
+ > This SDK is generated from Bird's public OpenAPI bundle inside Bird's internal monorepo, which is the single source of truth; this repository tracks tagged releases. Generation runs in the monorepo, so `make generate` won't work from a clone here — see [CONTRIBUTING.md](./CONTRIBUTING.md).
49
+
50
+ ## Quickstart
51
+
52
+ ```python
53
+ from bird import Bird
54
+
55
+ client = Bird(api_key="bk_eu1_...") # region inferred from the key prefix
56
+
57
+ message = client.email.send(
58
+ from_="hello@acme.com",
59
+ to=["customer@example.com"],
60
+ subject="Welcome to Acme",
61
+ html="<h1>Welcome</h1>",
62
+ )
63
+ print(message.id, message.status)
64
+ ```
65
+
66
+ `api_key` and `base_url` fall back to the `BIRD_API_KEY` / `BIRD_BASE_URL` environment variables, so `Bird()` with no arguments works when they are set. Use the client as a context manager (`with Bird(...) as client:`) to close the underlying HTTP connection pool.
67
+
68
+ ## Email
69
+
70
+ ```python
71
+ # Send
72
+ message = client.email.send(from_="hi@acme.com", to=["c@x.com"], subject="Hi", text="hello")
73
+
74
+ # Fetch
75
+ message = client.email.get("em_01krd…")
76
+
77
+ # List — iterating the page auto-paginates across cursors
78
+ for message in client.email.list(status="delivered"):
79
+ print(message.id, message.status)
80
+ ```
81
+
82
+ ### Client-wide email defaults
83
+
84
+ Defaults fill any unset `send` field; a per-send value always wins.
85
+
86
+ ```python
87
+ client = Bird(
88
+ api_key="bk_eu1_...",
89
+ email_defaults={"from_": "noreply@acme.com", "reply_to": ["support@acme.com"]},
90
+ )
91
+ client.email.send(to=["c@x.com"], subject="Receipt", text="…") # uses noreply@acme.com
92
+ ```
93
+
94
+ ## Webhooks
95
+
96
+ ```python
97
+ from bird import Bird, WebhookVerificationError
98
+
99
+ client = Bird(api_key="bk_eu1_...", webhook_secret="whsec_...")
100
+
101
+ # In your web handler — pass the RAW request body (bytes) and the request headers
102
+ try:
103
+ event = client.webhooks.unwrap(request.body, request.headers)
104
+ except WebhookVerificationError:
105
+ return Response(status=400)
106
+
107
+ if event.root.type == "email.delivered":
108
+ print("delivered:", event.root.data.message_id)
109
+ ```
110
+
111
+ > Endpoint management (registering/listing webhook endpoints) is not in this release; it returns once the delivery substrate stabilises.
112
+
113
+ ## Errors
114
+
115
+ Every failure raises a typed exception rooted at `BirdError`. `APIError` covers anything that goes wrong issuing a request — including transport failures — so a single `except APIError` is enough; `APIStatusError` carries the HTTP `status_code`.
116
+
117
+ ```python
118
+ from bird import APIStatusError, RateLimitError, ValidationError
119
+
120
+ try:
121
+ client.email.send(from_="hi@acme.com", to=["c@x.com"], subject="Hi", text="…")
122
+ except RateLimitError as err:
123
+ retry_in = err.retry_after # seconds, parsed from Retry-After
124
+ except ValidationError as err:
125
+ print(err.status_code, err.details) # 422 + per-field detail
126
+ except APIStatusError as err:
127
+ print(err.status_code, err.code, err.request_id)
128
+ ```
129
+
130
+ Transient failures (timeouts, 429, 5xx) retry automatically with jittered backoff that honors `Retry-After`; a mutation reuses one idempotency key across attempts, so a retried write never double-applies.
131
+
132
+ ## Raw response
133
+
134
+ Reach the status, headers, and `request_id` alongside the parsed model:
135
+
136
+ ```python
137
+ raw = client.email.with_raw_response.send(from_="hi@acme.com", to=["c@x.com"], subject="Hi", text="…")
138
+ print(raw.status_code, raw.request_id)
139
+ message = raw.parse()
140
+ ```
141
+
142
+ ## Async
143
+
144
+ `AsyncBird` mirrors `Bird` method-for-method — `await` each call and `async for` over a list:
145
+
146
+ ```python
147
+ import asyncio
148
+ from bird import AsyncBird
149
+
150
+ async def main() -> None:
151
+ async with AsyncBird(api_key="bk_eu1_...") as client:
152
+ await client.email.send(from_="hi@acme.com", to=["c@x.com"], subject="Hi", text="hello")
153
+ async for message in client.email.list(status="delivered"):
154
+ print(message.id)
155
+
156
+ asyncio.run(main())
157
+ ```
158
+
159
+ ## Configuration
160
+
161
+ | Option | Description |
162
+ | ------------------------ | ------------------------------------------------------------------------------ |
163
+ | `api_key` | API key; falls back to `BIRD_API_KEY`. |
164
+ | `region` / `base_url` | Region (or explicit base URL); falls back to the key prefix / `BIRD_BASE_URL`. |
165
+ | `timeout`, `max_retries` | Request timeout and retry budget; overridable per call via `options`. |
166
+ | `webhook_secret` | Signing secret for `webhooks.unwrap`. |
167
+ | `email_defaults` | Client-wide `send` defaults. |
168
+ | `http_client` | Inject your own `httpx.Client` / `AsyncClient`. |
169
+
170
+ `client.with_options(...)` derives a new client (reusing the connection pool); every method also takes a trailing `options` for per-call `timeout` / `max_retries` / `idempotency_key` / `extra_headers`.
171
+
172
+ ## Escape hatch
173
+
174
+ Any endpoint outside the typed surface is reachable through the verb methods, with the same auth, retries, and idempotency:
175
+
176
+ ```python
177
+ from bird import EmailMessage
178
+
179
+ message = client.get("/v1/email/messages/em_01krd…", cast_to=EmailMessage)
180
+ client.post("/v1/some/new/endpoint", body={"key": "value"})
181
+ ```
182
+
183
+ ## Design
184
+
185
+ The wire models are generated from the OpenAPI spec into `bird._generated`; this package is the hand-written idiomatic layer on top.
@@ -0,0 +1,152 @@
1
+ # Bird Python SDK
2
+
3
+ The official Python SDK for the [Bird](https://bird.com) email platform.
4
+
5
+ > **Status:** in development. The PyPI distribution name is `messagebird-sdk`; the import package is `bird`.
6
+
7
+ Requires Python 3.10+.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pip install messagebird-sdk # or: uv add messagebird-sdk
13
+ ```
14
+
15
+ > This SDK is generated from Bird's public OpenAPI bundle inside Bird's internal monorepo, which is the single source of truth; this repository tracks tagged releases. Generation runs in the monorepo, so `make generate` won't work from a clone here — see [CONTRIBUTING.md](./CONTRIBUTING.md).
16
+
17
+ ## Quickstart
18
+
19
+ ```python
20
+ from bird import Bird
21
+
22
+ client = Bird(api_key="bk_eu1_...") # region inferred from the key prefix
23
+
24
+ message = client.email.send(
25
+ from_="hello@acme.com",
26
+ to=["customer@example.com"],
27
+ subject="Welcome to Acme",
28
+ html="<h1>Welcome</h1>",
29
+ )
30
+ print(message.id, message.status)
31
+ ```
32
+
33
+ `api_key` and `base_url` fall back to the `BIRD_API_KEY` / `BIRD_BASE_URL` environment variables, so `Bird()` with no arguments works when they are set. Use the client as a context manager (`with Bird(...) as client:`) to close the underlying HTTP connection pool.
34
+
35
+ ## Email
36
+
37
+ ```python
38
+ # Send
39
+ message = client.email.send(from_="hi@acme.com", to=["c@x.com"], subject="Hi", text="hello")
40
+
41
+ # Fetch
42
+ message = client.email.get("em_01krd…")
43
+
44
+ # List — iterating the page auto-paginates across cursors
45
+ for message in client.email.list(status="delivered"):
46
+ print(message.id, message.status)
47
+ ```
48
+
49
+ ### Client-wide email defaults
50
+
51
+ Defaults fill any unset `send` field; a per-send value always wins.
52
+
53
+ ```python
54
+ client = Bird(
55
+ api_key="bk_eu1_...",
56
+ email_defaults={"from_": "noreply@acme.com", "reply_to": ["support@acme.com"]},
57
+ )
58
+ client.email.send(to=["c@x.com"], subject="Receipt", text="…") # uses noreply@acme.com
59
+ ```
60
+
61
+ ## Webhooks
62
+
63
+ ```python
64
+ from bird import Bird, WebhookVerificationError
65
+
66
+ client = Bird(api_key="bk_eu1_...", webhook_secret="whsec_...")
67
+
68
+ # In your web handler — pass the RAW request body (bytes) and the request headers
69
+ try:
70
+ event = client.webhooks.unwrap(request.body, request.headers)
71
+ except WebhookVerificationError:
72
+ return Response(status=400)
73
+
74
+ if event.root.type == "email.delivered":
75
+ print("delivered:", event.root.data.message_id)
76
+ ```
77
+
78
+ > Endpoint management (registering/listing webhook endpoints) is not in this release; it returns once the delivery substrate stabilises.
79
+
80
+ ## Errors
81
+
82
+ Every failure raises a typed exception rooted at `BirdError`. `APIError` covers anything that goes wrong issuing a request — including transport failures — so a single `except APIError` is enough; `APIStatusError` carries the HTTP `status_code`.
83
+
84
+ ```python
85
+ from bird import APIStatusError, RateLimitError, ValidationError
86
+
87
+ try:
88
+ client.email.send(from_="hi@acme.com", to=["c@x.com"], subject="Hi", text="…")
89
+ except RateLimitError as err:
90
+ retry_in = err.retry_after # seconds, parsed from Retry-After
91
+ except ValidationError as err:
92
+ print(err.status_code, err.details) # 422 + per-field detail
93
+ except APIStatusError as err:
94
+ print(err.status_code, err.code, err.request_id)
95
+ ```
96
+
97
+ Transient failures (timeouts, 429, 5xx) retry automatically with jittered backoff that honors `Retry-After`; a mutation reuses one idempotency key across attempts, so a retried write never double-applies.
98
+
99
+ ## Raw response
100
+
101
+ Reach the status, headers, and `request_id` alongside the parsed model:
102
+
103
+ ```python
104
+ raw = client.email.with_raw_response.send(from_="hi@acme.com", to=["c@x.com"], subject="Hi", text="…")
105
+ print(raw.status_code, raw.request_id)
106
+ message = raw.parse()
107
+ ```
108
+
109
+ ## Async
110
+
111
+ `AsyncBird` mirrors `Bird` method-for-method — `await` each call and `async for` over a list:
112
+
113
+ ```python
114
+ import asyncio
115
+ from bird import AsyncBird
116
+
117
+ async def main() -> None:
118
+ async with AsyncBird(api_key="bk_eu1_...") as client:
119
+ await client.email.send(from_="hi@acme.com", to=["c@x.com"], subject="Hi", text="hello")
120
+ async for message in client.email.list(status="delivered"):
121
+ print(message.id)
122
+
123
+ asyncio.run(main())
124
+ ```
125
+
126
+ ## Configuration
127
+
128
+ | Option | Description |
129
+ | ------------------------ | ------------------------------------------------------------------------------ |
130
+ | `api_key` | API key; falls back to `BIRD_API_KEY`. |
131
+ | `region` / `base_url` | Region (or explicit base URL); falls back to the key prefix / `BIRD_BASE_URL`. |
132
+ | `timeout`, `max_retries` | Request timeout and retry budget; overridable per call via `options`. |
133
+ | `webhook_secret` | Signing secret for `webhooks.unwrap`. |
134
+ | `email_defaults` | Client-wide `send` defaults. |
135
+ | `http_client` | Inject your own `httpx.Client` / `AsyncClient`. |
136
+
137
+ `client.with_options(...)` derives a new client (reusing the connection pool); every method also takes a trailing `options` for per-call `timeout` / `max_retries` / `idempotency_key` / `extra_headers`.
138
+
139
+ ## Escape hatch
140
+
141
+ Any endpoint outside the typed surface is reachable through the verb methods, with the same auth, retries, and idempotency:
142
+
143
+ ```python
144
+ from bird import EmailMessage
145
+
146
+ message = client.get("/v1/email/messages/em_01krd…", cast_to=EmailMessage)
147
+ client.post("/v1/some/new/endpoint", body={"key": "value"})
148
+ ```
149
+
150
+ ## Design
151
+
152
+ The wire models are generated from the OpenAPI spec into `bird._generated`; this package is the hand-written idiomatic layer on top.
@@ -0,0 +1,70 @@
1
+ # Python SDK for the Bird email platform (ADR-0045).
2
+ # Distribution name is `messagebird-sdk` on PyPI; the import package is `bird`.
3
+ [build-system]
4
+ requires = ["hatchling"]
5
+ build-backend = "hatchling.build"
6
+
7
+ [project]
8
+ name = "messagebird-sdk"
9
+ description = "The official Python SDK for the Bird email platform."
10
+ readme = "README.md"
11
+ license = "MIT"
12
+ authors = [{ name = "Bird" }]
13
+ dynamic = ["version"]
14
+ requires-python = ">=3.10"
15
+ keywords = ["bird", "email", "webhooks", "sdk", "api"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Communications :: Email",
26
+ "Typing :: Typed",
27
+ ]
28
+ dependencies = [
29
+ "pydantic>=2.0",
30
+ "httpx>=0.27",
31
+ ]
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/messagebird/bird-sdk-python#readme"
35
+ Repository = "https://github.com/messagebird/bird-sdk-python.git"
36
+ Issues = "https://github.com/messagebird/bird-sdk-python/issues"
37
+
38
+ [project.optional-dependencies]
39
+ dev = [
40
+ "pytest>=8",
41
+ "pytest-asyncio>=0.24",
42
+ "respx>=0.21",
43
+ "ruff>=0.6",
44
+ "pyright>=1.1",
45
+ "pyyaml>=6",
46
+ ]
47
+
48
+ [tool.hatch.version]
49
+ path = "src/bird/_version.py"
50
+
51
+ [tool.hatch.build.targets.wheel]
52
+ packages = ["src/bird"]
53
+
54
+ # Curate the sdist: ship the package, README, and LICENSE — not the internal agent
55
+ # docs (AGENTS.md / CLAUDE.md), tests, or the generator script.
56
+ [tool.hatch.build.targets.sdist]
57
+ include = ["/src/bird", "/README.md", "/LICENSE"]
58
+
59
+ [tool.pytest.ini_options]
60
+ asyncio_mode = "auto"
61
+
62
+ [tool.ruff]
63
+ line-length = 100
64
+ target-version = "py310"
65
+ extend-exclude = ["src/bird/_generated.py"] # generated; never hand-edited
66
+
67
+ [tool.pyright]
68
+ include = ["src", "tests", "examples"]
69
+ ignore = ["src/bird/_generated.py"] # generated; not held to the hand-written type bar
70
+ pythonVersion = "3.10"
@@ -0,0 +1,65 @@
1
+ """The official Python SDK for the Bird email platform (ADR-0045).
2
+
3
+ The wire models are generated from the OpenAPI spec into ``bird._generated`` and
4
+ never hand-edited; this package is the hand-written, idiomatic layer on top — a
5
+ synchronous ``Bird`` client and an asynchronous ``AsyncBird`` client, a typed
6
+ exception hierarchy, safe retries, pagination, and webhook verification.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from bird._client import AsyncBird, Bird
12
+ from bird._response import APIResponse
13
+ from bird._types import (
14
+ Attachment,
15
+ EmailDefaults,
16
+ EmailListParams,
17
+ EmailSendParams,
18
+ RequestOptions,
19
+ )
20
+ from bird._generated import (
21
+ EmailMessage,
22
+ WebhookEvent,
23
+ WebhookEventType,
24
+ )
25
+ from bird._exceptions import (
26
+ APIConnectionError,
27
+ APIError,
28
+ APIStatusError,
29
+ APITimeoutError,
30
+ BirdError,
31
+ ErrorDetail,
32
+ ErrorType,
33
+ RateLimitError,
34
+ ValidationError,
35
+ WebhookVerificationError,
36
+ )
37
+ from bird.pagination import AsyncPage, SyncPage
38
+ from bird._version import __version__
39
+
40
+ __all__ = [
41
+ "Bird",
42
+ "AsyncBird",
43
+ "RequestOptions",
44
+ "EmailDefaults",
45
+ "Attachment",
46
+ "EmailSendParams",
47
+ "EmailListParams",
48
+ "APIResponse",
49
+ "SyncPage",
50
+ "AsyncPage",
51
+ "EmailMessage",
52
+ "WebhookEvent",
53
+ "WebhookEventType",
54
+ "BirdError",
55
+ "APIError",
56
+ "APIStatusError",
57
+ "RateLimitError",
58
+ "ValidationError",
59
+ "APIConnectionError",
60
+ "APITimeoutError",
61
+ "WebhookVerificationError",
62
+ "ErrorDetail",
63
+ "ErrorType",
64
+ "__version__",
65
+ ]