hookbridge 2.0.0__tar.gz → 2.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.
- {hookbridge-2.0.0 → hookbridge-2.1.0}/.gitignore +3 -0
- {hookbridge-2.0.0 → hookbridge-2.1.0}/PKG-INFO +3 -2
- {hookbridge-2.0.0 → hookbridge-2.1.0}/hookbridge/__init__.py +7 -1
- {hookbridge-2.0.0 → hookbridge-2.1.0}/hookbridge/client.py +93 -0
- {hookbridge-2.0.0 → hookbridge-2.1.0}/hookbridge/types.py +40 -1
- {hookbridge-2.0.0 → hookbridge-2.1.0}/pyproject.toml +2 -1
- hookbridge-2.1.0/tests/test_openapi_sync_2026_11.py +750 -0
- {hookbridge-2.0.0 → hookbridge-2.1.0}/.claude/settings.local.json +0 -0
- {hookbridge-2.0.0 → hookbridge-2.1.0}/README.md +0 -0
- {hookbridge-2.0.0 → hookbridge-2.1.0}/hookbridge/errors.py +0 -0
- {hookbridge-2.0.0 → hookbridge-2.1.0}/hookbridge/py.typed +0 -0
- {hookbridge-2.0.0 → hookbridge-2.1.0}/requirements-dev.txt +0 -0
- {hookbridge-2.0.0 → hookbridge-2.1.0}/requirements.txt +0 -0
- {hookbridge-2.0.0 → hookbridge-2.1.0}/tests/__init__.py +0 -0
- {hookbridge-2.0.0 → hookbridge-2.1.0}/tests/conftest.py +0 -0
- {hookbridge-2.0.0 → hookbridge-2.1.0}/tests/test_async_client.py +0 -0
- {hookbridge-2.0.0 → hookbridge-2.1.0}/tests/test_client.py +0 -0
- {hookbridge-2.0.0 → hookbridge-2.1.0}/tests/test_client_spec_parity.py +0 -0
- {hookbridge-2.0.0 → hookbridge-2.1.0}/tests/test_errors.py +0 -0
- {hookbridge-2.0.0 → hookbridge-2.1.0}/tests/test_removed_session_only_methods.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: hookbridge
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Official HookBridge SDK for Python
|
|
5
5
|
Project-URL: Homepage, https://hookbridge.io
|
|
6
6
|
Project-URL: Documentation, https://hookbridge.io/docs/sdks/python
|
|
@@ -19,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
23
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
23
24
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
25
|
Classifier: Typing :: Typed
|
|
@@ -39,7 +39,9 @@ from hookbridge.types import (
|
|
|
39
39
|
DLQMessagesResponse,
|
|
40
40
|
Endpoint,
|
|
41
41
|
EndpointSummary,
|
|
42
|
+
ExportDirection,
|
|
42
43
|
ExportRecord,
|
|
44
|
+
HMACSignatureEncoding,
|
|
43
45
|
InboundEndpoint,
|
|
44
46
|
InboundEndpointMode,
|
|
45
47
|
InboundEndpointSummary,
|
|
@@ -81,6 +83,7 @@ from hookbridge.types import (
|
|
|
81
83
|
ReplayAllMessagesResponse,
|
|
82
84
|
ReplayBatchMessagesResponse,
|
|
83
85
|
ReplayBatchResult,
|
|
86
|
+
ReplayErrorCode,
|
|
84
87
|
ReplayMessageResponse,
|
|
85
88
|
RotateSecretResponse,
|
|
86
89
|
SendWebhookResponse,
|
|
@@ -95,7 +98,7 @@ from hookbridge.types import (
|
|
|
95
98
|
UsageHistoryRow,
|
|
96
99
|
)
|
|
97
100
|
|
|
98
|
-
__version__ = "2.
|
|
101
|
+
__version__ = "2.1.0"
|
|
99
102
|
|
|
100
103
|
__all__ = [
|
|
101
104
|
"HookBridge",
|
|
@@ -123,7 +126,9 @@ __all__ = [
|
|
|
123
126
|
"DLQMessagesResponse",
|
|
124
127
|
"Endpoint",
|
|
125
128
|
"EndpointSummary",
|
|
129
|
+
"ExportDirection",
|
|
126
130
|
"ExportRecord",
|
|
131
|
+
"HMACSignatureEncoding",
|
|
127
132
|
"InboundEndpoint",
|
|
128
133
|
"InboundEndpointMode",
|
|
129
134
|
"InboundEndpointSummary",
|
|
@@ -164,6 +169,7 @@ __all__ = [
|
|
|
164
169
|
"ReplayAllMessagesResponse",
|
|
165
170
|
"ReplayBatchMessagesResponse",
|
|
166
171
|
"ReplayBatchResult",
|
|
172
|
+
"ReplayErrorCode",
|
|
167
173
|
"ReplayMessageResponse",
|
|
168
174
|
"ReplayableMessageStatus",
|
|
169
175
|
"RotateSecretResponse",
|
|
@@ -45,7 +45,9 @@ from hookbridge.types import (
|
|
|
45
45
|
DLQMessagesResponse,
|
|
46
46
|
Endpoint,
|
|
47
47
|
EndpointSummary,
|
|
48
|
+
ExportDirection,
|
|
48
49
|
ExportRecord,
|
|
50
|
+
HMACSignatureEncoding,
|
|
49
51
|
InboundEndpoint,
|
|
50
52
|
InboundEndpointSummary,
|
|
51
53
|
InboundLogEntry,
|
|
@@ -166,6 +168,14 @@ def _parse_message(data: dict[str, Any]) -> Message:
|
|
|
166
168
|
deleted_at=_parse_datetime(data["deleted_at"]) if data.get("deleted_at") else None,
|
|
167
169
|
deleted_by_user_id=data.get("deleted_by_user_id"),
|
|
168
170
|
deleted_by_api_key_id=data.get("deleted_by_api_key_id"),
|
|
171
|
+
first_attempt_at=(
|
|
172
|
+
_parse_datetime(data["first_attempt_at"]) if data.get("first_attempt_at") else None
|
|
173
|
+
),
|
|
174
|
+
delivered_at=_parse_datetime(data["delivered_at"]) if data.get("delivered_at") else None,
|
|
175
|
+
processing_ms=data.get("processing_ms"),
|
|
176
|
+
total_delivery_ms=data.get("total_delivery_ms"),
|
|
177
|
+
queue_wait_ms=data.get("queue_wait_ms"),
|
|
178
|
+
retention_hours=data.get("retention_hours"),
|
|
169
179
|
)
|
|
170
180
|
|
|
171
181
|
|
|
@@ -186,6 +196,12 @@ def _parse_message_summary(data: dict[str, Any]) -> MessageSummary:
|
|
|
186
196
|
response_latency_ms=data.get("response_latency_ms"),
|
|
187
197
|
last_error=data.get("last_error"),
|
|
188
198
|
deleted_at=_parse_datetime(data["deleted_at"]) if data.get("deleted_at") else None,
|
|
199
|
+
first_attempt_at=(
|
|
200
|
+
_parse_datetime(data["first_attempt_at"]) if data.get("first_attempt_at") else None
|
|
201
|
+
),
|
|
202
|
+
processing_ms=data.get("processing_ms"),
|
|
203
|
+
total_delivery_ms=data.get("total_delivery_ms"),
|
|
204
|
+
queue_wait_ms=data.get("queue_wait_ms"),
|
|
189
205
|
)
|
|
190
206
|
|
|
191
207
|
|
|
@@ -198,6 +214,8 @@ def _parse_metrics(data: dict[str, Any]) -> Metrics:
|
|
|
198
214
|
retries=data["retries"],
|
|
199
215
|
success_rate=data["success_rate"],
|
|
200
216
|
avg_latency_ms=data["avg_latency_ms"],
|
|
217
|
+
avg_delivery_time_ms=data.get("avg_delivery_time_ms"),
|
|
218
|
+
avg_queue_wait_ms=data.get("avg_queue_wait_ms"),
|
|
201
219
|
)
|
|
202
220
|
|
|
203
221
|
|
|
@@ -247,11 +265,15 @@ def _parse_subscription(data: dict[str, Any]) -> Subscription:
|
|
|
247
265
|
max_endpoints=limits["max_endpoints"],
|
|
248
266
|
retention_days=limits["retention_days"],
|
|
249
267
|
max_retries=limits.get("max_retries"),
|
|
268
|
+
overage_rate_per_message=limits.get("overage_rate_per_message"),
|
|
250
269
|
),
|
|
251
270
|
usage=SubscriptionUsage(
|
|
252
271
|
messages_used=usage["messages_used"],
|
|
253
272
|
period_start=_parse_datetime(usage["period_start"]),
|
|
254
273
|
period_end=_parse_datetime(usage["period_end"]),
|
|
274
|
+
overage_count=usage.get("overage_count"),
|
|
275
|
+
overage_rate=usage.get("overage_rate"),
|
|
276
|
+
estimated_overage_charge=usage.get("estimated_overage_charge"),
|
|
255
277
|
),
|
|
256
278
|
cancel_at_period_end=data.get("cancel_at_period_end"),
|
|
257
279
|
current_period_end=(
|
|
@@ -281,6 +303,8 @@ def _parse_endpoint(data: dict[str, Any]) -> Endpoint:
|
|
|
281
303
|
rate_limit_rps=data.get("rate_limit_rps"),
|
|
282
304
|
burst=data.get("burst"),
|
|
283
305
|
headers=data.get("headers"),
|
|
306
|
+
ephemeral=data.get("ephemeral"),
|
|
307
|
+
expires_at=_parse_datetime(data["expires_at"]) if data.get("expires_at") else None,
|
|
284
308
|
)
|
|
285
309
|
|
|
286
310
|
|
|
@@ -291,6 +315,8 @@ def _parse_endpoint_summary(data: dict[str, Any]) -> EndpointSummary:
|
|
|
291
315
|
created_at=_parse_datetime(data["created_at"]),
|
|
292
316
|
paused=data["paused"],
|
|
293
317
|
description=data.get("description"),
|
|
318
|
+
ephemeral=data.get("ephemeral"),
|
|
319
|
+
expires_at=_parse_datetime(data["expires_at"]) if data.get("expires_at") else None,
|
|
294
320
|
)
|
|
295
321
|
|
|
296
322
|
|
|
@@ -314,6 +340,7 @@ def _parse_replay_batch_messages(data: dict[str, Any]) -> ReplayBatchMessagesRes
|
|
|
314
340
|
message_id=result["message_id"],
|
|
315
341
|
status=result["status"],
|
|
316
342
|
error=result.get("error"),
|
|
343
|
+
error_code=result.get("error_code"),
|
|
317
344
|
)
|
|
318
345
|
for result in data.get("results", [])
|
|
319
346
|
],
|
|
@@ -387,6 +414,10 @@ def _parse_inbound_endpoint(data: dict[str, Any]) -> InboundEndpoint:
|
|
|
387
414
|
signing_enabled=data["signing_enabled"],
|
|
388
415
|
created_at=_parse_datetime(data["created_at"]),
|
|
389
416
|
updated_at=_parse_datetime(data["updated_at"]),
|
|
417
|
+
hmac_signature_encoding=data.get("hmac_signature_encoding"),
|
|
418
|
+
ingest_url=data.get("ingest_url"),
|
|
419
|
+
ephemeral=data.get("ephemeral"),
|
|
420
|
+
expires_at=_parse_datetime(data["expires_at"]) if data.get("expires_at") else None,
|
|
390
421
|
)
|
|
391
422
|
|
|
392
423
|
|
|
@@ -420,6 +451,7 @@ def _parse_pull_endpoint(data: dict[str, Any]) -> PullEndpoint:
|
|
|
420
451
|
token_query_param=data.get("token_query_param"),
|
|
421
452
|
verify_hmac=data.get("verify_hmac"),
|
|
422
453
|
hmac_header_name=data.get("hmac_header_name"),
|
|
454
|
+
hmac_signature_encoding=data.get("hmac_signature_encoding"),
|
|
423
455
|
timestamp_header_name=data.get("timestamp_header_name"),
|
|
424
456
|
timestamp_ttl_seconds=data.get("timestamp_ttl_seconds"),
|
|
425
457
|
verify_ip_allowlist=data.get("verify_ip_allowlist"),
|
|
@@ -524,6 +556,8 @@ def _parse_inbound_endpoint_summary(data: dict[str, Any]) -> InboundEndpointSumm
|
|
|
524
556
|
active=data["active"],
|
|
525
557
|
paused=data["paused"],
|
|
526
558
|
created_at=_parse_datetime(data["created_at"]),
|
|
559
|
+
ephemeral=data.get("ephemeral"),
|
|
560
|
+
expires_at=_parse_datetime(data["expires_at"]) if data.get("expires_at") else None,
|
|
527
561
|
)
|
|
528
562
|
|
|
529
563
|
|
|
@@ -632,6 +666,7 @@ def _parse_inbound_message(data: dict[str, Any]) -> InboundMessage:
|
|
|
632
666
|
deleted_at=_parse_datetime(data["deleted_at"]) if data.get("deleted_at") else None,
|
|
633
667
|
deleted_by_user_id=data.get("deleted_by_user_id"),
|
|
634
668
|
deleted_by_api_key_id=data.get("deleted_by_api_key_id"),
|
|
669
|
+
retention_hours=data.get("retention_hours"),
|
|
635
670
|
)
|
|
636
671
|
|
|
637
672
|
|
|
@@ -645,6 +680,8 @@ def _parse_export_record(data: dict[str, Any]) -> ExportRecord:
|
|
|
645
680
|
created_at=_parse_datetime(data["created_at"]),
|
|
646
681
|
filter_status=data.get("filter_status"),
|
|
647
682
|
filter_endpoint_id=data.get("filter_endpoint_id"),
|
|
683
|
+
filter_inbound_endpoint_id=data.get("filter_inbound_endpoint_id"),
|
|
684
|
+
direction=data.get("direction"),
|
|
648
685
|
row_count=data.get("row_count"),
|
|
649
686
|
file_size_bytes=data.get("file_size_bytes"),
|
|
650
687
|
error_message=data.get("error_message"),
|
|
@@ -807,12 +844,15 @@ class HookBridge:
|
|
|
807
844
|
*,
|
|
808
845
|
headers: Optional[dict[str, str]] = None,
|
|
809
846
|
idempotency_key: Optional[str] = None,
|
|
847
|
+
retention_hours: Optional[int] = None,
|
|
810
848
|
) -> SendWebhookResponse:
|
|
811
849
|
body: dict[str, Any] = {"endpoint_id": endpoint_id, "payload": payload}
|
|
812
850
|
if headers:
|
|
813
851
|
body["headers"] = headers
|
|
814
852
|
if idempotency_key:
|
|
815
853
|
body["idempotency_key"] = idempotency_key
|
|
854
|
+
if retention_hours is not None:
|
|
855
|
+
body["retention_hours"] = retention_hours
|
|
816
856
|
response = self._request("POST", "/v1/webhooks/send", json=body, use_send_client=True)
|
|
817
857
|
data = response["data"]
|
|
818
858
|
return SendWebhookResponse(message_id=data["message_id"], status=data["status"])
|
|
@@ -938,6 +978,8 @@ class HookBridge:
|
|
|
938
978
|
rate_limit_rps: int = 0,
|
|
939
979
|
burst: int = 0,
|
|
940
980
|
headers: Optional[dict[str, str]] = None,
|
|
981
|
+
ephemeral: Optional[bool] = None,
|
|
982
|
+
ttl_minutes: Optional[int] = None,
|
|
941
983
|
) -> CreateEndpointResponse:
|
|
942
984
|
body: dict[str, Any] = {"url": url}
|
|
943
985
|
if description:
|
|
@@ -948,6 +990,10 @@ class HookBridge:
|
|
|
948
990
|
body["burst"] = burst
|
|
949
991
|
if headers:
|
|
950
992
|
body["headers"] = headers
|
|
993
|
+
if ephemeral is not None:
|
|
994
|
+
body["ephemeral"] = ephemeral
|
|
995
|
+
if ttl_minutes is not None:
|
|
996
|
+
body["ttl_minutes"] = ttl_minutes
|
|
951
997
|
data = self._request("POST", "/v1/endpoints", json=body)["data"]
|
|
952
998
|
return CreateEndpointResponse(
|
|
953
999
|
id=data["id"],
|
|
@@ -957,6 +1003,8 @@ class HookBridge:
|
|
|
957
1003
|
description=data.get("description"),
|
|
958
1004
|
signing_key_id=data.get("signing_key_id"),
|
|
959
1005
|
key_hint=data.get("key_hint"),
|
|
1006
|
+
ephemeral=data.get("ephemeral"),
|
|
1007
|
+
expires_at=_parse_datetime(data["expires_at"]) if data.get("expires_at") else None,
|
|
960
1008
|
)
|
|
961
1009
|
|
|
962
1010
|
def get_endpoint(self, endpoint_id: str) -> Endpoint:
|
|
@@ -1041,6 +1089,7 @@ class HookBridge:
|
|
|
1041
1089
|
token_value: Optional[str] = None,
|
|
1042
1090
|
verify_hmac: Optional[bool] = None,
|
|
1043
1091
|
hmac_header_name: Optional[str] = None,
|
|
1092
|
+
hmac_signature_encoding: Optional[HMACSignatureEncoding] = None,
|
|
1044
1093
|
hmac_secret: Optional[str] = None,
|
|
1045
1094
|
timestamp_header_name: Optional[str] = None,
|
|
1046
1095
|
timestamp_ttl_seconds: Optional[int] = None,
|
|
@@ -1063,6 +1112,7 @@ class HookBridge:
|
|
|
1063
1112
|
"token_value": token_value,
|
|
1064
1113
|
"verify_hmac": verify_hmac,
|
|
1065
1114
|
"hmac_header_name": hmac_header_name,
|
|
1115
|
+
"hmac_signature_encoding": hmac_signature_encoding,
|
|
1066
1116
|
"hmac_secret": hmac_secret,
|
|
1067
1117
|
"timestamp_header_name": timestamp_header_name,
|
|
1068
1118
|
"timestamp_ttl_seconds": timestamp_ttl_seconds,
|
|
@@ -1256,6 +1306,7 @@ class HookBridge:
|
|
|
1256
1306
|
token_value: Optional[str] = None,
|
|
1257
1307
|
verify_hmac: Optional[bool] = None,
|
|
1258
1308
|
hmac_header_name: Optional[str] = None,
|
|
1309
|
+
hmac_signature_encoding: Optional[HMACSignatureEncoding] = None,
|
|
1259
1310
|
hmac_secret: Optional[str] = None,
|
|
1260
1311
|
timestamp_header_name: Optional[str] = None,
|
|
1261
1312
|
timestamp_ttl_seconds: Optional[int] = None,
|
|
@@ -1264,6 +1315,8 @@ class HookBridge:
|
|
|
1264
1315
|
idempotency_header_names: Optional[list[str]] = None,
|
|
1265
1316
|
ingest_response_code: Optional[int] = None,
|
|
1266
1317
|
signing_enabled: Optional[bool] = None,
|
|
1318
|
+
ephemeral: Optional[bool] = None,
|
|
1319
|
+
ttl_minutes: Optional[int] = None,
|
|
1267
1320
|
) -> CreateInboundEndpointResponse:
|
|
1268
1321
|
body: dict[str, Any] = {}
|
|
1269
1322
|
if url is not None:
|
|
@@ -1278,6 +1331,7 @@ class HookBridge:
|
|
|
1278
1331
|
"token_value": token_value,
|
|
1279
1332
|
"verify_hmac": verify_hmac,
|
|
1280
1333
|
"hmac_header_name": hmac_header_name,
|
|
1334
|
+
"hmac_signature_encoding": hmac_signature_encoding,
|
|
1281
1335
|
"hmac_secret": hmac_secret,
|
|
1282
1336
|
"timestamp_header_name": timestamp_header_name,
|
|
1283
1337
|
"timestamp_ttl_seconds": timestamp_ttl_seconds,
|
|
@@ -1286,6 +1340,8 @@ class HookBridge:
|
|
|
1286
1340
|
"idempotency_header_names": idempotency_header_names,
|
|
1287
1341
|
"ingest_response_code": ingest_response_code,
|
|
1288
1342
|
"signing_enabled": signing_enabled,
|
|
1343
|
+
"ephemeral": ephemeral,
|
|
1344
|
+
"ttl_minutes": ttl_minutes,
|
|
1289
1345
|
}.items():
|
|
1290
1346
|
if value is not None:
|
|
1291
1347
|
body[key] = value
|
|
@@ -1301,6 +1357,9 @@ class HookBridge:
|
|
|
1301
1357
|
signing_key_id=data.get("signing_key_id"),
|
|
1302
1358
|
signing_secret=data.get("signing_secret"),
|
|
1303
1359
|
key_hint=data.get("key_hint"),
|
|
1360
|
+
hmac_signature_encoding=data.get("hmac_signature_encoding"),
|
|
1361
|
+
ephemeral=data.get("ephemeral"),
|
|
1362
|
+
expires_at=_parse_datetime(data["expires_at"]) if data.get("expires_at") else None,
|
|
1304
1363
|
)
|
|
1305
1364
|
|
|
1306
1365
|
def list_inbound_endpoints(
|
|
@@ -1497,6 +1556,8 @@ class HookBridge:
|
|
|
1497
1556
|
end_time: Union[datetime, str],
|
|
1498
1557
|
status: Optional[MessageStatus] = None,
|
|
1499
1558
|
endpoint_id: Optional[str] = None,
|
|
1559
|
+
direction: Optional[ExportDirection] = None,
|
|
1560
|
+
inbound_endpoint_id: Optional[str] = None,
|
|
1500
1561
|
) -> ExportRecord:
|
|
1501
1562
|
body: dict[str, Any] = {
|
|
1502
1563
|
"start_time": _json_value(start_time),
|
|
@@ -1506,6 +1567,10 @@ class HookBridge:
|
|
|
1506
1567
|
body["status"] = status
|
|
1507
1568
|
if endpoint_id is not None:
|
|
1508
1569
|
body["endpoint_id"] = endpoint_id
|
|
1570
|
+
if direction is not None:
|
|
1571
|
+
body["direction"] = direction
|
|
1572
|
+
if inbound_endpoint_id is not None:
|
|
1573
|
+
body["inbound_endpoint_id"] = inbound_endpoint_id
|
|
1509
1574
|
return _parse_export_record(self._request("POST", "/v1/exports", json=body)["data"])
|
|
1510
1575
|
|
|
1511
1576
|
def list_exports(self) -> list[ExportRecord]:
|
|
@@ -1728,12 +1793,15 @@ class AsyncHookBridge:
|
|
|
1728
1793
|
*,
|
|
1729
1794
|
headers: Optional[dict[str, str]] = None,
|
|
1730
1795
|
idempotency_key: Optional[str] = None,
|
|
1796
|
+
retention_hours: Optional[int] = None,
|
|
1731
1797
|
) -> SendWebhookResponse:
|
|
1732
1798
|
body: dict[str, Any] = {"endpoint_id": endpoint_id, "payload": payload}
|
|
1733
1799
|
if headers:
|
|
1734
1800
|
body["headers"] = headers
|
|
1735
1801
|
if idempotency_key:
|
|
1736
1802
|
body["idempotency_key"] = idempotency_key
|
|
1803
|
+
if retention_hours is not None:
|
|
1804
|
+
body["retention_hours"] = retention_hours
|
|
1737
1805
|
response = await self._request("POST", "/v1/webhooks/send", json=body, use_send_client=True)
|
|
1738
1806
|
data = response["data"]
|
|
1739
1807
|
return SendWebhookResponse(message_id=data["message_id"], status=data["status"])
|
|
@@ -1861,6 +1929,8 @@ class AsyncHookBridge:
|
|
|
1861
1929
|
rate_limit_rps: int = 0,
|
|
1862
1930
|
burst: int = 0,
|
|
1863
1931
|
headers: Optional[dict[str, str]] = None,
|
|
1932
|
+
ephemeral: Optional[bool] = None,
|
|
1933
|
+
ttl_minutes: Optional[int] = None,
|
|
1864
1934
|
) -> CreateEndpointResponse:
|
|
1865
1935
|
body: dict[str, Any] = {"url": url}
|
|
1866
1936
|
if description:
|
|
@@ -1871,6 +1941,10 @@ class AsyncHookBridge:
|
|
|
1871
1941
|
body["burst"] = burst
|
|
1872
1942
|
if headers:
|
|
1873
1943
|
body["headers"] = headers
|
|
1944
|
+
if ephemeral is not None:
|
|
1945
|
+
body["ephemeral"] = ephemeral
|
|
1946
|
+
if ttl_minutes is not None:
|
|
1947
|
+
body["ttl_minutes"] = ttl_minutes
|
|
1874
1948
|
data = (await self._request("POST", "/v1/endpoints", json=body))["data"]
|
|
1875
1949
|
return CreateEndpointResponse(
|
|
1876
1950
|
id=data["id"],
|
|
@@ -1880,6 +1954,8 @@ class AsyncHookBridge:
|
|
|
1880
1954
|
description=data.get("description"),
|
|
1881
1955
|
signing_key_id=data.get("signing_key_id"),
|
|
1882
1956
|
key_hint=data.get("key_hint"),
|
|
1957
|
+
ephemeral=data.get("ephemeral"),
|
|
1958
|
+
expires_at=_parse_datetime(data["expires_at"]) if data.get("expires_at") else None,
|
|
1883
1959
|
)
|
|
1884
1960
|
|
|
1885
1961
|
async def get_endpoint(self, endpoint_id: str) -> Endpoint:
|
|
@@ -1996,6 +2072,7 @@ class AsyncHookBridge:
|
|
|
1996
2072
|
token_value: Optional[str] = None,
|
|
1997
2073
|
verify_hmac: Optional[bool] = None,
|
|
1998
2074
|
hmac_header_name: Optional[str] = None,
|
|
2075
|
+
hmac_signature_encoding: Optional[HMACSignatureEncoding] = None,
|
|
1999
2076
|
hmac_secret: Optional[str] = None,
|
|
2000
2077
|
timestamp_header_name: Optional[str] = None,
|
|
2001
2078
|
timestamp_ttl_seconds: Optional[int] = None,
|
|
@@ -2018,6 +2095,7 @@ class AsyncHookBridge:
|
|
|
2018
2095
|
"token_value": token_value,
|
|
2019
2096
|
"verify_hmac": verify_hmac,
|
|
2020
2097
|
"hmac_header_name": hmac_header_name,
|
|
2098
|
+
"hmac_signature_encoding": hmac_signature_encoding,
|
|
2021
2099
|
"hmac_secret": hmac_secret,
|
|
2022
2100
|
"timestamp_header_name": timestamp_header_name,
|
|
2023
2101
|
"timestamp_ttl_seconds": timestamp_ttl_seconds,
|
|
@@ -2188,6 +2266,7 @@ class AsyncHookBridge:
|
|
|
2188
2266
|
token_value: Optional[str] = None,
|
|
2189
2267
|
verify_hmac: Optional[bool] = None,
|
|
2190
2268
|
hmac_header_name: Optional[str] = None,
|
|
2269
|
+
hmac_signature_encoding: Optional[HMACSignatureEncoding] = None,
|
|
2191
2270
|
hmac_secret: Optional[str] = None,
|
|
2192
2271
|
timestamp_header_name: Optional[str] = None,
|
|
2193
2272
|
timestamp_ttl_seconds: Optional[int] = None,
|
|
@@ -2196,6 +2275,8 @@ class AsyncHookBridge:
|
|
|
2196
2275
|
idempotency_header_names: Optional[list[str]] = None,
|
|
2197
2276
|
ingest_response_code: Optional[int] = None,
|
|
2198
2277
|
signing_enabled: Optional[bool] = None,
|
|
2278
|
+
ephemeral: Optional[bool] = None,
|
|
2279
|
+
ttl_minutes: Optional[int] = None,
|
|
2199
2280
|
) -> CreateInboundEndpointResponse:
|
|
2200
2281
|
body: dict[str, Any] = {}
|
|
2201
2282
|
if url is not None:
|
|
@@ -2210,6 +2291,7 @@ class AsyncHookBridge:
|
|
|
2210
2291
|
"token_value": token_value,
|
|
2211
2292
|
"verify_hmac": verify_hmac,
|
|
2212
2293
|
"hmac_header_name": hmac_header_name,
|
|
2294
|
+
"hmac_signature_encoding": hmac_signature_encoding,
|
|
2213
2295
|
"hmac_secret": hmac_secret,
|
|
2214
2296
|
"timestamp_header_name": timestamp_header_name,
|
|
2215
2297
|
"timestamp_ttl_seconds": timestamp_ttl_seconds,
|
|
@@ -2218,6 +2300,8 @@ class AsyncHookBridge:
|
|
|
2218
2300
|
"idempotency_header_names": idempotency_header_names,
|
|
2219
2301
|
"ingest_response_code": ingest_response_code,
|
|
2220
2302
|
"signing_enabled": signing_enabled,
|
|
2303
|
+
"ephemeral": ephemeral,
|
|
2304
|
+
"ttl_minutes": ttl_minutes,
|
|
2221
2305
|
}.items():
|
|
2222
2306
|
if value is not None:
|
|
2223
2307
|
body[key] = value
|
|
@@ -2233,6 +2317,9 @@ class AsyncHookBridge:
|
|
|
2233
2317
|
signing_key_id=data.get("signing_key_id"),
|
|
2234
2318
|
signing_secret=data.get("signing_secret"),
|
|
2235
2319
|
key_hint=data.get("key_hint"),
|
|
2320
|
+
hmac_signature_encoding=data.get("hmac_signature_encoding"),
|
|
2321
|
+
ephemeral=data.get("ephemeral"),
|
|
2322
|
+
expires_at=_parse_datetime(data["expires_at"]) if data.get("expires_at") else None,
|
|
2236
2323
|
)
|
|
2237
2324
|
|
|
2238
2325
|
async def list_inbound_endpoints(
|
|
@@ -2435,6 +2522,8 @@ class AsyncHookBridge:
|
|
|
2435
2522
|
end_time: Union[datetime, str],
|
|
2436
2523
|
status: Optional[MessageStatus] = None,
|
|
2437
2524
|
endpoint_id: Optional[str] = None,
|
|
2525
|
+
direction: Optional[ExportDirection] = None,
|
|
2526
|
+
inbound_endpoint_id: Optional[str] = None,
|
|
2438
2527
|
) -> ExportRecord:
|
|
2439
2528
|
body: dict[str, Any] = {
|
|
2440
2529
|
"start_time": _json_value(start_time),
|
|
@@ -2444,6 +2533,10 @@ class AsyncHookBridge:
|
|
|
2444
2533
|
body["status"] = status
|
|
2445
2534
|
if endpoint_id is not None:
|
|
2446
2535
|
body["endpoint_id"] = endpoint_id
|
|
2536
|
+
if direction is not None:
|
|
2537
|
+
body["direction"] = direction
|
|
2538
|
+
if inbound_endpoint_id is not None:
|
|
2539
|
+
body["inbound_endpoint_id"] = inbound_endpoint_id
|
|
2447
2540
|
return _parse_export_record((await self._request("POST", "/v1/exports", json=body))["data"])
|
|
2448
2541
|
|
|
2449
2542
|
async def list_exports(self) -> list[ExportRecord]:
|
|
@@ -10,7 +10,10 @@ MessageStatus = Literal["queued", "delivering", "succeeded", "pending_retry", "f
|
|
|
10
10
|
ReplayableMessageStatus = Literal["failed_permanent", "pending_retry"]
|
|
11
11
|
MetricsWindow = Literal["1h", "24h", "7d", "30d"]
|
|
12
12
|
APIKeyMode = Literal["live", "test"]
|
|
13
|
-
InboundEndpointMode = Literal["forward", "cli"]
|
|
13
|
+
InboundEndpointMode = Literal["forward", "cli", "pull"]
|
|
14
|
+
HMACSignatureEncoding = Literal["hex", "base64"]
|
|
15
|
+
ExportDirection = Literal["outbound", "inbound"]
|
|
16
|
+
ReplayErrorCode = Literal["MESSAGE_DELETED", "RETENTION_EXPIRED", "PAYLOAD_UNAVAILABLE"]
|
|
14
17
|
PullEndpointMode = Literal["pull"]
|
|
15
18
|
PullEventStatus = Literal["stored", "fetched", "delivered"]
|
|
16
19
|
PullEventListStatus = Literal["stored", "fetched", "delivered", "all"]
|
|
@@ -50,6 +53,12 @@ class Message:
|
|
|
50
53
|
deleted_at: Optional[datetime] = None
|
|
51
54
|
deleted_by_user_id: Optional[str] = None
|
|
52
55
|
deleted_by_api_key_id: Optional[str] = None
|
|
56
|
+
first_attempt_at: Optional[datetime] = None
|
|
57
|
+
delivered_at: Optional[datetime] = None
|
|
58
|
+
processing_ms: Optional[int] = None
|
|
59
|
+
total_delivery_ms: Optional[int] = None
|
|
60
|
+
queue_wait_ms: Optional[int] = None
|
|
61
|
+
retention_hours: Optional[int] = None
|
|
53
62
|
|
|
54
63
|
|
|
55
64
|
@dataclass(frozen=True)
|
|
@@ -65,6 +74,10 @@ class MessageSummary:
|
|
|
65
74
|
response_latency_ms: Optional[int] = None
|
|
66
75
|
last_error: Optional[str] = None
|
|
67
76
|
deleted_at: Optional[datetime] = None
|
|
77
|
+
first_attempt_at: Optional[datetime] = None
|
|
78
|
+
processing_ms: Optional[int] = None
|
|
79
|
+
total_delivery_ms: Optional[int] = None
|
|
80
|
+
queue_wait_ms: Optional[int] = None
|
|
68
81
|
|
|
69
82
|
|
|
70
83
|
@dataclass(frozen=True)
|
|
@@ -90,6 +103,8 @@ class Metrics:
|
|
|
90
103
|
retries: int
|
|
91
104
|
success_rate: float
|
|
92
105
|
avg_latency_ms: int
|
|
106
|
+
avg_delivery_time_ms: Optional[int] = None
|
|
107
|
+
avg_queue_wait_ms: Optional[int] = None
|
|
93
108
|
|
|
94
109
|
|
|
95
110
|
@dataclass(frozen=True)
|
|
@@ -140,6 +155,8 @@ class Endpoint:
|
|
|
140
155
|
rate_limit_rps: Optional[int] = None
|
|
141
156
|
burst: Optional[int] = None
|
|
142
157
|
headers: Optional[dict[str, str]] = None
|
|
158
|
+
ephemeral: Optional[bool] = None
|
|
159
|
+
expires_at: Optional[datetime] = None
|
|
143
160
|
|
|
144
161
|
|
|
145
162
|
@dataclass(frozen=True)
|
|
@@ -149,6 +166,8 @@ class EndpointSummary:
|
|
|
149
166
|
created_at: datetime
|
|
150
167
|
paused: bool
|
|
151
168
|
description: Optional[str] = None
|
|
169
|
+
ephemeral: Optional[bool] = None
|
|
170
|
+
expires_at: Optional[datetime] = None
|
|
152
171
|
|
|
153
172
|
|
|
154
173
|
@dataclass(frozen=True)
|
|
@@ -160,6 +179,8 @@ class CreateEndpointResponse:
|
|
|
160
179
|
description: Optional[str] = None
|
|
161
180
|
signing_key_id: Optional[str] = None
|
|
162
181
|
key_hint: Optional[str] = None
|
|
182
|
+
ephemeral: Optional[bool] = None
|
|
183
|
+
expires_at: Optional[datetime] = None
|
|
163
184
|
|
|
164
185
|
|
|
165
186
|
@dataclass(frozen=True)
|
|
@@ -201,6 +222,7 @@ class SubscriptionLimits:
|
|
|
201
222
|
max_endpoints: int
|
|
202
223
|
retention_days: int
|
|
203
224
|
max_retries: Optional[int] = None
|
|
225
|
+
overage_rate_per_message: Optional[float] = None
|
|
204
226
|
|
|
205
227
|
|
|
206
228
|
@dataclass(frozen=True)
|
|
@@ -208,6 +230,9 @@ class SubscriptionUsage:
|
|
|
208
230
|
messages_used: int
|
|
209
231
|
period_start: datetime
|
|
210
232
|
period_end: datetime
|
|
233
|
+
overage_count: Optional[int] = None
|
|
234
|
+
overage_rate: Optional[float] = None
|
|
235
|
+
estimated_overage_charge: Optional[float] = None
|
|
211
236
|
|
|
212
237
|
|
|
213
238
|
@dataclass(frozen=True)
|
|
@@ -234,6 +259,7 @@ class ReplayBatchResult:
|
|
|
234
259
|
message_id: str
|
|
235
260
|
status: Literal["replayed", "failed", "stuck"]
|
|
236
261
|
error: Optional[str] = None
|
|
262
|
+
error_code: Optional[ReplayErrorCode] = None
|
|
237
263
|
|
|
238
264
|
|
|
239
265
|
@dataclass(frozen=True)
|
|
@@ -307,6 +333,10 @@ class InboundEndpoint:
|
|
|
307
333
|
created_at: datetime
|
|
308
334
|
updated_at: datetime
|
|
309
335
|
description: Optional[str] = None
|
|
336
|
+
hmac_signature_encoding: Optional[HMACSignatureEncoding] = None
|
|
337
|
+
ingest_url: Optional[str] = None
|
|
338
|
+
ephemeral: Optional[bool] = None
|
|
339
|
+
expires_at: Optional[datetime] = None
|
|
310
340
|
|
|
311
341
|
|
|
312
342
|
@dataclass(frozen=True)
|
|
@@ -318,6 +348,8 @@ class InboundEndpointSummary:
|
|
|
318
348
|
active: bool
|
|
319
349
|
paused: bool
|
|
320
350
|
created_at: datetime
|
|
351
|
+
ephemeral: Optional[bool] = None
|
|
352
|
+
expires_at: Optional[datetime] = None
|
|
321
353
|
|
|
322
354
|
|
|
323
355
|
@dataclass(frozen=True)
|
|
@@ -332,6 +364,9 @@ class CreateInboundEndpointResponse:
|
|
|
332
364
|
signing_key_id: Optional[str] = None
|
|
333
365
|
signing_secret: Optional[str] = None
|
|
334
366
|
key_hint: Optional[str] = None
|
|
367
|
+
hmac_signature_encoding: Optional[HMACSignatureEncoding] = None
|
|
368
|
+
ephemeral: Optional[bool] = None
|
|
369
|
+
expires_at: Optional[datetime] = None
|
|
335
370
|
|
|
336
371
|
|
|
337
372
|
@dataclass(frozen=True)
|
|
@@ -388,6 +423,7 @@ class PullEndpoint:
|
|
|
388
423
|
token_query_param: Optional[str] = None
|
|
389
424
|
verify_hmac: Optional[bool] = None
|
|
390
425
|
hmac_header_name: Optional[str] = None
|
|
426
|
+
hmac_signature_encoding: Optional[HMACSignatureEncoding] = None
|
|
391
427
|
timestamp_header_name: Optional[str] = None
|
|
392
428
|
timestamp_ttl_seconds: Optional[int] = None
|
|
393
429
|
verify_ip_allowlist: Optional[bool] = None
|
|
@@ -601,6 +637,7 @@ class InboundMessage:
|
|
|
601
637
|
deleted_at: Optional[datetime] = None
|
|
602
638
|
deleted_by_user_id: Optional[str] = None
|
|
603
639
|
deleted_by_api_key_id: Optional[str] = None
|
|
640
|
+
retention_hours: Optional[int] = None
|
|
604
641
|
|
|
605
642
|
|
|
606
643
|
@dataclass(frozen=True)
|
|
@@ -613,6 +650,8 @@ class ExportRecord:
|
|
|
613
650
|
created_at: datetime
|
|
614
651
|
filter_status: Optional[str] = None
|
|
615
652
|
filter_endpoint_id: Optional[str] = None
|
|
653
|
+
filter_inbound_endpoint_id: Optional[str] = None
|
|
654
|
+
direction: Optional[ExportDirection] = None
|
|
616
655
|
row_count: Optional[int] = None
|
|
617
656
|
file_size_bytes: Optional[int] = None
|
|
618
657
|
error_message: Optional[str] = None
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hookbridge"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.1.0"
|
|
8
8
|
description = "Official HookBridge SDK for Python"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -30,6 +30,7 @@ classifiers = [
|
|
|
30
30
|
"Programming Language :: Python :: 3.11",
|
|
31
31
|
"Programming Language :: Python :: 3.12",
|
|
32
32
|
"Programming Language :: Python :: 3.13",
|
|
33
|
+
"Programming Language :: Python :: 3.14",
|
|
33
34
|
"Topic :: Internet :: WWW/HTTP",
|
|
34
35
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
35
36
|
"Typing :: Typed",
|