Skip to content

Commit 926e6b5

Browse files
Gkclaude
authored andcommitted
docs: complete README rewrite with badges, quickstart, error handling, method reference
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c0e2d05 commit 926e6b5

1 file changed

Lines changed: 198 additions & 82 deletions

File tree

README.md

Lines changed: 198 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,153 +1,269 @@
1-
# CueAPI Python SDK
1+
# cueapi-sdk
22

3-
The official Python SDK for [CueAPI](https://cueapi.ai) — scheduling infrastructure for agents.
3+
Your agents are failing silently. CueAPI tells you when and why.
4+
5+
**Cron has no concept of success. Cue does.**
6+
7+
[![PyPI version](https://img.shields.io/pypi/v/cueapi-sdk?label=pypi)](https://pypi.org/project/cueapi-sdk/)
8+
[![Python](https://img.shields.io/badge/python-3.9%2B-blue)](https://pypi.org/project/cueapi-sdk/)
9+
[![License](https://img.shields.io/badge/license-MIT-black)](LICENSE)
10+
[![Tests](https://img.shields.io/badge/tests-40%20passing-brightgreen)](.github/workflows)
11+
[![Docs](https://img.shields.io/badge/docs-docs.cueapi.ai-black)](https://docs.cueapi.ai)
12+
13+
The official Python SDK for [CueAPI](https://cueapi.ai) - open source execution accountability for AI agents.
14+
15+
---
416

517
## Install
618

719
```bash
820
pip install cueapi-sdk
921
```
1022

23+
---
24+
1125
## Quickstart
1226

1327
```python
1428
from cueapi import CueAPI
1529

1630
client = CueAPI("cue_sk_your_key")
31+
32+
# Schedule an agent to run every morning
1733
cue = client.cues.create(
18-
name="daily-report",
34+
name="morning-agent-brief",
1935
cron="0 9 * * *",
20-
callback="https://my-app.com/webhook",
21-
payload={"task": "generate_report"},
36+
timezone="America/Los_Angeles",
37+
callback="https://your-agent.com/run",
38+
payload={"task": "daily_brief"},
2239
)
23-
print(f"Next run: {cue.next_run}")
24-
```
2540

26-
## Why CueAPI?
41+
print(f"Scheduled. Next run: {cue.next_run}")
42+
# Scheduled. Next run: 2026-03-28T09:00:00-08:00
43+
```
2744

28-
- **Replace fragile cron jobs** — managed scheduling with automatic retries, execution logs, and failure alerts. No servers to maintain.
29-
- **Built for AI agents** — schedule agent tasks, coordinate multi-agent pipelines, and retry failed workflows with exponential backoff.
30-
- **Two transport modes** — webhook delivery to your public URL, or worker pull for agents behind firewalls.
45+
---
3146

32-
## Transport Modes
47+
## Why CueAPI over cron?
3348

34-
### Webhook
49+
Your agent ran at 3am. Did it succeed? Cron does not know.
3550

36-
CueAPI POSTs a signed payload to your callback URL when a cue fires:
51+
CueAPI tracks every execution separately from delivery, so you always know what happened.
3752

3853
```python
39-
cue = client.cues.create(
40-
name="webhook-task",
41-
cron="0 9 * * *",
42-
callback="https://my-app.com/webhook",
43-
payload={"action": "sync"},
44-
)
54+
# Check what actually happened
55+
execution = client.executions.get("exec_01HX...")
56+
print(execution.outcome) # "success" or "failure" -- reported by your handler
57+
print(execution.attempts) # 1 (or 2, 3 if it had to retry)
58+
print(execution.delivered_at) # exactly when it was delivered
59+
print(execution.status) # "delivered", "failed", "retrying"
4560
```
4661

47-
### Worker
62+
**What you get that cron cannot give you:**
63+
64+
- Execution history with outcome tracking
65+
- Automatic retries with exponential backoff (1, 5, 15 min)
66+
- Email + webhook alerts when all retries exhaust
67+
- Worker transport with no public URL needed
68+
- Signed webhook payloads
4869

49-
Your local daemon polls CueAPI for executions. No public URL needed:
70+
---
71+
72+
## Two transport modes
73+
74+
### Webhook (default)
75+
76+
CueAPI POSTs a signed payload to your URL when a cue fires:
5077

5178
```python
5279
cue = client.cues.create(
53-
name="worker-task",
80+
name="data-sync",
5481
cron="0 */6 * * *",
55-
transport="worker",
56-
payload={"pipeline": "etl"},
82+
callback="https://your-app.com/webhook",
83+
payload={"pipeline": "sync"},
5784
)
5885
```
5986

60-
Install the worker: `pip install cueapi-worker`
87+
Your handler receives the payload, processes it, and CueAPI records the outcome.
6188

62-
## Method Reference
89+
### Worker (no public URL needed)
6390

64-
### `CueAPI(api_key, *, base_url, timeout)`
91+
For agents running locally, in private networks, or behind firewalls. Your daemon polls CueAPI instead of waiting for inbound requests.
6592

66-
Create a client. `api_key` starts with `cue_sk_`.
67-
68-
### `client.cues.create(...)`
69-
70-
| Parameter | Type | Description |
71-
|---|---|---|
72-
| `name` | `str` | **Required.** Unique name for the cue. |
73-
| `cron` | `str` | Cron expression for recurring schedules. |
74-
| `at` | `str \| datetime` | ISO 8601 datetime for one-time schedules. |
75-
| `timezone` | `str` | IANA timezone (default `"UTC"`). |
76-
| `callback` | `str` | Webhook URL for execution delivery. |
77-
| `transport` | `str` | `"webhook"` (default) or `"worker"`. |
78-
| `payload` | `dict` | JSON payload included in each execution. |
79-
| `description` | `str` | Optional description. |
80-
| `retry` | `dict` | `{"max_attempts": 3, "backoff_minutes": [1, 5, 15]}` |
81-
| `on_failure` | `dict` | `{"email": true, "webhook": null, "pause": false}` |
82-
83-
Returns a `Cue` object.
84-
85-
### `client.cues.list(*, limit, offset, status)`
86-
87-
Returns a `CueList` with `.cues`, `.total`, `.limit`, `.offset`.
88-
89-
### `client.cues.get(cue_id)`
90-
91-
Returns a `Cue` object.
92-
93-
### `client.cues.update(cue_id, **fields)`
93+
```bash
94+
pip install cueapi-worker
95+
```
9496

95-
Update any field. Only provided fields are changed.
97+
```python
98+
from cueapi_worker import Worker
9699

97-
### `client.cues.pause(cue_id)`
100+
worker = Worker(api_key="cue_sk_your_key")
98101

99-
Pause a cue. Returns the updated `Cue`.
102+
@worker.handler("run-agent")
103+
def handle(payload):
104+
result = run_my_agent(payload["task"])
105+
return {"status": "success", "summary": result}
100106

101-
### `client.cues.resume(cue_id)`
107+
worker.start() # polls continuously, no inbound firewall rules needed
108+
```
102109

103-
Resume a paused cue. Returns the updated `Cue`.
110+
Create the cue with `transport="worker"`:
104111

105-
### `client.cues.delete(cue_id)`
112+
```python
113+
cue = client.cues.create(
114+
name="nightly-pipeline",
115+
cron="0 2 * * *",
116+
transport="worker",
117+
payload={"pipeline": "etl"},
118+
)
119+
```
106120

107-
Delete a cue. Returns `None`.
121+
---
108122

109-
## Webhook Verification
123+
## Webhook verification
110124

111-
Verify incoming webhook signatures in your handler:
125+
Always verify incoming webhook signatures before processing:
112126

113127
```python
114128
from cueapi import verify_webhook
115129

116-
is_valid = verify_webhook(
117-
payload=request.body,
118-
signature=request.headers["X-CueAPI-Signature"],
119-
timestamp=request.headers["X-CueAPI-Timestamp"],
120-
secret="whsec_your_secret",
121-
)
130+
@app.post("/webhook")
131+
def handle_cue(request: Request):
132+
is_valid = verify_webhook(
133+
payload=request.body,
134+
signature=request.headers["X-CueAPI-Signature"],
135+
timestamp=request.headers["X-CueAPI-Timestamp"],
136+
secret="whsec_your_secret",
137+
tolerance=300, # seconds, default
138+
)
139+
140+
if not is_valid:
141+
return {"error": "invalid signature"}, 401
142+
143+
data = request.json()
144+
run_task(data["payload"])
145+
return {"outcome": "success"}
122146
```
123147

124-
## Error Handling
148+
Signatures use HMAC-SHA256 in `v1={hex}` format. The `tolerance` parameter rejects replayed requests older than 5 minutes.
149+
150+
---
151+
152+
## Error handling
125153

126154
```python
127-
from cueapi import CueAPI, AuthenticationError, RateLimitError, CueNotFoundError
155+
from cueapi import (
156+
CueAPI,
157+
AuthenticationError,
158+
CueLimitExceededError,
159+
CueNotFoundError,
160+
InvalidScheduleError,
161+
RateLimitError,
162+
CueAPIServerError,
163+
)
128164

129165
try:
130-
cue = client.cues.get("cue_abc123")
131-
except CueNotFoundError:
132-
print("Cue not found")
166+
cue = client.cues.create(
167+
name="my-agent",
168+
cron="0 9 * * *",
169+
callback="https://example.com/run",
170+
)
171+
except CueLimitExceededError:
172+
print("Plan limit reached. Upgrade at cueapi.ai")
173+
except InvalidScheduleError as e:
174+
print(f"Bad cron expression: {e}")
133175
except AuthenticationError:
134176
print("Invalid API key")
135177
except RateLimitError as e:
136178
print(f"Rate limited. Retry after {e.retry_after}s")
179+
except CueAPIServerError:
180+
print("Server error. CueAPI status at status.cueapi.ai")
137181
```
138182

139-
| Exception | HTTP Status | When |
183+
| Exception | HTTP | When |
140184
|---|---|---|
141185
| `AuthenticationError` | 401 | Invalid or missing API key |
142186
| `CueLimitExceededError` | 403 | Plan cue limit reached |
143-
| `CueNotFoundError` | 404 | Cue ID doesn't exist |
187+
| `CueNotFoundError` | 404 | Cue ID does not exist |
144188
| `InvalidScheduleError` | 400/422 | Bad cron expression or request body |
145189
| `RateLimitError` | 429 | Too many requests |
146190
| `CueAPIServerError` | 5xx | Server error |
147191

192+
---
193+
194+
## Full method reference
195+
196+
### `CueAPI(api_key, *, base_url, timeout)`
197+
198+
```python
199+
client = CueAPI(
200+
api_key="cue_sk_...", # or set CUEAPI_API_KEY env var
201+
base_url="https://api.cueapi.ai", # default
202+
timeout=30, # seconds, default
203+
)
204+
```
205+
206+
### `client.cues.create(**fields)`
207+
208+
| Parameter | Type | Description |
209+
|---|---|---|
210+
| `name` | `str` | Required. Unique name. |
211+
| `cron` | `str` | Cron expression for recurring schedules. |
212+
| `at` | `str or datetime` | ISO 8601 for one-time schedules. |
213+
| `timezone` | `str` | IANA timezone (default `"UTC"`). |
214+
| `callback` | `str` | Webhook URL (required if `transport="webhook"`). |
215+
| `transport` | `str` | `"webhook"` (default) or `"worker"`. |
216+
| `payload` | `dict` | JSON payload included in each execution. |
217+
| `description` | `str` | Optional description. |
218+
| `retry` | `dict` | `{"max_attempts": 3, "backoff_minutes": [1, 5, 15]}` |
219+
| `on_failure` | `dict` | `{"email": true, "webhook": null, "pause": false}` |
220+
221+
Returns a `Cue` object.
222+
223+
### Other cue methods
224+
225+
```python
226+
client.cues.list(limit=20, offset=0, status="active") # CueList
227+
client.cues.get("cue_abc123") # Cue
228+
client.cues.update("cue_abc123", cron="0 10 * * *") # Cue
229+
client.cues.pause("cue_abc123") # Cue
230+
client.cues.resume("cue_abc123") # Cue
231+
client.cues.delete("cue_abc123") # None
232+
```
233+
234+
### Executions
235+
236+
```python
237+
client.executions.list(cue_id="cue_abc123", limit=20) # ExecutionList
238+
client.executions.get("exec_01HX...") # Execution
239+
```
240+
241+
---
242+
243+
## Examples
244+
245+
See [`/examples`](examples/) for working code:
246+
247+
- [`basic_usage.py`](examples/basic_usage.py) - create, list, pause, delete cues
248+
- [`webhook_handler.py`](examples/webhook_handler.py) - FastAPI handler with signature verification
249+
- [`worker_setup.py`](examples/worker_setup.py) - worker daemon for private network agents
250+
251+
---
252+
148253
## Links
149254

150-
- [Documentation](https://docs.cueapi.ai)
151-
- [API Reference](https://docs.cueapi.ai/api-reference/overview/)
152-
- [Dashboard](https://dashboard.cueapi.ai)
153-
- [CueAPI](https://cueapi.ai)
255+
- [Dashboard](https://dashboard.cueapi.ai) - manage cues and view executions
256+
- [Documentation](https://docs.cueapi.ai) - full guides and API reference
257+
- [API Reference](https://docs.cueapi.ai/api-reference/overview) - all endpoints
258+
- [cueapi-core](https://github.com/cueapi/cueapi-core) - open source server
259+
- [cueapi.ai](https://cueapi.ai) - hosted service, free tier available
260+
261+
---
262+
263+
## License
264+
265+
MIT. See [LICENSE](LICENSE).
266+
267+
---
268+
269+
*Built by [Vector Apps](https://cueapi.ai/about)*

0 commit comments

Comments
 (0)