Skip to content

Commit 934918a

Browse files
George-iamgeobon
andauthored
feat: add approvals and capabilities helpers to Python SDK (#8)
Expose approvals.decision and capabilities.get methods with tests and quickstart examples so Track C parity can progress in GA clients. Made-with: Cursor Co-authored-by: George-iam <georgeb@gmail.com>
1 parent c0d56a1 commit 934918a

3 files changed

Lines changed: 80 additions & 0 deletions

File tree

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@ with AxmeClient(config) as client:
4242
idempotency_key="reply-001",
4343
)
4444
print(replied)
45+
approval = client.decide_approval(
46+
"55555555-5555-4555-8555-555555555555",
47+
decision="approve",
48+
comment="Looks good",
49+
idempotency_key="approval-001",
50+
)
51+
print(approval["approval"]["decision"])
52+
capabilities = client.get_capabilities()
53+
print(capabilities["supported_intent_types"])
4554
subscription = client.upsert_webhook_subscription(
4655
{
4756
"callback_url": "https://integrator.example/webhooks/axme",

axme_sdk/client.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,30 @@ def reply_inbox_thread(
138138
retryable=idempotency_key is not None,
139139
)
140140

141+
def decide_approval(
142+
self,
143+
approval_id: str,
144+
*,
145+
decision: str,
146+
comment: str | None = None,
147+
idempotency_key: str | None = None,
148+
trace_id: str | None = None,
149+
) -> dict[str, Any]:
150+
payload: dict[str, Any] = {"decision": decision}
151+
if comment is not None:
152+
payload["comment"] = comment
153+
return self._request_json(
154+
"POST",
155+
f"/v1/approvals/{approval_id}/decision",
156+
json_body=payload,
157+
idempotency_key=idempotency_key,
158+
trace_id=trace_id,
159+
retryable=idempotency_key is not None,
160+
)
161+
162+
def get_capabilities(self, *, trace_id: str | None = None) -> dict[str, Any]:
163+
return self._request_json("GET", "/v1/capabilities", trace_id=trace_id, retryable=True)
164+
141165
def upsert_webhook_subscription(
142166
self,
143167
payload: dict[str, Any],

tests/test_client.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,53 @@ def handler(request: httpx.Request) -> httpx.Response:
231231
}
232232

233233

234+
def test_decide_approval_success() -> None:
235+
approval_id = "55555555-5555-4555-8555-555555555555"
236+
237+
def handler(request: httpx.Request) -> httpx.Response:
238+
assert request.method == "POST"
239+
assert request.url.path == f"/v1/approvals/{approval_id}/decision"
240+
assert request.headers["idempotency-key"] == "approval-1"
241+
assert request.read() == b'{"decision":"approve","comment":"approved"}'
242+
return httpx.Response(
243+
200,
244+
json={
245+
"ok": True,
246+
"approval": {
247+
"approval_id": approval_id,
248+
"decision": "approve",
249+
"comment": "approved",
250+
"decided_at": "2026-02-28T00:00:01Z",
251+
},
252+
},
253+
)
254+
255+
client = _client(handler)
256+
assert client.decide_approval(
257+
approval_id,
258+
decision="approve",
259+
comment="approved",
260+
idempotency_key="approval-1",
261+
)["approval"]["approval_id"] == approval_id
262+
263+
264+
def test_get_capabilities_success() -> None:
265+
def handler(request: httpx.Request) -> httpx.Response:
266+
assert request.method == "GET"
267+
assert request.url.path == "/v1/capabilities"
268+
return httpx.Response(
269+
200,
270+
json={
271+
"ok": True,
272+
"capabilities": ["inbox", "intents"],
273+
"supported_intent_types": ["intent.ask.v1", "intent.notify.v1"],
274+
},
275+
)
276+
277+
client = _client(handler)
278+
assert client.get_capabilities()["ok"] is True
279+
280+
234281
@pytest.mark.parametrize(
235282
("status_code", "expected_exception"),
236283
[

0 commit comments

Comments
 (0)