midbound-cloud 0.0.0.dev0__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,28 @@
1
+ # Fetched from live API
2
+ openapi.json
3
+
4
+ # Generated code
5
+ typescript/src/generated/
6
+ python/src/midbound_cloud/generated/
7
+ go/generated/
8
+ java/src/main/java/
9
+ php/src/
10
+ rust/src/
11
+
12
+ # Build artifacts
13
+ typescript/dist/
14
+ python/dist/
15
+ java/target/
16
+ rust/target/
17
+
18
+ # Dependencies
19
+ node_modules/
20
+ __pycache__/
21
+ *.egg-info/
22
+ .venv/
23
+ venv/
24
+
25
+ # IDE
26
+ .idea/
27
+ *.iml
28
+ .vscode/
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.4
2
+ Name: midbound-cloud
3
+ Version: 0.0.0.dev0
4
+ Summary: Official Midbound Cloud SDK for Python
5
+ Project-URL: Homepage, https://midbound.cloud
6
+ Project-URL: Documentation, https://midbound.cloud/docs
7
+ Project-URL: Repository, https://github.com/midbound/midbound-monorepo
8
+ Project-URL: Issues, https://github.com/midbound/midbound-monorepo/issues
9
+ Author-email: Midbound <support@midbound.com>
10
+ License-Expression: MIT
11
+ Keywords: api,b2b,enrichment,midbound,sdk,visitor-identification
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.9
22
+ Requires-Dist: httpx>=0.25.0
23
+ Requires-Dist: pydantic>=2.0.0
24
+ Description-Content-Type: text/markdown
25
+
26
+ # midbound-cloud
27
+
28
+ Official Midbound Cloud SDK for Python.
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ pip install midbound-cloud
34
+ ```
35
+
36
+ ## Usage
37
+
38
+ ```python
39
+ from midbound_cloud import Midbound
40
+
41
+ client = Midbound(api_key="your-api-key")
42
+
43
+ # Enrich a person by LinkedIn URL
44
+ result = client.enrich.person(linkedin_url="https://linkedin.com/in/example")
45
+ print(result)
46
+ ```
47
+
48
+ ## Webhook Verification
49
+
50
+ ```python
51
+ from midbound_cloud import webhooks
52
+
53
+ # Verify a webhook signature
54
+ is_valid = webhooks.verify_signature(
55
+ payload=request.body,
56
+ signature=request.headers["x-midbound-signature"],
57
+ secret=os.environ["WEBHOOK_SECRET"]
58
+ )
59
+
60
+ # Or construct a typed event
61
+ event = webhooks.construct_event(
62
+ payload=request.body,
63
+ signature=request.headers["x-midbound-signature"],
64
+ secret=os.environ["WEBHOOK_SECRET"]
65
+ )
66
+ print(event["type"]) # e.g., "identity.resolved"
67
+ ```
68
+
69
+ ## Documentation
70
+
71
+ Full documentation available at [midbound.cloud/docs](https://midbound.cloud/docs).
@@ -0,0 +1,46 @@
1
+ # midbound-cloud
2
+
3
+ Official Midbound Cloud SDK for Python.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install midbound-cloud
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```python
14
+ from midbound_cloud import Midbound
15
+
16
+ client = Midbound(api_key="your-api-key")
17
+
18
+ # Enrich a person by LinkedIn URL
19
+ result = client.enrich.person(linkedin_url="https://linkedin.com/in/example")
20
+ print(result)
21
+ ```
22
+
23
+ ## Webhook Verification
24
+
25
+ ```python
26
+ from midbound_cloud import webhooks
27
+
28
+ # Verify a webhook signature
29
+ is_valid = webhooks.verify_signature(
30
+ payload=request.body,
31
+ signature=request.headers["x-midbound-signature"],
32
+ secret=os.environ["WEBHOOK_SECRET"]
33
+ )
34
+
35
+ # Or construct a typed event
36
+ event = webhooks.construct_event(
37
+ payload=request.body,
38
+ signature=request.headers["x-midbound-signature"],
39
+ secret=os.environ["WEBHOOK_SECRET"]
40
+ )
41
+ print(event["type"]) # e.g., "identity.resolved"
42
+ ```
43
+
44
+ ## Documentation
45
+
46
+ Full documentation available at [midbound.cloud/docs](https://midbound.cloud/docs).
@@ -0,0 +1,51 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "midbound-cloud"
7
+ version = "0.0.0.dev0"
8
+ description = "Official Midbound Cloud SDK for Python"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.9"
12
+ authors = [
13
+ { name = "Midbound", email = "support@midbound.com" }
14
+ ]
15
+ keywords = [
16
+ "midbound",
17
+ "api",
18
+ "sdk",
19
+ "enrichment",
20
+ "b2b",
21
+ "visitor-identification"
22
+ ]
23
+ classifiers = [
24
+ "Development Status :: 4 - Beta",
25
+ "Intended Audience :: Developers",
26
+ "License :: OSI Approved :: MIT License",
27
+ "Programming Language :: Python :: 3",
28
+ "Programming Language :: Python :: 3.9",
29
+ "Programming Language :: Python :: 3.10",
30
+ "Programming Language :: Python :: 3.11",
31
+ "Programming Language :: Python :: 3.12",
32
+ "Typing :: Typed"
33
+ ]
34
+ dependencies = [
35
+ "httpx>=0.25.0",
36
+ "pydantic>=2.0.0"
37
+ ]
38
+
39
+ [project.urls]
40
+ Homepage = "https://midbound.cloud"
41
+ Documentation = "https://midbound.cloud/docs"
42
+ Repository = "https://github.com/midbound/midbound-monorepo"
43
+ Issues = "https://github.com/midbound/midbound-monorepo/issues"
44
+
45
+ [tool.hatch.build.targets.wheel]
46
+ packages = ["src/midbound_cloud"]
47
+
48
+ [tool.hatch.build.targets.sdist]
49
+ include = [
50
+ "/src"
51
+ ]
@@ -0,0 +1,11 @@
1
+ """
2
+ Midbound Cloud SDK for Python.
3
+
4
+ Official SDK for interacting with the Midbound Cloud API.
5
+ """
6
+
7
+ from .client import Midbound
8
+ from . import webhooks
9
+
10
+ __all__ = ["Midbound", "webhooks"]
11
+ __version__ = "0.0.0.dev0"
@@ -0,0 +1,91 @@
1
+ """
2
+ Midbound Cloud SDK client.
3
+ """
4
+
5
+ from typing import Optional
6
+ import httpx
7
+
8
+
9
+ class EnrichAPI:
10
+ """Enrichment API for person and company data."""
11
+
12
+ def __init__(self, client: httpx.Client):
13
+ self._client = client
14
+
15
+ def person(
16
+ self,
17
+ linkedin_url: Optional[str] = None,
18
+ email: Optional[str] = None,
19
+ webhook_url: Optional[str] = None,
20
+ webhook_secret: Optional[str] = None,
21
+ ) -> dict:
22
+ """
23
+ Enrich a person by LinkedIn URL or email.
24
+
25
+ Args:
26
+ linkedin_url: LinkedIn profile URL
27
+ email: Email address
28
+ webhook_url: Optional webhook URL for async results
29
+ webhook_secret: Optional webhook signing secret
30
+
31
+ Returns:
32
+ Enrichment result dictionary
33
+ """
34
+ payload = {}
35
+ if linkedin_url:
36
+ payload["linkedinUrl"] = linkedin_url
37
+ if email:
38
+ payload["email"] = email
39
+ if webhook_url:
40
+ payload["webhookUrl"] = webhook_url
41
+ if webhook_secret:
42
+ payload["webhookSecret"] = webhook_secret
43
+
44
+ response = self._client.post("/v1/enrich/person", json=payload)
45
+ response.raise_for_status()
46
+ return response.json()
47
+
48
+
49
+ class Midbound:
50
+ """
51
+ Midbound Cloud SDK client.
52
+
53
+ Example:
54
+ >>> from midbound_cloud import Midbound
55
+ >>> client = Midbound(api_key="your-api-key")
56
+ >>> result = client.enrich.person(linkedin_url="https://linkedin.com/in/example")
57
+ """
58
+
59
+ def __init__(
60
+ self,
61
+ api_key: str,
62
+ base_url: str = "https://api.midbound.cloud",
63
+ timeout: float = 30.0,
64
+ ):
65
+ """
66
+ Initialize the Midbound client.
67
+
68
+ Args:
69
+ api_key: Your Midbound API key
70
+ base_url: Base URL for the API (default: production)
71
+ timeout: Request timeout in seconds
72
+ """
73
+ self._client = httpx.Client(
74
+ base_url=base_url,
75
+ headers={
76
+ "x-api-key": api_key,
77
+ "Content-Type": "application/json",
78
+ },
79
+ timeout=timeout,
80
+ )
81
+ self.enrich = EnrichAPI(self._client)
82
+
83
+ def close(self) -> None:
84
+ """Close the HTTP client."""
85
+ self._client.close()
86
+
87
+ def __enter__(self) -> "Midbound":
88
+ return self
89
+
90
+ def __exit__(self, *args) -> None:
91
+ self.close()
@@ -0,0 +1,125 @@
1
+ """
2
+ Webhook signature verification utilities.
3
+
4
+ Verify webhook payloads from Midbound to ensure they are authentic
5
+ and haven't been tampered with.
6
+ """
7
+
8
+ import hmac
9
+ import hashlib
10
+ import json
11
+ import re
12
+ import time
13
+ from typing import Any, Dict, TypeVar, Type, Union
14
+
15
+ T = TypeVar("T")
16
+
17
+
18
+ def verify_signature(
19
+ payload: str,
20
+ signature: str,
21
+ secret: str,
22
+ max_age_seconds: int = 300,
23
+ ) -> bool:
24
+ """
25
+ Verify a webhook signature.
26
+
27
+ Validates that the webhook payload was signed by Midbound using your
28
+ webhook signing secret. Also checks that the signature hasn't expired.
29
+
30
+ Args:
31
+ payload: The raw JSON payload string from the request body
32
+ signature: The signature header value (x-midbound-signature)
33
+ secret: Your webhook signing secret
34
+ max_age_seconds: Maximum age of signature in seconds (default: 300 = 5 minutes)
35
+
36
+ Returns:
37
+ True if signature is valid and not expired
38
+
39
+ Example:
40
+ >>> from midbound_cloud import webhooks
41
+ >>>
42
+ >>> is_valid = webhooks.verify_signature(
43
+ ... payload=request.body,
44
+ ... signature=request.headers["x-midbound-signature"],
45
+ ... secret=os.environ["WEBHOOK_SECRET"]
46
+ ... )
47
+ """
48
+ match = re.match(r"t=(\d+),v1=([a-f0-9]+)", signature)
49
+ if not match:
50
+ return False
51
+
52
+ timestamp_str, received_sig = match.groups()
53
+ timestamp = int(timestamp_str)
54
+
55
+ # Check expiry
56
+ if time.time() - timestamp > max_age_seconds:
57
+ return False
58
+
59
+ # Verify signature
60
+ signed_payload = f"{timestamp}.{payload}"
61
+ expected_sig = hmac.new(
62
+ secret.encode(),
63
+ signed_payload.encode(),
64
+ hashlib.sha256,
65
+ ).hexdigest()
66
+
67
+ # Use timing-safe comparison to prevent timing attacks
68
+ return hmac.compare_digest(expected_sig, received_sig)
69
+
70
+
71
+ def construct_event(
72
+ payload: str,
73
+ signature: str,
74
+ secret: str,
75
+ event_class: Type[T] = dict, # type: ignore
76
+ ) -> Union[T, Dict[str, Any]]:
77
+ """
78
+ Verify signature and construct a webhook event.
79
+
80
+ Combines signature verification with JSON parsing. Raises ValueError
81
+ if the signature is invalid.
82
+
83
+ Args:
84
+ payload: The raw JSON payload string from the request body
85
+ signature: The signature header value (x-midbound-signature)
86
+ secret: Your webhook signing secret
87
+ event_class: Optional class to instantiate with event data
88
+
89
+ Returns:
90
+ The parsed webhook event (dict or event_class instance)
91
+
92
+ Raises:
93
+ ValueError: If signature is invalid
94
+
95
+ Example:
96
+ >>> from midbound_cloud import webhooks
97
+ >>>
98
+ >>> try:
99
+ ... event = webhooks.construct_event(
100
+ ... payload=request.body,
101
+ ... signature=request.headers["x-midbound-signature"],
102
+ ... secret=os.environ["WEBHOOK_SECRET"]
103
+ ... )
104
+ ... print(event["type"]) # e.g., "identity.resolved"
105
+ ... except ValueError:
106
+ ... print("Invalid webhook signature")
107
+ """
108
+ if not verify_signature(payload, signature, secret):
109
+ raise ValueError("Invalid webhook signature")
110
+
111
+ data = json.loads(payload)
112
+
113
+ if event_class is dict:
114
+ return data
115
+ return event_class(**data)
116
+
117
+
118
+ # Event type constants
119
+ EVENT_TYPES = (
120
+ "identity.resolved",
121
+ "identity.qualified",
122
+ "identity.enriched",
123
+ "identity.validated",
124
+ "identity.session.finalized",
125
+ )