|
1 | 1 | # axme-sdk-python |
2 | 2 |
|
3 | | -Official Python SDK for Axme APIs and workflows. |
| 3 | +**Official Python SDK for the AXME platform.** Send and manage intents, observe lifecycle events, work with inbox and approvals, and access the full enterprise admin surface — all from idiomatic Python. |
4 | 4 |
|
5 | | -Canonical protocol positioning: |
| 5 | +> **Alpha** · API surface is stabilizing. Not recommended for production workloads yet. |
| 6 | +> Bug reports, feedback, and alpha access → [hello@axme.ai](mailto:hello@axme.ai) |
6 | 7 |
|
7 | | -- **AXP is the Intent Protocol (durable execution layer).** |
| 8 | +--- |
8 | 9 |
|
9 | | -## Status |
| 10 | +## What You Can Do With This SDK |
10 | 11 |
|
11 | | -Initial v1 skeleton in progress. |
| 12 | +The AXME Python SDK gives you a fully typed client for the AXME platform. You can: |
| 13 | + |
| 14 | +- **Send intents** — create typed, durable actions that the platform guarantees to deliver |
| 15 | +- **Observe lifecycle** — stream real-time state transitions, waiting events, and delivery confirmations |
| 16 | +- **Approve or reject** — handle human-in-the-loop steps directly from Python code |
| 17 | +- **Control workflows** — pause, resume, cancel, update retry policies and reminders mid-flight |
| 18 | +- **Administer** — manage organizations, workspaces, service accounts, and access grants |
| 19 | + |
| 20 | +--- |
| 21 | + |
| 22 | +## Install |
| 23 | + |
| 24 | +```bash |
| 25 | +pip install axme-sdk |
| 26 | +``` |
| 27 | + |
| 28 | +For local development from source: |
| 29 | + |
| 30 | +```bash |
| 31 | +python -m pip install -e ".[dev]" |
| 32 | +``` |
| 33 | + |
| 34 | +--- |
12 | 35 |
|
13 | 36 | ## Quickstart |
14 | 37 |
|
15 | 38 | ```python |
16 | 39 | from axme_sdk import AxmeClient, AxmeClientConfig |
17 | 40 |
|
18 | | -config = AxmeClientConfig( |
19 | | - base_url="https://gateway.example.com", |
20 | | - api_key="YOUR_API_KEY", |
21 | | - max_retries=2, |
22 | | - retry_backoff_seconds=0.2, |
| 41 | +client = AxmeClient( |
| 42 | + AxmeClientConfig( |
| 43 | + base_url="https://gateway.axme.ai", |
| 44 | + api_key="YOUR_API_KEY", |
| 45 | + ) |
23 | 46 | ) |
24 | 47 |
|
25 | | -with AxmeClient(config) as client: |
26 | | - print(client.health(trace_id="trace-quickstart-001")) |
27 | | - result = client.create_intent( |
28 | | - { |
29 | | - "intent_type": "notify.message.v1", |
30 | | - "from_agent": "agent://example/sender", |
31 | | - "to_agent": "agent://example/receiver", |
32 | | - "payload": {"text": "hello"}, |
33 | | - }, |
34 | | - correlation_id="11111111-1111-1111-1111-111111111111", |
35 | | - idempotency_key="create-intent-001", |
36 | | - ) |
37 | | - print(result) |
38 | | - print(client.get_intent(result["intent_id"])["intent"]["status"]) |
39 | | - sent_intent_id = client.send_intent( |
40 | | - { |
41 | | - "intent_type": "notify.message.v1", |
42 | | - "from_agent": "agent://example/sender", |
43 | | - "to_agent": "agent://example/receiver", |
44 | | - "payload": {"text": "hello again"}, |
45 | | - }, |
46 | | - idempotency_key="send-intent-001", |
47 | | - ) |
48 | | - print(sent_intent_id) |
49 | | - print(client.list_intent_events(sent_intent_id, since=0)) |
50 | | - for event in client.observe(sent_intent_id, since=0, wait_seconds=10): |
51 | | - print(event["event_type"], event["status"]) |
52 | | - if event["status"] in {"COMPLETED", "FAILED", "CANCELED"}: |
53 | | - break |
54 | | - terminal = client.wait_for(sent_intent_id, timeout_seconds=30) |
55 | | - print(terminal["status"]) |
56 | | - inbox = client.list_inbox(owner_agent="agent://example/receiver", trace_id="trace-inbox-001") |
57 | | - print(inbox) |
58 | | - thread = client.get_inbox_thread("11111111-1111-4111-8111-111111111111", owner_agent="agent://example/receiver") |
59 | | - print(thread["thread"]["status"]) |
60 | | - changes = client.list_inbox_changes(owner_agent="agent://example/receiver", limit=50) |
61 | | - print(changes["next_cursor"], changes["has_more"]) |
62 | | - replied = client.reply_inbox_thread( |
63 | | - "11111111-1111-4111-8111-111111111111", |
64 | | - message="Acknowledged", |
65 | | - owner_agent="agent://example/receiver", |
66 | | - idempotency_key="reply-001", |
67 | | - ) |
68 | | - print(replied) |
69 | | - delegated = client.delegate_inbox_thread( |
70 | | - "11111111-1111-4111-8111-111111111111", |
71 | | - {"delegate_to": "agent://example/delegate", "note": "handoff"}, |
72 | | - owner_agent="agent://example/receiver", |
73 | | - idempotency_key="delegate-001", |
74 | | - ) |
75 | | - print(delegated["thread"]["status"]) |
76 | | - approved = client.approve_inbox_thread( |
77 | | - "11111111-1111-4111-8111-111111111111", |
78 | | - {"comment": "approved"}, |
79 | | - owner_agent="agent://example/receiver", |
80 | | - idempotency_key="approve-001", |
81 | | - ) |
82 | | - print(approved["thread"]["status"]) |
83 | | - rejected = client.reject_inbox_thread( |
84 | | - "11111111-1111-4111-8111-111111111111", |
85 | | - {"comment": "rejected"}, |
86 | | - owner_agent="agent://example/receiver", |
87 | | - idempotency_key="reject-001", |
88 | | - ) |
89 | | - print(rejected["thread"]["status"]) |
90 | | - deleted = client.delete_inbox_messages( |
91 | | - "11111111-1111-4111-8111-111111111111", |
92 | | - {"mode": "self", "limit": 1}, |
93 | | - owner_agent="agent://example/receiver", |
94 | | - idempotency_key="delete-001", |
95 | | - ) |
96 | | - print(deleted["deleted_count"]) |
97 | | - approval = client.decide_approval( |
98 | | - "55555555-5555-4555-8555-555555555555", |
99 | | - decision="approve", |
100 | | - comment="Looks good", |
101 | | - idempotency_key="approval-001", |
102 | | - ) |
103 | | - print(approval["approval"]["decision"]) |
104 | | - capabilities = client.get_capabilities() |
105 | | - print(capabilities["supported_intent_types"]) |
106 | | - invite = client.create_invite( |
107 | | - {"owner_agent": "agent://example/receiver", "recipient_hint": "Partner A", "ttl_seconds": 3600}, |
108 | | - idempotency_key="invite-create-001", |
109 | | - ) |
110 | | - print(invite["token"]) |
111 | | - invite_details = client.get_invite(invite["token"]) |
112 | | - print(invite_details["status"]) |
113 | | - accepted = client.accept_invite( |
114 | | - invite["token"], |
115 | | - {"nick": "@PartnerA.User", "display_name": "Partner A"}, |
116 | | - idempotency_key="invite-accept-001", |
117 | | - ) |
118 | | - print(accepted["public_address"]) |
119 | | - media_upload = client.create_media_upload( |
120 | | - { |
121 | | - "owner_agent": "agent://example/receiver", |
122 | | - "filename": "contract.pdf", |
123 | | - "mime_type": "application/pdf", |
124 | | - "size_bytes": 12345, |
125 | | - }, |
126 | | - idempotency_key="media-create-001", |
127 | | - ) |
128 | | - print(media_upload["upload_id"]) |
129 | | - media_state = client.get_media_upload(media_upload["upload_id"]) |
130 | | - print(media_state["upload"]["status"]) |
131 | | - finalized = client.finalize_media_upload( |
132 | | - {"upload_id": media_upload["upload_id"], "size_bytes": 12345}, |
133 | | - idempotency_key="media-finalize-001", |
134 | | - ) |
135 | | - print(finalized["status"]) |
136 | | - schema = client.upsert_schema( |
137 | | - { |
138 | | - "semantic_type": "axme.calendar.schedule.v1", |
139 | | - "schema_json": {"type": "object", "required": ["date"], "properties": {"date": {"type": "string"}}}, |
140 | | - "compatibility_mode": "strict", |
141 | | - }, |
142 | | - idempotency_key="schema-upsert-001", |
143 | | - ) |
144 | | - print(schema["schema"]["schema_hash"]) |
145 | | - schema_get = client.get_schema("axme.calendar.schedule.v1") |
146 | | - print(schema_get["schema"]["semantic_type"]) |
147 | | - registered = client.register_nick( |
148 | | - {"nick": "@partner.user", "display_name": "Partner User"}, |
149 | | - idempotency_key="nick-register-001", |
150 | | - ) |
151 | | - print(registered["owner_agent"]) |
152 | | - nick_check = client.check_nick("@partner.user") |
153 | | - print(nick_check["available"]) |
154 | | - renamed = client.rename_nick( |
155 | | - {"owner_agent": registered["owner_agent"], "nick": "@partner.new"}, |
156 | | - idempotency_key="nick-rename-001", |
157 | | - ) |
158 | | - print(renamed["public_address"]) |
159 | | - profile = client.get_user_profile(registered["owner_agent"]) |
160 | | - print(profile["updated_at"]) |
161 | | - profile_updated = client.update_user_profile( |
162 | | - {"owner_agent": registered["owner_agent"], "display_name": "Partner User Updated"}, |
163 | | - idempotency_key="profile-update-001", |
164 | | - ) |
165 | | - print(profile_updated["display_name"]) |
166 | | - subscription = client.upsert_webhook_subscription( |
167 | | - { |
168 | | - "callback_url": "https://integrator.example/webhooks/axme", |
169 | | - "event_types": ["inbox.thread_created"], |
170 | | - "active": True, |
| 48 | +# Check connectivity |
| 49 | +print(client.health()) |
| 50 | + |
| 51 | +# Send an intent |
| 52 | +intent = client.create_intent( |
| 53 | + { |
| 54 | + "intent_type": "order.fulfillment.v1", |
| 55 | + "payload": {"order_id": "ord_123", "priority": "high"}, |
| 56 | + "owner_agent": "agent://fulfillment-service", |
| 57 | + }, |
| 58 | + idempotency_key="fulfill-ord-123-001", |
| 59 | +) |
| 60 | +print(intent["intent_id"], intent["status"]) |
| 61 | + |
| 62 | +# Wait for resolution |
| 63 | +resolved = client.wait_for(intent["intent_id"], terminal_states={"RESOLVED", "CANCELLED"}) |
| 64 | +print(resolved["status"]) |
| 65 | +``` |
| 66 | + |
| 67 | +--- |
| 68 | + |
| 69 | +## API Method Families |
| 70 | + |
| 71 | +The SDK covers the full public API surface organized into families. The map below shows all method groups and how they relate to the platform's intent lifecycle. |
| 72 | + |
| 73 | + |
| 74 | + |
| 75 | +*Each family corresponds to a segment of the lifecycle or an operational domain. Intents and inbox are D1 (core). Approvals, schemas, and media are D2. Enterprise admin and service accounts are D3.* |
| 76 | + |
| 77 | +--- |
| 78 | + |
| 79 | +## Create and Control Sequence |
| 80 | + |
| 81 | +From calling `create_intent()` to receiving a delivery confirmation — the full interaction sequence with the platform: |
| 82 | + |
| 83 | + |
| 84 | + |
| 85 | +*The SDK sets the `Idempotency-Key` and `X-Correlation-Id` headers automatically. The gateway validates, persists, and returns the intent in `PENDING` state. The scheduler picks it up and drives delivery.* |
| 86 | + |
| 87 | +--- |
| 88 | + |
| 89 | +## Idempotency and Replay Protection |
| 90 | + |
| 91 | +Every mutating call in the SDK accepts an optional `idempotency_key`. Use it for all operations you might retry. |
| 92 | + |
| 93 | + |
| 94 | + |
| 95 | +*Duplicate requests with the same key return the original response without re-executing. Keys expire after 24 hours. The SDK will warn if you reuse a key with different parameters.* |
| 96 | + |
| 97 | +```python |
| 98 | +# Safe to call multiple times — only executes once |
| 99 | +intent = client.create_intent(payload, idempotency_key="my-unique-key-001") |
| 100 | +``` |
| 101 | + |
| 102 | +--- |
| 103 | + |
| 104 | +## Observing Intent Events |
| 105 | + |
| 106 | +The SDK provides a streaming event observer that delivers real-time lifecycle events over SSE: |
| 107 | + |
| 108 | +```python |
| 109 | +for event in client.observe(intent["intent_id"]): |
| 110 | + print(event["event_type"], event["status"]) |
| 111 | + if event["status"] in {"RESOLVED", "CANCELLED", "EXPIRED"}: |
| 112 | + break |
| 113 | +``` |
| 114 | + |
| 115 | +--- |
| 116 | + |
| 117 | +## Approvals and Human-in-the-Loop |
| 118 | + |
| 119 | +```python |
| 120 | +# Fetch pending approvals for an agent |
| 121 | +pending = client.list_inbox({"owner_agent": "agent://manager", "status": "PENDING"}) |
| 122 | + |
| 123 | +for item in pending["items"]: |
| 124 | + # Approve with a note |
| 125 | + client.resolve_approval(item["intent_id"], {"decision": "approved", "note": "LGTM"}) |
| 126 | +``` |
| 127 | + |
| 128 | +--- |
| 129 | + |
| 130 | +## Workflow Controls |
| 131 | + |
| 132 | +Update retry policy, reminders, or TTL on a live intent without cancelling it: |
| 133 | + |
| 134 | +```python |
| 135 | +client.update_intent_controls( |
| 136 | + intent_id, |
| 137 | + { |
| 138 | + "controls": { |
| 139 | + "max_retries": 5, |
| 140 | + "retry_delay_seconds": 30, |
| 141 | + "reminders": [{"offset_seconds": 3600, "note": "1h reminder"}], |
171 | 142 | } |
172 | | - ) |
173 | | - print(subscription) |
174 | | - events = client.publish_webhook_event( |
175 | | - {"event_type": "inbox.thread_created", "source": "sdk-example", "payload": {"thread_id": "t-1"}}, |
176 | | - owner_agent="agent://example/receiver", |
177 | | - ) |
178 | | - print(events["event_id"]) |
179 | | - mcp_info = client.mcp_initialize() |
180 | | - print(mcp_info["protocolVersion"]) |
181 | | - tools = client.mcp_list_tools() |
182 | | - print(len(tools.get("tools", []))) |
183 | | - mcp_result = client.mcp_call_tool( |
184 | | - "axme.send", |
185 | | - arguments={"to": "agent://example/receiver", "text": "hello from MCP"}, |
186 | | - owner_agent="agent://example/receiver", |
187 | | - idempotency_key="mcp-send-001", |
188 | | - ) |
189 | | - print(mcp_result.get("status")) |
| 143 | + }, |
| 144 | + policy_generation=intent["policy_generation"], |
| 145 | +) |
190 | 146 | ``` |
191 | 147 |
|
192 | | -## Development |
| 148 | +--- |
| 149 | + |
| 150 | +## SDK Diagrams |
| 151 | + |
| 152 | +The SDK docs folder contains diagrams for the API patterns used by this client: |
| 153 | + |
| 154 | +| Diagram | Description | |
| 155 | +|---|---| |
| 156 | +| [`01-api-method-family-map`](docs/diagrams/01-api-method-family-map.svg) | Full API family overview | |
| 157 | +| [`02-create-and-control-sequence`](docs/diagrams/02-create-and-control-sequence.svg) | Intent creation and control flow | |
| 158 | +| [`03-idempotency-and-replay-protection`](docs/diagrams/03-idempotency-and-replay-protection.svg) | Idempotency protocol | |
| 159 | + |
| 160 | +--- |
| 161 | + |
| 162 | +## Tests |
193 | 163 |
|
194 | 164 | ```bash |
195 | | -python -m pip install -e ".[dev]" |
196 | 165 | pytest |
197 | 166 | ``` |
| 167 | + |
| 168 | +--- |
| 169 | + |
| 170 | +## Repository Structure |
| 171 | + |
| 172 | +``` |
| 173 | +axme-sdk-python/ |
| 174 | +├── axme_sdk/ |
| 175 | +│ ├── client.py # AxmeClient — all API methods |
| 176 | +│ ├── config.py # AxmeClientConfig |
| 177 | +│ └── exceptions.py # AxmeAPIError and subclasses |
| 178 | +├── tests/ # Unit and integration tests |
| 179 | +└── docs/ |
| 180 | + └── diagrams/ # Diagram copies for README embedding |
| 181 | +``` |
| 182 | + |
| 183 | +--- |
| 184 | + |
| 185 | +## Related Repositories |
| 186 | + |
| 187 | +| Repository | Role | |
| 188 | +|---|---| |
| 189 | +| [axme-docs](https://github.com/AxmeAI/axme-docs) | Full API reference and integration guides | |
| 190 | +| [axme-spec](https://github.com/AxmeAI/axme-spec) | Schema contracts this SDK implements | |
| 191 | +| [axme-conformance](https://github.com/AxmeAI/axme-conformance) | Conformance suite that validates this SDK | |
| 192 | +| [axme-examples](https://github.com/AxmeAI/axme-examples) | Runnable examples using this SDK | |
| 193 | +| [axme-sdk-typescript](https://github.com/AxmeAI/axme-sdk-typescript) | TypeScript equivalent | |
| 194 | +| [axme-sdk-go](https://github.com/AxmeAI/axme-sdk-go) | Go equivalent | |
| 195 | + |
| 196 | +--- |
| 197 | + |
| 198 | +## Contributing & Contact |
| 199 | + |
| 200 | +- Bug reports and feature requests: open an issue in this repository |
| 201 | +- Alpha program access and integration questions: [hello@axme.ai](mailto:hello@axme.ai) |
| 202 | +- Security disclosures: see [SECURITY.md](SECURITY.md) |
| 203 | +- Contribution guidelines: [CONTRIBUTING.md](CONTRIBUTING.md) |
0 commit comments