beckn-sdk 1.0.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,9 @@
1
+ Metadata-Version: 2.4
2
+ Name: beckn-sdk
3
+ Version: 1.0.0
4
+ Summary: BeckN Protocol SDK for Python
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: httpx>=0.27.0
7
+ Provides-Extra: dev
8
+ Requires-Dist: pytest>=8.0; extra == "dev"
9
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
@@ -0,0 +1,347 @@
1
+ # BeckN Protocol SDK — Python
2
+
3
+ Python SDK for the BeckN Protocol, implementing the Beckn protocol specification for domain-agnostic digital commerce.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install beckn-sdk
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ### Initialize Client
14
+
15
+ ```python
16
+ from beckn import BeckNClient, BeckNClientConfig
17
+
18
+ client = BeckNClient(BeckNClientConfig(
19
+ base_url="https://api.beckn.network/v1",
20
+ api_key="bk_your_api_key_here",
21
+ timeout=30000,
22
+ ))
23
+ ```
24
+
25
+ ### Register a BAP
26
+
27
+ ```python
28
+ bap = client.create_bap(BapCreate(
29
+ id="my-bap-001",
30
+ name="My Store App",
31
+ endpoint="https://store.example.com",
32
+ country="US",
33
+ lat=40.7128,
34
+ lon=-74.0060,
35
+ ))
36
+ ```
37
+
38
+ ### Register a BPP
39
+
40
+ ```python
41
+ bpp = client.create_bpp(BppCreate(
42
+ id="my-bpp-001",
43
+ name="My Provider App",
44
+ endpoint="https://provider.example.com",
45
+ country="US",
46
+ currency="USD",
47
+ lat=40.7128,
48
+ lon=-74.0060,
49
+ ))
50
+ ```
51
+
52
+ ### Create an Order
53
+
54
+ ```python
55
+ order = client.create_order(OrderCreate(
56
+ id="order-001",
57
+ transaction_id="txn-001",
58
+ bap_id="my-bap-001",
59
+ bpp_id="my-bpp-001",
60
+ ))
61
+ ```
62
+
63
+ ### Discover Nearby BPPs (GeoDNS)
64
+
65
+ ```python
66
+ # Find BPPs in a specific country
67
+ bpps = client.discover_nearest_bap(country="US")
68
+
69
+ # Find BPPs near a geographic location
70
+ nearby = client.discover_marketplace(lat=40.7128, lng=-74.0060, radius_km=50, limit=10)
71
+ ```
72
+
73
+ ### GBP (Google Business Profile)
74
+
75
+ ```python
76
+ # Register a GBP account
77
+ account = client.create_gbp_account(GbpAccountCreate(
78
+ email="merchant@example.com",
79
+ account_name="accounts/1234567890",
80
+ ))
81
+
82
+ # Sync GBP locations as BPPs (auto-registers A2A agent cards)
83
+ synced = client.sync_gbp_locations(account.id)
84
+ print(f"{len(synced['bpps'])} BPPs synced")
85
+ ```
86
+
87
+ ### A2A Agent Discovery
88
+
89
+ ```python
90
+ # Discover agents near a location (cross-protocol with GeoDNS)
91
+ nearby = client.discover_marketplace(lat=40.7128, lng=-74.0060, radius_km=50, limit=10)
92
+
93
+ # Register an agent card
94
+ agent = client.register_agent_card(AgentCardRegister(
95
+ agent_id="agent-001",
96
+ name="Travel Booking Agent",
97
+ url="https://travel.example.com",
98
+ capabilities={"streaming": True},
99
+ skills=[{"id": "search", "name": "Travel Search"}],
100
+ ))
101
+
102
+ # Discover agents by skill
103
+ results = client.discover_agents(skill="booking")
104
+ ```
105
+
106
+ ### ANP (Agent Network Protocol)
107
+
108
+ ```python
109
+ # Announce a DID to the network
110
+ announcement = client.announce(AnpAnnouncementCreate(
111
+ announcement_id="ann-001",
112
+ did="did:example:123",
113
+ service_endpoint="https://agent.example.com",
114
+ ))
115
+
116
+ # Register a witness node
117
+ witness = client.register_witness(AnpWitnessRegister(
118
+ witness_id="witness-001",
119
+ did="did:example:witness-1",
120
+ endpoint="https://witness.example.com",
121
+ protocols=["gossip", "http"],
122
+ ))
123
+
124
+ # Verify a DID document
125
+ verification = client.verify(AnpVerificationVerify(
126
+ did="did:example:123",
127
+ did_document={"id": "did:example:123"},
128
+ method="key",
129
+ ))
130
+ ```
131
+
132
+ ### ACP (Agent Communication Protocol)
133
+
134
+ ```python
135
+ # Register a credential issuer
136
+ issuer = client.register_issuer(AcpIssuerRegister(
137
+ issuer_id="issuer-001",
138
+ name="Test Issuer",
139
+ authorization_endpoint="https://issuer.example.com/authorize",
140
+ token_endpoint="https://issuer.example.com/token",
141
+ jwks_uri="https://issuer.example.com/.well-known/jwks.json",
142
+ ))
143
+
144
+ # Issue a token
145
+ token = client.issue_token(AcpTokenIssue(
146
+ token_value="eyJhbGciOiJSUzI1NiIs...",
147
+ subject="did:example:subject-1",
148
+ ))
149
+
150
+ # Submit a verifiable presentation
151
+ presentation = client.submit_presentation(AcpPresentationSubmit(
152
+ presentation_id="vp-123",
153
+ holder_did="did:example:holder-1",
154
+ issuer_id="issuer-001",
155
+ claims={"age": 25},
156
+ ))
157
+ ```
158
+
159
+ ### Google Business Profile Sync
160
+
161
+ ```python
162
+ # Register a GBP account
163
+ account = client.create_gbp_account({
164
+ "email": "merchant@example.com",
165
+ "account_name": "accounts/1234567890",
166
+ })
167
+
168
+ # Sync GBP locations as BPPs (auto-registers A2A agent cards)
169
+ result = client.sync_gbp_locations(account["id"])
170
+ print(f"{len(result['bpps'])} BPPs synced")
171
+ ```
172
+
173
+ ### A2A Agent Discovery
174
+
175
+ ```python
176
+ # Discover nearby BPPs via GeoDNS (maps to A2A agents)
177
+ bpps = client.discover_nearest_bpp(country="US")
178
+
179
+ # Register an agent card
180
+ agent_card = client.register_agent_card({
181
+ "agent_id": "agent-001",
182
+ "name": "Travel Booking Agent",
183
+ "url": "https://travel.example.com",
184
+ "capabilities": {"streaming": True},
185
+ "skills": [{"id": "search", "name": "Travel Search"}],
186
+ })
187
+
188
+ # Send a message to a task
189
+ message = client.send_message({
190
+ "message_id": "msg-001",
191
+ "context_id": "ctx-1",
192
+ "role": "user",
193
+ "parts": [{"type": "text", "text": "Find flights to NYC"}],
194
+ })
195
+ ```
196
+
197
+ ### ANP (Agent Network Protocol)
198
+
199
+ ```python
200
+ # Announce a DID to the network
201
+ announcement = client.announce({
202
+ "announcement_id": "ann-001",
203
+ "did": "did:example:123",
204
+ "service_endpoint": "https://agent.example.com",
205
+ })
206
+
207
+ # Register a witness node
208
+ witness = client.register_witness({
209
+ "witness_id": "witness-001",
210
+ "did": "did:example:witness-1",
211
+ "endpoint": "https://witness.example.com",
212
+ "protocols": ["gossip", "http"],
213
+ })
214
+ ```
215
+
216
+ ### ACP (Agent Communication Protocol)
217
+
218
+ ```python
219
+ # Register a credential issuer
220
+ issuer = client.register_issuer({
221
+ "issuer_id": "issuer-001",
222
+ "name": "Test Issuer",
223
+ "authorization_endpoint": "https://issuer.example.com/authorize",
224
+ "token_endpoint": "https://issuer.example.com/token",
225
+ "jwks_uri": "https://issuer.example.com/.well-known/jwks.json",
226
+ })
227
+
228
+ # Issue a token
229
+ token = client.issue_token({
230
+ "token_value": "eyJhbGciOiJSUzI1NiIs...",
231
+ "subject": "did:example:subject-1",
232
+ })
233
+
234
+ # Submit a verifiable presentation
235
+ presentation = client.submit_presentation({
236
+ "id": "pres-001",
237
+ "presentation_id": "vp-123",
238
+ "vp": {"@context": ["..."], "type": "VerifiablePresentation"},
239
+ "credential_issuer_id": "issuer-001",
240
+ "holder": "did:example:holder-1",
241
+ })
242
+ ```
243
+
244
+ ## API Reference
245
+
246
+ ### BeckNClientConfig
247
+
248
+ | Field | Type | Default | Description |
249
+ |-------|------|---------|-------------|
250
+ | `base_url` | `str` | `'http://localhost:4000/v1'` | API base URL |
251
+ | `api_key` | `Optional[str]` | `None` | API key for authentication |
252
+ | `timeout` | `int` | `30000` | Request timeout in milliseconds |
253
+
254
+ ### Methods
255
+
256
+ #### Orders
257
+ - `list_orders() -> list[Order]`
258
+ - `create_order(data: OrderCreate) -> Order`
259
+ - `get_order(id: str) -> Order`
260
+ - `delete_order(id: str) -> None`
261
+
262
+ #### BAPs
263
+ - `list_baps() -> list[Bap]`
264
+ - `list_baps_by_company(company_id: str) -> list[Bap]`
265
+ - `list_baps_by_country(country: str) -> list[Bap]`
266
+ - `list_baps_nearby(lat: float, lng: float, radius_km: int = 50) -> list[Bap]`
267
+ - `create_bap(data: BapCreate) -> Bap`
268
+ - `get_bap(bap_id: str) -> Bap`
269
+ - `update_bap(bap_id: str, data: BapUpdate) -> Bap`
270
+
271
+ #### BPPs
272
+ - `list_bpps() -> list[Bpp]`
273
+ - `list_bpps_by_company(company_id: str) -> list[Bpp]`
274
+ - `list_bpps_by_country(country: str) -> list[Bpp]`
275
+ - `list_bpps_nearby(lat: float, lng: float, radius_km: int = 50) -> list[Bpp]`
276
+ - `create_bpp(data: BppCreate) -> Bpp`
277
+ - `get_bpp(bpp_id: str) -> Bpp`
278
+ - `update_bpp(bpp_id: str, data: BppUpdate) -> Bpp`
279
+
280
+ #### Companies (B2B Multi-Tenant)
281
+ - `list_companies() -> list[Company]`
282
+ - `create_company(data: CompanyCreate) -> Company`
283
+ - `get_company(company_id: str) -> Company`
284
+ - `list_companies_by_domain(domain: str) -> list[Company]`
285
+
286
+ #### API Keys
287
+ - `list_api_keys() -> list[ApiKey]`
288
+ - `create_api_key(data: ApiKeyCreate) -> ApiKeyResponse`
289
+ - `verify_api_key(secret: str) -> ApiKeyVerifyResponse`
290
+ - `revoke_api_key(key_id: str) -> ApiKey`
291
+ - `rotate_api_key(key_id: str) -> ApiKeyResponse`
292
+
293
+ #### GeoDNS Discovery
294
+ - `discover_nearest_bap(country: str = None, city: str = None) -> GeoDnsResult`
295
+ - `discover_nearest_bpp(country: str = None, city: str = None) -> GeoDnsResult`
296
+ - `discover_marketplace(lat: float, lng: float, radius_km: int = 50, limit: int = 10) -> list[GeoDnsResult]`
297
+
298
+ #### GBP (Google Business Profile)
299
+ - `list_gbp_accounts() -> list[GbpAccount]`
300
+ - `create_gbp_account(data: dict) -> GbpAccount`
301
+ - `sync_gbp_locations(account_id: str) -> dict`
302
+
303
+ #### A2A (Agent-to-Agent)
304
+ - `list_agent_cards() -> list[AgentCard]`
305
+ - `register_agent_card(data: AgentCardRegister) -> AgentCard`
306
+ - `list_tasks() -> list[A2ATask]`
307
+ - `create_task(data: A2ATaskCreate) -> A2ATask`
308
+ - `send_message(data: A2ATaskMessageSend) -> A2ATaskMessage`
309
+ - `list_messages() -> list[A2ATaskMessage]`
310
+ - `list_artifacts() -> list[A2AArtifact]`
311
+ - `create_artifact(data: A2AArtifactCreate) -> A2AArtifact`
312
+
313
+ #### MCP (Model Context Protocol)
314
+ - `list_tools() -> list[McpTool]`
315
+ - `create_tool(data: McpToolCreate) -> McpTool`
316
+ - `list_resources() -> list[McpResource]`
317
+ - `create_resource(data: McpResourceCreate) -> McpResource`
318
+ - `list_prompts() -> list[McpPrompt]`
319
+ - `create_prompt(data: McpPromptCreate) -> McpPrompt`
320
+
321
+ #### ACP (Agent Communication Protocol)
322
+ - `list_issuers() -> list[AcpIssuer]`
323
+ - `register_issuer(data: AcpIssuerRegister) -> AcpIssuer`
324
+ - `list_tokens() -> list[AcpToken]`
325
+ - `issue_token(data: AcpTokenIssue) -> AcpToken`
326
+ - `introspect_token(token: str) -> AcpToken`
327
+ - `list_presentations() -> list[AcpPresentation]`
328
+ - `submit_presentation(data: AcpPresentationSubmit) -> AcpPresentation`
329
+ - `list_policies() -> list[AcpAccessPolicy]`
330
+ - `create_policy(data: AcpAccessPolicyCreate) -> AcpAccessPolicy`
331
+
332
+ #### ANP (Agent Network Protocol)
333
+ - `list_announcements() -> list[AnpAnnouncement]`
334
+ - `announce(data: AnpAnnouncementCreate) -> AnpAnnouncement`
335
+ - `list_witnesses() -> list[AnpWitness]`
336
+ - `register_witness(data: AnpWitnessRegister) -> AnpWitness`
337
+ - `verify(data: AnpVerificationVerify) -> AnpVerification`
338
+
339
+ #### Subscriptions
340
+ - `list_subscriptions() -> list[Subscription]`
341
+ - `create_subscription(data: SubscriptionCreate) -> Subscription`
342
+ - `cancel_subscription(sub_id: str) -> Subscription`
343
+ - `renew_subscription(sub_id: str) -> Subscription`
344
+
345
+ ## License
346
+
347
+ Apache-2.0
@@ -0,0 +1,40 @@
1
+ """BeckN Protocol SDK."""
2
+
3
+ __version__ = "1.0.0"
4
+
5
+ from .client import BeckNClient, BeckNError
6
+ from .types import (
7
+ Bap, BapCreate, BapUpdate, Bpp, BppCreate, BppUpdate,
8
+ Order, OrderCreate, OrderUpdate, OrderState, Item, ItemCreate,
9
+ Provider, ProviderCreate, Fulfillment, FulfillmentCreate,
10
+ Subscription, SubscriptionCreate, SubscriptionStatus, SubscriberType,
11
+ ApiKey, ApiKeyCreate, ApiKeyResponse, ApiKeyVerifyRequest, ApiKeyVerifyResponse,
12
+ GeoLocation, GeoDnsResult, GeoDiscoveryConfig,
13
+ AgentCard, AgentCardRegister, A2ATask, A2ATaskCreate, TaskStatus,
14
+ A2ATaskMessage, A2ATaskMessageSend, A2AArtifact, A2AArtifactCreate,
15
+ McpTool, McpToolCreate, McpResource, McpResourceCreate,
16
+ McpPrompt, McpPromptCreate, McpClientType, McpClientRegister, TransportType,
17
+ AcpIssuer, AcpIssuerRegister, AcpToken, AcpTokenIssue,
18
+ AcpPresentation, AcpPresentationSubmit, AcpAccessPolicy, AcpAccessPolicyCreate,
19
+ AnpAnnouncement, AnpAnnouncementCreate, AnpWitness, AnpWitnessRegister,
20
+ AnpVerification, AnpVerificationVerify, MessageRole,
21
+ BeckNClientConfig,
22
+ )
23
+
24
+ __all__ = [
25
+ "BeckNClient", "BeckNError", "BeckNClientConfig",
26
+ "Bap", "BapCreate", "BapUpdate", "Bpp", "BppCreate", "BppUpdate",
27
+ "Order", "OrderCreate", "OrderUpdate", "OrderState", "Item", "ItemCreate",
28
+ "Provider", "ProviderCreate", "Fulfillment", "FulfillmentCreate",
29
+ "Subscription", "SubscriptionCreate", "SubscriptionStatus", "SubscriberType",
30
+ "ApiKey", "ApiKeyCreate", "ApiKeyResponse", "ApiKeyVerifyRequest", "ApiKeyVerifyResponse",
31
+ "GeoLocation", "GeoDnsResult", "GeoDiscoveryConfig",
32
+ "AgentCard", "AgentCardRegister", "A2ATask", "A2ATaskCreate", "TaskStatus",
33
+ "A2ATaskMessage", "A2ATaskMessageSend", "A2AArtifact", "A2AArtifactCreate",
34
+ "McpTool", "McpToolCreate", "McpResource", "McpResourceCreate",
35
+ "McpPrompt", "McpPromptCreate", "McpClientType", "McpClientRegister", "TransportType",
36
+ "AcpIssuer", "AcpIssuerRegister", "AcpToken", "AcpTokenIssue",
37
+ "AcpPresentation", "AcpPresentationSubmit", "AcpAccessPolicy", "AcpAccessPolicyCreate",
38
+ "AnpAnnouncement", "AnpAnnouncementCreate", "AnpWitness", "AnpWitnessRegister",
39
+ "AnpVerification", "AnpVerificationVerify", "MessageRole",
40
+ ]
@@ -0,0 +1,288 @@
1
+ """Client module for the BeckN Protocol SDK."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ from typing import Any, Optional, TypeVar
8
+ from dataclasses import asdict
9
+
10
+ import httpx
11
+
12
+ from .types import (
13
+ Bap, BapCreate, BapUpdate, Bpp, BppCreate, BppUpdate,
14
+ Order, OrderCreate, OrderUpdate, Item, ItemCreate,
15
+ Provider, ProviderCreate, Fulfillment, FulfillmentCreate,
16
+ Subscription, SubscriptionCreate,
17
+ ApiKey, ApiKeyCreate, ApiKeyResponse, ApiKeyVerifyRequest, ApiKeyVerifyResponse,
18
+ ErrorResponse, GeoDnsResult, GeoLocation,
19
+ AgentCard, AgentCardRegister, A2ATask, A2ATaskCreate,
20
+ A2ATaskMessage, A2ATaskMessageSend, A2AArtifact, A2AArtifactCreate,
21
+ McpTool, McpToolCreate, McpResource, McpResourceCreate,
22
+ McpPrompt, McpPromptCreate, McpClientType, McpClientRegister,
23
+ AcpIssuer, AcpIssuerRegister, AcpToken, AcpTokenIssue,
24
+ AcpPresentation, AcpPresentationSubmit, AcpAccessPolicy, AcpAccessPolicyCreate,
25
+ AnpAnnouncement, AnpAnnouncementCreate, AnpWitness, AnpWitnessRegister,
26
+ AnpVerification, AnpVerificationVerify,
27
+ Company, CompanyCreate, BeckNClientConfig,
28
+ )
29
+
30
+ T = TypeVar('T')
31
+
32
+
33
+ class BeckNError(Exception):
34
+ def __init__(self, message: str, status: int = 500, response: Optional[ErrorResponse] = None):
35
+ super().__init__(message)
36
+ self.status = status
37
+ self.response = response
38
+
39
+
40
+ def _to_dict(obj: Any) -> dict[str, Any]:
41
+ if isinstance(obj, dict):
42
+ return obj
43
+ if hasattr(obj, '__dict__'):
44
+ return {k: v for k, v in obj.__dict__.items() if v is not None}
45
+ return asdict(obj)
46
+
47
+
48
+ def _from_dict(cls: type, data: dict[str, Any]) -> Any:
49
+ if hasattr(cls, '__dataclass_fields__'):
50
+ field_names = set(cls.__dataclass_fields__.keys())
51
+ filtered = {k: v for k, v in data.items() if k in field_names}
52
+ return cls(**filtered)
53
+ return data
54
+
55
+
56
+ class BeckNClient:
57
+ def __init__(self, config: Optional[BeckNClientConfig] = None):
58
+ if config is None:
59
+ config = BeckNClientConfig()
60
+ self.base_url = config.base_url or os.environ.get('BECKN_API_URL', 'http://localhost:4000/v1')
61
+ self.api_key = config.api_key or os.environ.get('BECKN_API_KEY')
62
+ self.timeout = config.timeout or 30
63
+ self._client = httpx.Client(timeout=self.timeout, base_url=self.base_url)
64
+ self._async_client = httpx.AsyncClient(timeout=self.timeout, base_url=self.base_url)
65
+
66
+ def _headers(self) -> dict[str, str]:
67
+ h = {'Content-Type': 'application/json', 'Accept': 'application/json'}
68
+ if self.api_key:
69
+ h['x-api-key'] = self.api_key
70
+ return h
71
+
72
+ def _request(self, method: str, path: str, body: Optional[dict] = None) -> dict[str, Any]:
73
+ kwargs = {'headers': self._headers()}
74
+ if body is not None:
75
+ kwargs['content'] = json.dumps(body)
76
+ resp = self._client.request(method, path, **kwargs)
77
+ if not resp.is_success:
78
+ try:
79
+ err = resp.json()
80
+ except Exception:
81
+ err = {'message': resp.text}
82
+ raise BeckNError(err.get('message', f'HTTP {resp.status_code}'), resp.status_code, _from_dict(ErrorResponse, err))
83
+ if resp.status_code == 204:
84
+ return None
85
+ return resp.json()
86
+
87
+ # === BeckN Orders ===
88
+ def list_orders(self) -> list[Order]:
89
+ return [Order(**o) for o in self._request('GET', '/orders')]
90
+
91
+ def create_order(self, data: OrderCreate) -> Order:
92
+ return Order(**self._request('POST', '/orders', _to_dict(data)))
93
+
94
+ def get_order(self, order_id: str) -> Order:
95
+ return Order(**self._request('GET', f'/orders/{order_id}'))
96
+
97
+ def update_order(self, order_id: str, data: OrderUpdate) -> Order:
98
+ return Order(**self._request('PATCH', f'/orders/{order_id}', _to_dict(data)))
99
+
100
+ def delete_order(self, order_id: str) -> None:
101
+ self._request('DELETE', f'/orders/{order_id}')
102
+
103
+ # === BAPs ===
104
+ def list_baps(self) -> list[Bap]:
105
+ return [Bap(**b) for b in self._request('GET', '/baps')]
106
+
107
+ def list_baps_by_company(self, company_id: str) -> list[Bap]:
108
+ return [Bap(**b) for b in self._request('GET', f'/baps?company_id={company_id}')]
109
+
110
+ def create_bap(self, data: BapCreate) -> Bap:
111
+ return Bap(**self._request('POST', '/baps', _to_dict(data)))
112
+
113
+ def get_bap(self, bap_id: str) -> Bap:
114
+ return Bap(**self._request('GET', f'/baps/{bap_id}'))
115
+
116
+ def update_bap(self, bap_id: str, data: BapUpdate) -> Bap:
117
+ return Bap(**self._request('PATCH', f'/baps/{bap_id}', _to_dict(data)))
118
+
119
+ # === BPPs ===
120
+ def list_bpps(self) -> list[Bpp]:
121
+ return [Bpp(**b) for b in self._request('GET', '/bpps')]
122
+
123
+ def list_bpps_by_company(self, company_id: str) -> list[Bpp]:
124
+ return [Bpp(**b) for b in self._request('GET', f'/bpps?company_id={company_id}')]
125
+
126
+ def create_bpp(self, data: BppCreate) -> Bpp:
127
+ return Bpp(**self._request('POST', '/bpps', _to_dict(data)))
128
+
129
+ def get_bpp(self, bpp_id: str) -> Bpp:
130
+ return Bpp(**self._request('GET', f'/bpps/{bpp_id}'))
131
+
132
+ def update_bpp(self, bpp_id: str, data: BppUpdate) -> Bpp:
133
+ return Bpp(**self._request('PATCH', f'/bpps/{bpp_id}', _to_dict(data)))
134
+
135
+ # === Items ===
136
+ def list_items(self) -> list[Item]:
137
+ return [Item(**i) for i in self._request('GET', '/items')]
138
+
139
+ def create_item(self, data: ItemCreate) -> Item:
140
+ return Item(**self._request('POST', '/items', _to_dict(data)))
141
+
142
+ def get_item(self, item_id: str) -> Item:
143
+ return Item(**self._request('GET', f'/items/{item_id}'))
144
+
145
+ # === Providers ===
146
+ def list_providers(self) -> list[Provider]:
147
+ return [Provider(**p) for p in self._request('GET', '/providers')]
148
+
149
+ def create_provider(self, data: ProviderCreate) -> Provider:
150
+ return Provider(**self._request('POST', '/providers', _to_dict(data)))
151
+
152
+ def get_provider(self, provider_id: str) -> Provider:
153
+ return Provider(**self._request('GET', f'/providers/{provider_id}'))
154
+
155
+ def delete_provider(self, provider_id: str) -> None:
156
+ self._request('DELETE', f'/providers/{provider_id}')
157
+
158
+ # === Fulfillments ===
159
+ def list_fulfillments(self) -> list[Fulfillment]:
160
+ return [Fulfillment(**f) for f in self._request('GET', '/fulfillments')]
161
+
162
+ def create_fulfillment(self, data: FulfillmentCreate) -> Fulfillment:
163
+ return Fulfillment(**self._request('POST', '/fulfillments', _to_dict(data)))
164
+
165
+ def track_fulfillment(self, tracking_id: str) -> list[Fulfillment]:
166
+ return [Fulfillment(**f) for f in self._request('POST', '/fulfillments/track', {'tracking_id': tracking_id})]
167
+
168
+ # === Subscriptions ===
169
+ def list_subscriptions(self) -> list[Subscription]:
170
+ return [Subscription(**s) for s in self._request('GET', '/subscriptions')]
171
+
172
+ def create_subscription(self, data: SubscriptionCreate) -> Subscription:
173
+ return Subscription(**self._request('POST', '/subscriptions', _to_dict(data)))
174
+
175
+ def get_subscription(self, sub_id: str) -> Subscription:
176
+ return Subscription(**self._request('GET', f'/subscriptions/{sub_id}'))
177
+
178
+ def cancel_subscription(self, sub_id: str) -> Subscription:
179
+ return Subscription(**self._request('POST', f'/subscriptions/{sub_id}/cancel', {}))
180
+
181
+ def renew_subscription(self, sub_id: str) -> Subscription:
182
+ return Subscription(**self._request('POST', f'/subscriptions/{sub_id}/renew', {}))
183
+
184
+ # === API Keys ===
185
+ def list_api_keys(self) -> list[ApiKey]:
186
+ return [ApiKey(**k) for k in self._request('GET', '/api-keys')]
187
+
188
+ def create_api_key(self, data: ApiKeyCreate) -> ApiKeyResponse:
189
+ return ApiKeyResponse(**self._request('POST', '/api-keys', _to_dict(data)))
190
+
191
+ def get_api_key(self, key_id: str) -> ApiKey:
192
+ return ApiKey(**self._request('GET', f'/api-keys/{key_id}'))
193
+
194
+ def verify_api_key(self, secret: str) -> ApiKeyVerifyResponse:
195
+ resp = self._request('POST', '/api-keys/verify', {'secret': secret})
196
+ return ApiKeyVerifyResponse(**resp)
197
+
198
+ def revoke_api_key(self, key_id: str) -> ApiKey:
199
+ return ApiKey(**self._request('POST', f'/api-keys/{key_id}/revoke', {}))
200
+
201
+ def rotate_api_key(self, key_id: str) -> ApiKeyResponse:
202
+ return ApiKeyResponse(**self._request('POST', f'/api-keys/{key_id}/rotate', {}))
203
+
204
+ def list_api_keys_by_owner(self, owner_id: str) -> list[ApiKey]:
205
+ return [ApiKey(**k) for k in self._request('GET', f'/api-keys/owner/{owner_id}')]
206
+
207
+ # === GeoDNS Discovery ===
208
+ def discover_nearest_bap(self, country: Optional[str] = None, city: Optional[str] = None) -> GeoDnsResult:
209
+ from urllib.parse import urlencode
210
+ params = []
211
+ if country: params.append(f'country={country}')
212
+ if city: params.append(f'city={city}')
213
+ query = urlencode({'country': country or '', 'city': city or ''})
214
+ resp = self._request('GET', f'/geodns/baps?{query}')
215
+ loc = GeoLocation(**resp['location'])
216
+ return GeoDnsResult(**resp, location=loc)
217
+
218
+ def discover_nearest_bpp(self, country: Optional[str] = None, city: Optional[str] = None) -> GeoDnsResult:
219
+ from urllib.parse import urlencode
220
+ query = urlencode({'country': country or '', 'city': city or ''})
221
+ resp = self._request('GET', f'/geodns/bpps?{query}')
222
+ loc = GeoLocation(**resp['location'])
223
+ return GeoDnsResult(**resp, location=loc)
224
+
225
+ def discover_marketplace(self, lat: float, lng: float, radius_km: int = 50, limit: int = 10) -> list[GeoDnsResult]:
226
+ resp = self._request('GET', f'/geodns/marketplaces?lat={lat}&lng={lng}&radius_km={radius_km}&limit={limit}')
227
+ return [GeoDnsResult(**r, location=GeoLocation(**r['location'])) for r in resp]
228
+
229
+ # === A2A ===
230
+ def list_agent_cards(self) -> list[AgentCard]:
231
+ return [AgentCard(**a) for a in self._request('GET', '/a2a/agent-cards')]
232
+
233
+ def register_agent_card(self, data: AgentCardRegister) -> AgentCard:
234
+ return AgentCard(**self._request('POST', '/a2a/agent-cards', _to_dict(data)))
235
+
236
+ def get_agent_card(self, agent_id: str) -> AgentCard:
237
+ return AgentCard(**self._request('GET', f'/a2a/agent-cards/{agent_id}'))
238
+
239
+ # === MCP ===
240
+ def list_tools(self) -> list[McpTool]:
241
+ return [McpTool(**t) for t in self._request('GET', '/mcp/tools')]
242
+
243
+ def create_tool(self, data: McpToolCreate) -> McpTool:
244
+ return McpTool(**self._request('POST', '/mcp/tools', _to_dict(data)))
245
+
246
+ # === ACP ===
247
+ def list_issuers(self) -> list[AcpIssuer]:
248
+ return [AcpIssuer(**i) for i in self._request('GET', '/acp/issuers')]
249
+
250
+ def register_issuer(self, data: AcpIssuerRegister) -> AcpIssuer:
251
+ return AcpIssuer(**self._request('POST', '/acp/issuers', _to_dict(data)))
252
+
253
+ def issue_token(self, data: AcpTokenIssue) -> AcpToken:
254
+ return AcpToken(**self._request('POST', '/acp/tokens', _to_dict(data)))
255
+
256
+ # === ANP ===
257
+ def list_announcements(self) -> list[AnpAnnouncement]:
258
+ return [AnpAnnouncement(**a) for a in self._request('GET', '/anp/announcements')]
259
+
260
+ def announce(self, data: AnpAnnouncementCreate) -> AnpAnnouncement:
261
+ return AnpAnnouncement(**self._request('POST', '/anp/announcements', _to_dict(data)))
262
+
263
+ # === Companies (B2B Multi-Tenant) ===
264
+ def list_companies(self) -> list[Company]:
265
+ return [Company(**c) for c in self._request('GET', '/companies')]
266
+
267
+ def create_company(self, data: CompanyCreate) -> Company:
268
+ return Company(**self._request('POST', '/companies', _to_dict(data)))
269
+
270
+ def get_company(self, company_id: str) -> Company:
271
+ return Company(**self._request('GET', f'/companies/{company_id}'))
272
+
273
+ def list_companies_by_domain(self, domain: str) -> list[Company]:
274
+ return [Company(**c) for c in self._request('GET', f'/companies?domain={domain}')]
275
+
276
+ # === GBP (Google Business Profile) ===
277
+ def list_gbp_accounts(self) -> list[Any]:
278
+ return self._request('GET', '/gbp/accounts')
279
+
280
+ def create_gbp_account(self, data: dict[str, Any]) -> Any:
281
+ return self._request('POST', '/gbp/accounts', data)
282
+
283
+ def sync_gbp_locations(self, account_id: str) -> dict[str, Any]:
284
+ return self._request('POST', f'/gbp/accounts/{account_id}/sync', {})
285
+
286
+ # === Health ===
287
+ def health(self) -> dict[str, Any]:
288
+ return self._request('GET', '/health')