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.
@@ -206,3 +206,6 @@ marimo/_static/
206
206
  marimo/_lsp/
207
207
  __marimo__/
208
208
  PUBLISHING.md
209
+
210
+ # Private scratch list — lives in the parent folder, never in a repo
211
+ FINDINGS.md
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: hookbridge
3
- Version: 2.0.0
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.0.0"
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.0.0"
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",