orita-sdk 0.1.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.
- orita_sdk-0.1.0/PKG-INFO +383 -0
- orita_sdk-0.1.0/README.md +367 -0
- orita_sdk-0.1.0/orita/__init__.py +5 -0
- orita_sdk-0.1.0/orita/client.py +99 -0
- orita_sdk-0.1.0/orita/exceptions.py +11 -0
- orita_sdk-0.1.0/orita_sdk.egg-info/PKG-INFO +383 -0
- orita_sdk-0.1.0/orita_sdk.egg-info/SOURCES.txt +10 -0
- orita_sdk-0.1.0/orita_sdk.egg-info/dependency_links.txt +1 -0
- orita_sdk-0.1.0/orita_sdk.egg-info/requires.txt +1 -0
- orita_sdk-0.1.0/orita_sdk.egg-info/top_level.txt +1 -0
- orita_sdk-0.1.0/pyproject.toml +23 -0
- orita_sdk-0.1.0/setup.cfg +4 -0
orita_sdk-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: orita-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: The scheduling infrastructure for AI agents — Python SDK
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://orita.online/developers
|
|
7
|
+
Project-URL: Repository, https://github.com/Alkilo-do/orita-python
|
|
8
|
+
Project-URL: Documentation, https://orita.online/developers
|
|
9
|
+
Keywords: scheduling,booking,ai-agents,api
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Requires-Python: >=3.8
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: requests>=2.28
|
|
16
|
+
|
|
17
|
+
# orita-python
|
|
18
|
+
|
|
19
|
+
[](https://pypi.org/project/orita-sdk/)
|
|
20
|
+
[](https://www.python.org/downloads/)
|
|
21
|
+
[](https://opensource.org/licenses/MIT)
|
|
22
|
+
|
|
23
|
+
**The scheduling infrastructure for AI agents — Python SDK**
|
|
24
|
+
|
|
25
|
+
[Orita](https://orita.online) is the scheduling layer purpose-built for AI agents. Connect your LLM to real calendar availability in minutes.
|
|
26
|
+
|
|
27
|
+
→ **Docs & API keys:** [orita.online/developers](https://orita.online/developers)
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install orita-sdk
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Quickstart
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from orita import OritaClient
|
|
41
|
+
|
|
42
|
+
client = OritaClient(api_key="orita_your_key_here")
|
|
43
|
+
slots = client.slots(event_type_id="evt_abc123", date="2026-08-01")
|
|
44
|
+
booking = client.book(
|
|
45
|
+
event_type_id="evt_abc123", date="2026-08-01", time=slots[0]["value"],
|
|
46
|
+
client_name="Ana", client_lastname="López", client_email="ana@example.com",
|
|
47
|
+
)
|
|
48
|
+
print(booking["id"]) # book_xyz789
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Authentication
|
|
54
|
+
|
|
55
|
+
All requests require an API key obtained from [orita.online/developers](https://orita.online/developers).
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from orita import OritaClient
|
|
59
|
+
|
|
60
|
+
client = OritaClient(api_key="orita_your_key_here")
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
API keys must start with `orita_`. Pass a custom `base_url` to point to a self-hosted or staging instance.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## API Reference
|
|
68
|
+
|
|
69
|
+
### `client.event_types() → list`
|
|
70
|
+
|
|
71
|
+
List all active event types for your account.
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
event_types = client.event_types()
|
|
75
|
+
# [{"id": "evt_abc123", "title": "Initial Consultation", "duration": 30, ...}]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
### `client.slots(event_type_id, date) → list`
|
|
81
|
+
|
|
82
|
+
Get available time slots for a given event type on a specific date.
|
|
83
|
+
|
|
84
|
+
| Parameter | Type | Description |
|
|
85
|
+
|-----------|------|-------------|
|
|
86
|
+
| `event_type_id` | `str` | Event type ID from `event_types()` |
|
|
87
|
+
| `date` | `str` | Date in `YYYY-MM-DD` format |
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
slots = client.slots(event_type_id="evt_abc123", date="2026-08-01")
|
|
91
|
+
# [{"label": "09:00 AM", "value": "09:00"}, {"label": "09:30 AM", "value": "09:30"}, ...]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
### `client.book(...) → dict`
|
|
97
|
+
|
|
98
|
+
Book an appointment. Returns the created booking object.
|
|
99
|
+
|
|
100
|
+
| Parameter | Type | Required | Description |
|
|
101
|
+
|-----------|------|----------|-------------|
|
|
102
|
+
| `event_type_id` | `str` | ✅ | Event type ID |
|
|
103
|
+
| `date` | `str` | ✅ | Date in `YYYY-MM-DD` |
|
|
104
|
+
| `time` | `str` | ✅ | Time in `HH:MM` (from `slots()`) |
|
|
105
|
+
| `client_name` | `str` | ✅ | Client first name |
|
|
106
|
+
| `client_lastname` | `str` | ✅ | Client last name |
|
|
107
|
+
| `client_email` | `str` | ✅ | Client email |
|
|
108
|
+
| `client_timezone` | `str` | ❌ | IANA timezone (e.g. `America/New_York`) |
|
|
109
|
+
| `notes` | `str` | ❌ | Additional notes for the professional |
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
booking = client.book(
|
|
113
|
+
event_type_id="evt_abc123",
|
|
114
|
+
date="2026-08-01",
|
|
115
|
+
time="10:00",
|
|
116
|
+
client_name="Carlos",
|
|
117
|
+
client_lastname="García",
|
|
118
|
+
client_email="carlos@example.com",
|
|
119
|
+
client_timezone="America/Bogota",
|
|
120
|
+
notes="First appointment — prefers video call",
|
|
121
|
+
)
|
|
122
|
+
# {"id": "book_xyz789", "status": "confirmed", "date": "2026-08-01", ...}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### `client.bookings(page, limit, status) → list`
|
|
128
|
+
|
|
129
|
+
List bookings with optional filtering.
|
|
130
|
+
|
|
131
|
+
| Parameter | Type | Default | Description |
|
|
132
|
+
|-----------|------|---------|-------------|
|
|
133
|
+
| `page` | `int` | `1` | Page number |
|
|
134
|
+
| `limit` | `int` | `20` | Results per page |
|
|
135
|
+
| `status` | `str` | `None` | Filter: `confirmed`, `cancelled`, `completed` |
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
confirmed = client.bookings(status="confirmed")
|
|
139
|
+
all_bookings = client.bookings(page=2, limit=50)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
### `client.get_booking(booking_id) → dict`
|
|
145
|
+
|
|
146
|
+
Retrieve a single booking by ID.
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
booking = client.get_booking("book_xyz789")
|
|
150
|
+
print(booking["status"]) # "confirmed"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
### `client.cancel(booking_id) → dict`
|
|
156
|
+
|
|
157
|
+
Cancel a booking by ID.
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
result = client.cancel("book_xyz789")
|
|
161
|
+
print(result["status"]) # "cancelled"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
### `client.profile() → dict`
|
|
167
|
+
|
|
168
|
+
Get your own **Capability Manifest** — the machine-readable description of your scheduling configuration that AI agents can read to understand how to book you.
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
manifest = client.profile()
|
|
172
|
+
print(manifest["username"])
|
|
173
|
+
print(manifest["eventTypes"])
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
### `client.update_profile(**fields) → dict`
|
|
179
|
+
|
|
180
|
+
Update your profile fields.
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
updated = client.update_profile(bio="AI-native therapist", timezone="Europe/Madrid")
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
### `client.get_profile(username) → dict`
|
|
189
|
+
|
|
190
|
+
Fetch the **public Capability Manifest** for any professional by username. No API key required internally — useful for cross-professional lookups.
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
manifest = client.get_profile("dra-martinez")
|
|
194
|
+
# {"username": "dra-martinez", "eventTypes": [...], ...}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Error Handling
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
from orita import OritaClient
|
|
203
|
+
from orita import OritaAuthError, OritaNotFoundError, OritaSlotUnavailableError, OritaError
|
|
204
|
+
|
|
205
|
+
client = OritaClient(api_key="orita_your_key")
|
|
206
|
+
|
|
207
|
+
try:
|
|
208
|
+
booking = client.book(
|
|
209
|
+
event_type_id="evt_abc123",
|
|
210
|
+
date="2026-08-01",
|
|
211
|
+
time="10:00",
|
|
212
|
+
client_name="Ana",
|
|
213
|
+
client_lastname="López",
|
|
214
|
+
client_email="ana@example.com",
|
|
215
|
+
)
|
|
216
|
+
except OritaAuthError:
|
|
217
|
+
print("Invalid API key")
|
|
218
|
+
except OritaSlotUnavailableError:
|
|
219
|
+
print("That slot was just taken — fetch slots again")
|
|
220
|
+
except OritaNotFoundError:
|
|
221
|
+
print("Event type not found")
|
|
222
|
+
except OritaError as e:
|
|
223
|
+
print(f"API error: {e}")
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
| Exception | HTTP Status | When |
|
|
227
|
+
|-----------|-------------|------|
|
|
228
|
+
| `OritaAuthError` | 401 | Invalid or missing API key |
|
|
229
|
+
| `OritaNotFoundError` | 404 | Resource not found |
|
|
230
|
+
| `OritaSlotUnavailableError` | 409 | Slot already taken |
|
|
231
|
+
| `OritaError` | Other 4xx/5xx | Generic API error |
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Framework Integrations
|
|
236
|
+
|
|
237
|
+
### OpenAI Agents SDK
|
|
238
|
+
|
|
239
|
+
Give your OpenAI agent the ability to book appointments in real time:
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
from openai import OpenAI
|
|
243
|
+
from orita import OritaClient
|
|
244
|
+
import json
|
|
245
|
+
|
|
246
|
+
orita = OritaClient(api_key="orita_your_key")
|
|
247
|
+
client = OpenAI()
|
|
248
|
+
|
|
249
|
+
tools = [
|
|
250
|
+
{
|
|
251
|
+
"type": "function",
|
|
252
|
+
"function": {
|
|
253
|
+
"name": "get_available_slots",
|
|
254
|
+
"description": "Get available appointment slots for a given date",
|
|
255
|
+
"parameters": {
|
|
256
|
+
"type": "object",
|
|
257
|
+
"properties": {
|
|
258
|
+
"event_type_id": {"type": "string"},
|
|
259
|
+
"date": {"type": "string", "description": "YYYY-MM-DD"},
|
|
260
|
+
},
|
|
261
|
+
"required": ["event_type_id", "date"],
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
]
|
|
266
|
+
|
|
267
|
+
def handle_tool_call(name, args):
|
|
268
|
+
if name == "get_available_slots":
|
|
269
|
+
return json.dumps(orita.slots(args["event_type_id"], args["date"]))
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
→ Full example: [`examples/openai_agent.py`](examples/openai_agent.py)
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
### LangChain
|
|
277
|
+
|
|
278
|
+
Wrap Orita methods as LangChain `@tool` functions:
|
|
279
|
+
|
|
280
|
+
```python
|
|
281
|
+
from langchain.tools import tool
|
|
282
|
+
from orita import OritaClient
|
|
283
|
+
|
|
284
|
+
orita = OritaClient(api_key="orita_your_key")
|
|
285
|
+
|
|
286
|
+
@tool
|
|
287
|
+
def get_available_slots(date_str: str) -> str:
|
|
288
|
+
"""Get available appointment slots for a date (YYYY-MM-DD)."""
|
|
289
|
+
slots = orita.slots(event_type_id="evt_abc123", date=date_str)
|
|
290
|
+
return "\n".join([f"- {s['label']}" for s in slots])
|
|
291
|
+
|
|
292
|
+
@tool
|
|
293
|
+
def book_appointment(date_str: str, time_str: str, name: str, lastname: str, email: str) -> str:
|
|
294
|
+
"""Book an appointment for a client."""
|
|
295
|
+
booking = orita.book(
|
|
296
|
+
event_type_id="evt_abc123", date=date_str, time=time_str,
|
|
297
|
+
client_name=name, client_lastname=lastname, client_email=email,
|
|
298
|
+
)
|
|
299
|
+
return f"Confirmed! Booking ID: {booking['id']}"
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
→ Full example: [`examples/langchain_tool.py`](examples/langchain_tool.py)
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
### CrewAI
|
|
307
|
+
|
|
308
|
+
```python
|
|
309
|
+
from crewai.tools import tool
|
|
310
|
+
from orita import OritaClient
|
|
311
|
+
|
|
312
|
+
orita = OritaClient(api_key="orita_your_key")
|
|
313
|
+
|
|
314
|
+
@tool("Get Available Slots")
|
|
315
|
+
def get_slots(event_type_id: str, date: str) -> str:
|
|
316
|
+
"""Get available appointment slots. Input: event_type_id and date (YYYY-MM-DD)."""
|
|
317
|
+
slots = orita.slots(event_type_id=event_type_id, date=date)
|
|
318
|
+
return str([{"label": s["label"], "value": s["value"]} for s in slots])
|
|
319
|
+
|
|
320
|
+
@tool("Book Appointment")
|
|
321
|
+
def book(event_type_id: str, date: str, time: str,
|
|
322
|
+
client_name: str, client_lastname: str, client_email: str) -> str:
|
|
323
|
+
"""Book an appointment for a client."""
|
|
324
|
+
booking = orita.book(
|
|
325
|
+
event_type_id=event_type_id, date=date, time=time,
|
|
326
|
+
client_name=client_name, client_lastname=client_lastname, client_email=client_email,
|
|
327
|
+
)
|
|
328
|
+
return f"Booking {booking['id']} confirmed for {date} at {time}"
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## Full Example: Book from Available Slots
|
|
334
|
+
|
|
335
|
+
```python
|
|
336
|
+
from orita import OritaClient
|
|
337
|
+
from datetime import date, timedelta
|
|
338
|
+
|
|
339
|
+
client = OritaClient(api_key="orita_your_key_here")
|
|
340
|
+
|
|
341
|
+
# 1. Get event types
|
|
342
|
+
event_types = client.event_types()
|
|
343
|
+
event_type_id = event_types[0]["id"]
|
|
344
|
+
|
|
345
|
+
# 2. Get tomorrow's slots
|
|
346
|
+
tomorrow = (date.today() + timedelta(days=1)).isoformat()
|
|
347
|
+
slots = client.slots(event_type_id=event_type_id, date=tomorrow)
|
|
348
|
+
|
|
349
|
+
# 3. Book the first available
|
|
350
|
+
if slots:
|
|
351
|
+
booking = client.book(
|
|
352
|
+
event_type_id=event_type_id,
|
|
353
|
+
date=tomorrow,
|
|
354
|
+
time=slots[0]["value"],
|
|
355
|
+
client_name="Juan",
|
|
356
|
+
client_lastname="García",
|
|
357
|
+
client_email="juan@example.com",
|
|
358
|
+
)
|
|
359
|
+
print(f"✅ Booked: {booking['id']}")
|
|
360
|
+
else:
|
|
361
|
+
print("No availability tomorrow")
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## Requirements
|
|
367
|
+
|
|
368
|
+
- Python 3.8+
|
|
369
|
+
- `requests >= 2.28`
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## Links
|
|
374
|
+
|
|
375
|
+
- 🌐 **Website:** [orita.online](https://orita.online)
|
|
376
|
+
- 📚 **Developer docs:** [orita.online/developers](https://orita.online/developers)
|
|
377
|
+
- 🐛 **Issues:** [github.com/Alkilo-do/orita-python/issues](https://github.com/Alkilo-do/orita-python/issues)
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## License
|
|
382
|
+
|
|
383
|
+
MIT © [Alkilo-do](https://github.com/Alkilo-do)
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
# orita-python
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/orita-sdk/)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
**The scheduling infrastructure for AI agents — Python SDK**
|
|
8
|
+
|
|
9
|
+
[Orita](https://orita.online) is the scheduling layer purpose-built for AI agents. Connect your LLM to real calendar availability in minutes.
|
|
10
|
+
|
|
11
|
+
→ **Docs & API keys:** [orita.online/developers](https://orita.online/developers)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install orita-sdk
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quickstart
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from orita import OritaClient
|
|
25
|
+
|
|
26
|
+
client = OritaClient(api_key="orita_your_key_here")
|
|
27
|
+
slots = client.slots(event_type_id="evt_abc123", date="2026-08-01")
|
|
28
|
+
booking = client.book(
|
|
29
|
+
event_type_id="evt_abc123", date="2026-08-01", time=slots[0]["value"],
|
|
30
|
+
client_name="Ana", client_lastname="López", client_email="ana@example.com",
|
|
31
|
+
)
|
|
32
|
+
print(booking["id"]) # book_xyz789
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Authentication
|
|
38
|
+
|
|
39
|
+
All requests require an API key obtained from [orita.online/developers](https://orita.online/developers).
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from orita import OritaClient
|
|
43
|
+
|
|
44
|
+
client = OritaClient(api_key="orita_your_key_here")
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
API keys must start with `orita_`. Pass a custom `base_url` to point to a self-hosted or staging instance.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## API Reference
|
|
52
|
+
|
|
53
|
+
### `client.event_types() → list`
|
|
54
|
+
|
|
55
|
+
List all active event types for your account.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
event_types = client.event_types()
|
|
59
|
+
# [{"id": "evt_abc123", "title": "Initial Consultation", "duration": 30, ...}]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
### `client.slots(event_type_id, date) → list`
|
|
65
|
+
|
|
66
|
+
Get available time slots for a given event type on a specific date.
|
|
67
|
+
|
|
68
|
+
| Parameter | Type | Description |
|
|
69
|
+
|-----------|------|-------------|
|
|
70
|
+
| `event_type_id` | `str` | Event type ID from `event_types()` |
|
|
71
|
+
| `date` | `str` | Date in `YYYY-MM-DD` format |
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
slots = client.slots(event_type_id="evt_abc123", date="2026-08-01")
|
|
75
|
+
# [{"label": "09:00 AM", "value": "09:00"}, {"label": "09:30 AM", "value": "09:30"}, ...]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
### `client.book(...) → dict`
|
|
81
|
+
|
|
82
|
+
Book an appointment. Returns the created booking object.
|
|
83
|
+
|
|
84
|
+
| Parameter | Type | Required | Description |
|
|
85
|
+
|-----------|------|----------|-------------|
|
|
86
|
+
| `event_type_id` | `str` | ✅ | Event type ID |
|
|
87
|
+
| `date` | `str` | ✅ | Date in `YYYY-MM-DD` |
|
|
88
|
+
| `time` | `str` | ✅ | Time in `HH:MM` (from `slots()`) |
|
|
89
|
+
| `client_name` | `str` | ✅ | Client first name |
|
|
90
|
+
| `client_lastname` | `str` | ✅ | Client last name |
|
|
91
|
+
| `client_email` | `str` | ✅ | Client email |
|
|
92
|
+
| `client_timezone` | `str` | ❌ | IANA timezone (e.g. `America/New_York`) |
|
|
93
|
+
| `notes` | `str` | ❌ | Additional notes for the professional |
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
booking = client.book(
|
|
97
|
+
event_type_id="evt_abc123",
|
|
98
|
+
date="2026-08-01",
|
|
99
|
+
time="10:00",
|
|
100
|
+
client_name="Carlos",
|
|
101
|
+
client_lastname="García",
|
|
102
|
+
client_email="carlos@example.com",
|
|
103
|
+
client_timezone="America/Bogota",
|
|
104
|
+
notes="First appointment — prefers video call",
|
|
105
|
+
)
|
|
106
|
+
# {"id": "book_xyz789", "status": "confirmed", "date": "2026-08-01", ...}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
### `client.bookings(page, limit, status) → list`
|
|
112
|
+
|
|
113
|
+
List bookings with optional filtering.
|
|
114
|
+
|
|
115
|
+
| Parameter | Type | Default | Description |
|
|
116
|
+
|-----------|------|---------|-------------|
|
|
117
|
+
| `page` | `int` | `1` | Page number |
|
|
118
|
+
| `limit` | `int` | `20` | Results per page |
|
|
119
|
+
| `status` | `str` | `None` | Filter: `confirmed`, `cancelled`, `completed` |
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
confirmed = client.bookings(status="confirmed")
|
|
123
|
+
all_bookings = client.bookings(page=2, limit=50)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
### `client.get_booking(booking_id) → dict`
|
|
129
|
+
|
|
130
|
+
Retrieve a single booking by ID.
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
booking = client.get_booking("book_xyz789")
|
|
134
|
+
print(booking["status"]) # "confirmed"
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
### `client.cancel(booking_id) → dict`
|
|
140
|
+
|
|
141
|
+
Cancel a booking by ID.
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
result = client.cancel("book_xyz789")
|
|
145
|
+
print(result["status"]) # "cancelled"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
### `client.profile() → dict`
|
|
151
|
+
|
|
152
|
+
Get your own **Capability Manifest** — the machine-readable description of your scheduling configuration that AI agents can read to understand how to book you.
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
manifest = client.profile()
|
|
156
|
+
print(manifest["username"])
|
|
157
|
+
print(manifest["eventTypes"])
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
### `client.update_profile(**fields) → dict`
|
|
163
|
+
|
|
164
|
+
Update your profile fields.
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
updated = client.update_profile(bio="AI-native therapist", timezone="Europe/Madrid")
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
### `client.get_profile(username) → dict`
|
|
173
|
+
|
|
174
|
+
Fetch the **public Capability Manifest** for any professional by username. No API key required internally — useful for cross-professional lookups.
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
manifest = client.get_profile("dra-martinez")
|
|
178
|
+
# {"username": "dra-martinez", "eventTypes": [...], ...}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Error Handling
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from orita import OritaClient
|
|
187
|
+
from orita import OritaAuthError, OritaNotFoundError, OritaSlotUnavailableError, OritaError
|
|
188
|
+
|
|
189
|
+
client = OritaClient(api_key="orita_your_key")
|
|
190
|
+
|
|
191
|
+
try:
|
|
192
|
+
booking = client.book(
|
|
193
|
+
event_type_id="evt_abc123",
|
|
194
|
+
date="2026-08-01",
|
|
195
|
+
time="10:00",
|
|
196
|
+
client_name="Ana",
|
|
197
|
+
client_lastname="López",
|
|
198
|
+
client_email="ana@example.com",
|
|
199
|
+
)
|
|
200
|
+
except OritaAuthError:
|
|
201
|
+
print("Invalid API key")
|
|
202
|
+
except OritaSlotUnavailableError:
|
|
203
|
+
print("That slot was just taken — fetch slots again")
|
|
204
|
+
except OritaNotFoundError:
|
|
205
|
+
print("Event type not found")
|
|
206
|
+
except OritaError as e:
|
|
207
|
+
print(f"API error: {e}")
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
| Exception | HTTP Status | When |
|
|
211
|
+
|-----------|-------------|------|
|
|
212
|
+
| `OritaAuthError` | 401 | Invalid or missing API key |
|
|
213
|
+
| `OritaNotFoundError` | 404 | Resource not found |
|
|
214
|
+
| `OritaSlotUnavailableError` | 409 | Slot already taken |
|
|
215
|
+
| `OritaError` | Other 4xx/5xx | Generic API error |
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Framework Integrations
|
|
220
|
+
|
|
221
|
+
### OpenAI Agents SDK
|
|
222
|
+
|
|
223
|
+
Give your OpenAI agent the ability to book appointments in real time:
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
from openai import OpenAI
|
|
227
|
+
from orita import OritaClient
|
|
228
|
+
import json
|
|
229
|
+
|
|
230
|
+
orita = OritaClient(api_key="orita_your_key")
|
|
231
|
+
client = OpenAI()
|
|
232
|
+
|
|
233
|
+
tools = [
|
|
234
|
+
{
|
|
235
|
+
"type": "function",
|
|
236
|
+
"function": {
|
|
237
|
+
"name": "get_available_slots",
|
|
238
|
+
"description": "Get available appointment slots for a given date",
|
|
239
|
+
"parameters": {
|
|
240
|
+
"type": "object",
|
|
241
|
+
"properties": {
|
|
242
|
+
"event_type_id": {"type": "string"},
|
|
243
|
+
"date": {"type": "string", "description": "YYYY-MM-DD"},
|
|
244
|
+
},
|
|
245
|
+
"required": ["event_type_id", "date"],
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
]
|
|
250
|
+
|
|
251
|
+
def handle_tool_call(name, args):
|
|
252
|
+
if name == "get_available_slots":
|
|
253
|
+
return json.dumps(orita.slots(args["event_type_id"], args["date"]))
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
→ Full example: [`examples/openai_agent.py`](examples/openai_agent.py)
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
### LangChain
|
|
261
|
+
|
|
262
|
+
Wrap Orita methods as LangChain `@tool` functions:
|
|
263
|
+
|
|
264
|
+
```python
|
|
265
|
+
from langchain.tools import tool
|
|
266
|
+
from orita import OritaClient
|
|
267
|
+
|
|
268
|
+
orita = OritaClient(api_key="orita_your_key")
|
|
269
|
+
|
|
270
|
+
@tool
|
|
271
|
+
def get_available_slots(date_str: str) -> str:
|
|
272
|
+
"""Get available appointment slots for a date (YYYY-MM-DD)."""
|
|
273
|
+
slots = orita.slots(event_type_id="evt_abc123", date=date_str)
|
|
274
|
+
return "\n".join([f"- {s['label']}" for s in slots])
|
|
275
|
+
|
|
276
|
+
@tool
|
|
277
|
+
def book_appointment(date_str: str, time_str: str, name: str, lastname: str, email: str) -> str:
|
|
278
|
+
"""Book an appointment for a client."""
|
|
279
|
+
booking = orita.book(
|
|
280
|
+
event_type_id="evt_abc123", date=date_str, time=time_str,
|
|
281
|
+
client_name=name, client_lastname=lastname, client_email=email,
|
|
282
|
+
)
|
|
283
|
+
return f"Confirmed! Booking ID: {booking['id']}"
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
→ Full example: [`examples/langchain_tool.py`](examples/langchain_tool.py)
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
### CrewAI
|
|
291
|
+
|
|
292
|
+
```python
|
|
293
|
+
from crewai.tools import tool
|
|
294
|
+
from orita import OritaClient
|
|
295
|
+
|
|
296
|
+
orita = OritaClient(api_key="orita_your_key")
|
|
297
|
+
|
|
298
|
+
@tool("Get Available Slots")
|
|
299
|
+
def get_slots(event_type_id: str, date: str) -> str:
|
|
300
|
+
"""Get available appointment slots. Input: event_type_id and date (YYYY-MM-DD)."""
|
|
301
|
+
slots = orita.slots(event_type_id=event_type_id, date=date)
|
|
302
|
+
return str([{"label": s["label"], "value": s["value"]} for s in slots])
|
|
303
|
+
|
|
304
|
+
@tool("Book Appointment")
|
|
305
|
+
def book(event_type_id: str, date: str, time: str,
|
|
306
|
+
client_name: str, client_lastname: str, client_email: str) -> str:
|
|
307
|
+
"""Book an appointment for a client."""
|
|
308
|
+
booking = orita.book(
|
|
309
|
+
event_type_id=event_type_id, date=date, time=time,
|
|
310
|
+
client_name=client_name, client_lastname=client_lastname, client_email=client_email,
|
|
311
|
+
)
|
|
312
|
+
return f"Booking {booking['id']} confirmed for {date} at {time}"
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## Full Example: Book from Available Slots
|
|
318
|
+
|
|
319
|
+
```python
|
|
320
|
+
from orita import OritaClient
|
|
321
|
+
from datetime import date, timedelta
|
|
322
|
+
|
|
323
|
+
client = OritaClient(api_key="orita_your_key_here")
|
|
324
|
+
|
|
325
|
+
# 1. Get event types
|
|
326
|
+
event_types = client.event_types()
|
|
327
|
+
event_type_id = event_types[0]["id"]
|
|
328
|
+
|
|
329
|
+
# 2. Get tomorrow's slots
|
|
330
|
+
tomorrow = (date.today() + timedelta(days=1)).isoformat()
|
|
331
|
+
slots = client.slots(event_type_id=event_type_id, date=tomorrow)
|
|
332
|
+
|
|
333
|
+
# 3. Book the first available
|
|
334
|
+
if slots:
|
|
335
|
+
booking = client.book(
|
|
336
|
+
event_type_id=event_type_id,
|
|
337
|
+
date=tomorrow,
|
|
338
|
+
time=slots[0]["value"],
|
|
339
|
+
client_name="Juan",
|
|
340
|
+
client_lastname="García",
|
|
341
|
+
client_email="juan@example.com",
|
|
342
|
+
)
|
|
343
|
+
print(f"✅ Booked: {booking['id']}")
|
|
344
|
+
else:
|
|
345
|
+
print("No availability tomorrow")
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## Requirements
|
|
351
|
+
|
|
352
|
+
- Python 3.8+
|
|
353
|
+
- `requests >= 2.28`
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## Links
|
|
358
|
+
|
|
359
|
+
- 🌐 **Website:** [orita.online](https://orita.online)
|
|
360
|
+
- 📚 **Developer docs:** [orita.online/developers](https://orita.online/developers)
|
|
361
|
+
- 🐛 **Issues:** [github.com/Alkilo-do/orita-python/issues](https://github.com/Alkilo-do/orita-python/issues)
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## License
|
|
366
|
+
|
|
367
|
+
MIT © [Alkilo-do](https://github.com/Alkilo-do)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from .exceptions import OritaError, OritaAuthError, OritaNotFoundError, OritaSlotUnavailableError
|
|
4
|
+
|
|
5
|
+
class OritaClient:
|
|
6
|
+
BASE_URL = "https://orita.online/api/v1"
|
|
7
|
+
|
|
8
|
+
def __init__(self, api_key: str, base_url: Optional[str] = None):
|
|
9
|
+
if not api_key.startswith("orita_"):
|
|
10
|
+
raise OritaAuthError("API key must start with 'orita_'")
|
|
11
|
+
self.api_key = api_key
|
|
12
|
+
self.base_url = base_url or self.BASE_URL
|
|
13
|
+
self.session = requests.Session()
|
|
14
|
+
self.session.headers.update({
|
|
15
|
+
"Authorization": f"Bearer {api_key}",
|
|
16
|
+
"Content-Type": "application/json",
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
def _request(self, method: str, path: str, **kwargs):
|
|
20
|
+
url = f"{self.base_url}{path}"
|
|
21
|
+
response = self.session.request(method, url, **kwargs)
|
|
22
|
+
if response.status_code == 401:
|
|
23
|
+
raise OritaAuthError("Invalid API key")
|
|
24
|
+
if response.status_code == 404:
|
|
25
|
+
raise OritaNotFoundError(response.json().get("error", "Not found"))
|
|
26
|
+
if response.status_code == 409:
|
|
27
|
+
raise OritaSlotUnavailableError(response.json().get("error", "Slot unavailable"))
|
|
28
|
+
if not response.ok:
|
|
29
|
+
raise OritaError(response.json().get("error", "API error"))
|
|
30
|
+
return response.json()
|
|
31
|
+
|
|
32
|
+
def event_types(self) -> list:
|
|
33
|
+
"""List all active event types for this account."""
|
|
34
|
+
return self._request("GET", "/event-types")["data"]
|
|
35
|
+
|
|
36
|
+
def slots(self, event_type_id: str, date: str) -> list:
|
|
37
|
+
"""Get available time slots for an event type on a given date (YYYY-MM-DD)."""
|
|
38
|
+
data = self._request("GET", "/slots", params={
|
|
39
|
+
"eventTypeId": event_type_id,
|
|
40
|
+
"date": date,
|
|
41
|
+
})
|
|
42
|
+
return data["slots"]
|
|
43
|
+
|
|
44
|
+
def book(
|
|
45
|
+
self,
|
|
46
|
+
event_type_id: str,
|
|
47
|
+
date: str,
|
|
48
|
+
time: str,
|
|
49
|
+
client_name: str,
|
|
50
|
+
client_lastname: str,
|
|
51
|
+
client_email: str,
|
|
52
|
+
client_timezone: Optional[str] = None,
|
|
53
|
+
notes: Optional[str] = None,
|
|
54
|
+
) -> dict:
|
|
55
|
+
"""Book an appointment. Returns the booking object."""
|
|
56
|
+
payload = {
|
|
57
|
+
"eventTypeId": event_type_id,
|
|
58
|
+
"date": date,
|
|
59
|
+
"time": time,
|
|
60
|
+
"clientName": client_name,
|
|
61
|
+
"clientLastname": client_lastname,
|
|
62
|
+
"clientEmail": client_email,
|
|
63
|
+
}
|
|
64
|
+
if client_timezone:
|
|
65
|
+
payload["clientTimezone"] = client_timezone
|
|
66
|
+
if notes:
|
|
67
|
+
payload["notes"] = notes
|
|
68
|
+
return self._request("POST", "/bookings", json=payload)["data"]
|
|
69
|
+
|
|
70
|
+
def bookings(self, page: int = 1, limit: int = 20, status: Optional[str] = None) -> list:
|
|
71
|
+
"""List bookings with optional status filter."""
|
|
72
|
+
params = {"page": page, "limit": limit}
|
|
73
|
+
if status:
|
|
74
|
+
params["status"] = status
|
|
75
|
+
return self._request("GET", "/bookings", params=params)["data"]
|
|
76
|
+
|
|
77
|
+
def get_booking(self, booking_id: str) -> dict:
|
|
78
|
+
"""Get a booking by ID."""
|
|
79
|
+
return self._request("GET", f"/bookings/{booking_id}")["data"]
|
|
80
|
+
|
|
81
|
+
def cancel(self, booking_id: str) -> dict:
|
|
82
|
+
"""Cancel a booking by ID."""
|
|
83
|
+
return self._request("POST", f"/bookings/{booking_id}/cancel")["data"]
|
|
84
|
+
|
|
85
|
+
def profile(self) -> dict:
|
|
86
|
+
"""Get your agent capability manifest (Capability Manifest)."""
|
|
87
|
+
return self._request("GET", "/profile")["data"]
|
|
88
|
+
|
|
89
|
+
def update_profile(self, **fields) -> dict:
|
|
90
|
+
"""Update your agent profile fields."""
|
|
91
|
+
return self._request("PUT", "/profile", json=fields)["data"]
|
|
92
|
+
|
|
93
|
+
def get_profile(self, username: str) -> dict:
|
|
94
|
+
"""Get the public Capability Manifest for any professional by username (no auth needed)."""
|
|
95
|
+
import requests as req
|
|
96
|
+
response = req.get(f"{self.base_url}/profile", params={"username": username})
|
|
97
|
+
if response.status_code == 404:
|
|
98
|
+
raise OritaNotFoundError(f"Professional '{username}' not found")
|
|
99
|
+
return response.json()["data"]
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: orita-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: The scheduling infrastructure for AI agents — Python SDK
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://orita.online/developers
|
|
7
|
+
Project-URL: Repository, https://github.com/Alkilo-do/orita-python
|
|
8
|
+
Project-URL: Documentation, https://orita.online/developers
|
|
9
|
+
Keywords: scheduling,booking,ai-agents,api
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Requires-Python: >=3.8
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: requests>=2.28
|
|
16
|
+
|
|
17
|
+
# orita-python
|
|
18
|
+
|
|
19
|
+
[](https://pypi.org/project/orita-sdk/)
|
|
20
|
+
[](https://www.python.org/downloads/)
|
|
21
|
+
[](https://opensource.org/licenses/MIT)
|
|
22
|
+
|
|
23
|
+
**The scheduling infrastructure for AI agents — Python SDK**
|
|
24
|
+
|
|
25
|
+
[Orita](https://orita.online) is the scheduling layer purpose-built for AI agents. Connect your LLM to real calendar availability in minutes.
|
|
26
|
+
|
|
27
|
+
→ **Docs & API keys:** [orita.online/developers](https://orita.online/developers)
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install orita-sdk
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Quickstart
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from orita import OritaClient
|
|
41
|
+
|
|
42
|
+
client = OritaClient(api_key="orita_your_key_here")
|
|
43
|
+
slots = client.slots(event_type_id="evt_abc123", date="2026-08-01")
|
|
44
|
+
booking = client.book(
|
|
45
|
+
event_type_id="evt_abc123", date="2026-08-01", time=slots[0]["value"],
|
|
46
|
+
client_name="Ana", client_lastname="López", client_email="ana@example.com",
|
|
47
|
+
)
|
|
48
|
+
print(booking["id"]) # book_xyz789
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Authentication
|
|
54
|
+
|
|
55
|
+
All requests require an API key obtained from [orita.online/developers](https://orita.online/developers).
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from orita import OritaClient
|
|
59
|
+
|
|
60
|
+
client = OritaClient(api_key="orita_your_key_here")
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
API keys must start with `orita_`. Pass a custom `base_url` to point to a self-hosted or staging instance.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## API Reference
|
|
68
|
+
|
|
69
|
+
### `client.event_types() → list`
|
|
70
|
+
|
|
71
|
+
List all active event types for your account.
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
event_types = client.event_types()
|
|
75
|
+
# [{"id": "evt_abc123", "title": "Initial Consultation", "duration": 30, ...}]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
### `client.slots(event_type_id, date) → list`
|
|
81
|
+
|
|
82
|
+
Get available time slots for a given event type on a specific date.
|
|
83
|
+
|
|
84
|
+
| Parameter | Type | Description |
|
|
85
|
+
|-----------|------|-------------|
|
|
86
|
+
| `event_type_id` | `str` | Event type ID from `event_types()` |
|
|
87
|
+
| `date` | `str` | Date in `YYYY-MM-DD` format |
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
slots = client.slots(event_type_id="evt_abc123", date="2026-08-01")
|
|
91
|
+
# [{"label": "09:00 AM", "value": "09:00"}, {"label": "09:30 AM", "value": "09:30"}, ...]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
### `client.book(...) → dict`
|
|
97
|
+
|
|
98
|
+
Book an appointment. Returns the created booking object.
|
|
99
|
+
|
|
100
|
+
| Parameter | Type | Required | Description |
|
|
101
|
+
|-----------|------|----------|-------------|
|
|
102
|
+
| `event_type_id` | `str` | ✅ | Event type ID |
|
|
103
|
+
| `date` | `str` | ✅ | Date in `YYYY-MM-DD` |
|
|
104
|
+
| `time` | `str` | ✅ | Time in `HH:MM` (from `slots()`) |
|
|
105
|
+
| `client_name` | `str` | ✅ | Client first name |
|
|
106
|
+
| `client_lastname` | `str` | ✅ | Client last name |
|
|
107
|
+
| `client_email` | `str` | ✅ | Client email |
|
|
108
|
+
| `client_timezone` | `str` | ❌ | IANA timezone (e.g. `America/New_York`) |
|
|
109
|
+
| `notes` | `str` | ❌ | Additional notes for the professional |
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
booking = client.book(
|
|
113
|
+
event_type_id="evt_abc123",
|
|
114
|
+
date="2026-08-01",
|
|
115
|
+
time="10:00",
|
|
116
|
+
client_name="Carlos",
|
|
117
|
+
client_lastname="García",
|
|
118
|
+
client_email="carlos@example.com",
|
|
119
|
+
client_timezone="America/Bogota",
|
|
120
|
+
notes="First appointment — prefers video call",
|
|
121
|
+
)
|
|
122
|
+
# {"id": "book_xyz789", "status": "confirmed", "date": "2026-08-01", ...}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### `client.bookings(page, limit, status) → list`
|
|
128
|
+
|
|
129
|
+
List bookings with optional filtering.
|
|
130
|
+
|
|
131
|
+
| Parameter | Type | Default | Description |
|
|
132
|
+
|-----------|------|---------|-------------|
|
|
133
|
+
| `page` | `int` | `1` | Page number |
|
|
134
|
+
| `limit` | `int` | `20` | Results per page |
|
|
135
|
+
| `status` | `str` | `None` | Filter: `confirmed`, `cancelled`, `completed` |
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
confirmed = client.bookings(status="confirmed")
|
|
139
|
+
all_bookings = client.bookings(page=2, limit=50)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
### `client.get_booking(booking_id) → dict`
|
|
145
|
+
|
|
146
|
+
Retrieve a single booking by ID.
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
booking = client.get_booking("book_xyz789")
|
|
150
|
+
print(booking["status"]) # "confirmed"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
### `client.cancel(booking_id) → dict`
|
|
156
|
+
|
|
157
|
+
Cancel a booking by ID.
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
result = client.cancel("book_xyz789")
|
|
161
|
+
print(result["status"]) # "cancelled"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
### `client.profile() → dict`
|
|
167
|
+
|
|
168
|
+
Get your own **Capability Manifest** — the machine-readable description of your scheduling configuration that AI agents can read to understand how to book you.
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
manifest = client.profile()
|
|
172
|
+
print(manifest["username"])
|
|
173
|
+
print(manifest["eventTypes"])
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
### `client.update_profile(**fields) → dict`
|
|
179
|
+
|
|
180
|
+
Update your profile fields.
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
updated = client.update_profile(bio="AI-native therapist", timezone="Europe/Madrid")
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
### `client.get_profile(username) → dict`
|
|
189
|
+
|
|
190
|
+
Fetch the **public Capability Manifest** for any professional by username. No API key required internally — useful for cross-professional lookups.
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
manifest = client.get_profile("dra-martinez")
|
|
194
|
+
# {"username": "dra-martinez", "eventTypes": [...], ...}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Error Handling
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
from orita import OritaClient
|
|
203
|
+
from orita import OritaAuthError, OritaNotFoundError, OritaSlotUnavailableError, OritaError
|
|
204
|
+
|
|
205
|
+
client = OritaClient(api_key="orita_your_key")
|
|
206
|
+
|
|
207
|
+
try:
|
|
208
|
+
booking = client.book(
|
|
209
|
+
event_type_id="evt_abc123",
|
|
210
|
+
date="2026-08-01",
|
|
211
|
+
time="10:00",
|
|
212
|
+
client_name="Ana",
|
|
213
|
+
client_lastname="López",
|
|
214
|
+
client_email="ana@example.com",
|
|
215
|
+
)
|
|
216
|
+
except OritaAuthError:
|
|
217
|
+
print("Invalid API key")
|
|
218
|
+
except OritaSlotUnavailableError:
|
|
219
|
+
print("That slot was just taken — fetch slots again")
|
|
220
|
+
except OritaNotFoundError:
|
|
221
|
+
print("Event type not found")
|
|
222
|
+
except OritaError as e:
|
|
223
|
+
print(f"API error: {e}")
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
| Exception | HTTP Status | When |
|
|
227
|
+
|-----------|-------------|------|
|
|
228
|
+
| `OritaAuthError` | 401 | Invalid or missing API key |
|
|
229
|
+
| `OritaNotFoundError` | 404 | Resource not found |
|
|
230
|
+
| `OritaSlotUnavailableError` | 409 | Slot already taken |
|
|
231
|
+
| `OritaError` | Other 4xx/5xx | Generic API error |
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Framework Integrations
|
|
236
|
+
|
|
237
|
+
### OpenAI Agents SDK
|
|
238
|
+
|
|
239
|
+
Give your OpenAI agent the ability to book appointments in real time:
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
from openai import OpenAI
|
|
243
|
+
from orita import OritaClient
|
|
244
|
+
import json
|
|
245
|
+
|
|
246
|
+
orita = OritaClient(api_key="orita_your_key")
|
|
247
|
+
client = OpenAI()
|
|
248
|
+
|
|
249
|
+
tools = [
|
|
250
|
+
{
|
|
251
|
+
"type": "function",
|
|
252
|
+
"function": {
|
|
253
|
+
"name": "get_available_slots",
|
|
254
|
+
"description": "Get available appointment slots for a given date",
|
|
255
|
+
"parameters": {
|
|
256
|
+
"type": "object",
|
|
257
|
+
"properties": {
|
|
258
|
+
"event_type_id": {"type": "string"},
|
|
259
|
+
"date": {"type": "string", "description": "YYYY-MM-DD"},
|
|
260
|
+
},
|
|
261
|
+
"required": ["event_type_id", "date"],
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
]
|
|
266
|
+
|
|
267
|
+
def handle_tool_call(name, args):
|
|
268
|
+
if name == "get_available_slots":
|
|
269
|
+
return json.dumps(orita.slots(args["event_type_id"], args["date"]))
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
→ Full example: [`examples/openai_agent.py`](examples/openai_agent.py)
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
### LangChain
|
|
277
|
+
|
|
278
|
+
Wrap Orita methods as LangChain `@tool` functions:
|
|
279
|
+
|
|
280
|
+
```python
|
|
281
|
+
from langchain.tools import tool
|
|
282
|
+
from orita import OritaClient
|
|
283
|
+
|
|
284
|
+
orita = OritaClient(api_key="orita_your_key")
|
|
285
|
+
|
|
286
|
+
@tool
|
|
287
|
+
def get_available_slots(date_str: str) -> str:
|
|
288
|
+
"""Get available appointment slots for a date (YYYY-MM-DD)."""
|
|
289
|
+
slots = orita.slots(event_type_id="evt_abc123", date=date_str)
|
|
290
|
+
return "\n".join([f"- {s['label']}" for s in slots])
|
|
291
|
+
|
|
292
|
+
@tool
|
|
293
|
+
def book_appointment(date_str: str, time_str: str, name: str, lastname: str, email: str) -> str:
|
|
294
|
+
"""Book an appointment for a client."""
|
|
295
|
+
booking = orita.book(
|
|
296
|
+
event_type_id="evt_abc123", date=date_str, time=time_str,
|
|
297
|
+
client_name=name, client_lastname=lastname, client_email=email,
|
|
298
|
+
)
|
|
299
|
+
return f"Confirmed! Booking ID: {booking['id']}"
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
→ Full example: [`examples/langchain_tool.py`](examples/langchain_tool.py)
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
### CrewAI
|
|
307
|
+
|
|
308
|
+
```python
|
|
309
|
+
from crewai.tools import tool
|
|
310
|
+
from orita import OritaClient
|
|
311
|
+
|
|
312
|
+
orita = OritaClient(api_key="orita_your_key")
|
|
313
|
+
|
|
314
|
+
@tool("Get Available Slots")
|
|
315
|
+
def get_slots(event_type_id: str, date: str) -> str:
|
|
316
|
+
"""Get available appointment slots. Input: event_type_id and date (YYYY-MM-DD)."""
|
|
317
|
+
slots = orita.slots(event_type_id=event_type_id, date=date)
|
|
318
|
+
return str([{"label": s["label"], "value": s["value"]} for s in slots])
|
|
319
|
+
|
|
320
|
+
@tool("Book Appointment")
|
|
321
|
+
def book(event_type_id: str, date: str, time: str,
|
|
322
|
+
client_name: str, client_lastname: str, client_email: str) -> str:
|
|
323
|
+
"""Book an appointment for a client."""
|
|
324
|
+
booking = orita.book(
|
|
325
|
+
event_type_id=event_type_id, date=date, time=time,
|
|
326
|
+
client_name=client_name, client_lastname=client_lastname, client_email=client_email,
|
|
327
|
+
)
|
|
328
|
+
return f"Booking {booking['id']} confirmed for {date} at {time}"
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## Full Example: Book from Available Slots
|
|
334
|
+
|
|
335
|
+
```python
|
|
336
|
+
from orita import OritaClient
|
|
337
|
+
from datetime import date, timedelta
|
|
338
|
+
|
|
339
|
+
client = OritaClient(api_key="orita_your_key_here")
|
|
340
|
+
|
|
341
|
+
# 1. Get event types
|
|
342
|
+
event_types = client.event_types()
|
|
343
|
+
event_type_id = event_types[0]["id"]
|
|
344
|
+
|
|
345
|
+
# 2. Get tomorrow's slots
|
|
346
|
+
tomorrow = (date.today() + timedelta(days=1)).isoformat()
|
|
347
|
+
slots = client.slots(event_type_id=event_type_id, date=tomorrow)
|
|
348
|
+
|
|
349
|
+
# 3. Book the first available
|
|
350
|
+
if slots:
|
|
351
|
+
booking = client.book(
|
|
352
|
+
event_type_id=event_type_id,
|
|
353
|
+
date=tomorrow,
|
|
354
|
+
time=slots[0]["value"],
|
|
355
|
+
client_name="Juan",
|
|
356
|
+
client_lastname="García",
|
|
357
|
+
client_email="juan@example.com",
|
|
358
|
+
)
|
|
359
|
+
print(f"✅ Booked: {booking['id']}")
|
|
360
|
+
else:
|
|
361
|
+
print("No availability tomorrow")
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## Requirements
|
|
367
|
+
|
|
368
|
+
- Python 3.8+
|
|
369
|
+
- `requests >= 2.28`
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## Links
|
|
374
|
+
|
|
375
|
+
- 🌐 **Website:** [orita.online](https://orita.online)
|
|
376
|
+
- 📚 **Developer docs:** [orita.online/developers](https://orita.online/developers)
|
|
377
|
+
- 🐛 **Issues:** [github.com/Alkilo-do/orita-python/issues](https://github.com/Alkilo-do/orita-python/issues)
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## License
|
|
382
|
+
|
|
383
|
+
MIT © [Alkilo-do](https://github.com/Alkilo-do)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
orita/__init__.py
|
|
4
|
+
orita/client.py
|
|
5
|
+
orita/exceptions.py
|
|
6
|
+
orita_sdk.egg-info/PKG-INFO
|
|
7
|
+
orita_sdk.egg-info/SOURCES.txt
|
|
8
|
+
orita_sdk.egg-info/dependency_links.txt
|
|
9
|
+
orita_sdk.egg-info/requires.txt
|
|
10
|
+
orita_sdk.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
requests>=2.28
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
orita
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "orita-sdk"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "The scheduling infrastructure for AI agents — Python SDK"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
keywords = ["scheduling", "booking", "ai-agents", "api"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Operating System :: OS Independent",
|
|
17
|
+
]
|
|
18
|
+
dependencies = ["requests>=2.28"]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://orita.online/developers"
|
|
22
|
+
Repository = "https://github.com/Alkilo-do/orita-python"
|
|
23
|
+
Documentation = "https://orita.online/developers"
|