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.
@@ -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
+ [![PyPI version](https://img.shields.io/pypi/v/orita-sdk.svg)](https://pypi.org/project/orita-sdk/)
20
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
21
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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
+ [![PyPI version](https://img.shields.io/pypi/v/orita-sdk.svg)](https://pypi.org/project/orita-sdk/)
4
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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,5 @@
1
+ from .client import OritaClient
2
+ from .exceptions import OritaError, OritaAuthError, OritaNotFoundError, OritaSlotUnavailableError
3
+
4
+ __version__ = "0.1.0"
5
+ __all__ = ["OritaClient", "OritaError", "OritaAuthError", "OritaNotFoundError", "OritaSlotUnavailableError"]
@@ -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,11 @@
1
+ class OritaError(Exception):
2
+ pass
3
+
4
+ class OritaAuthError(OritaError):
5
+ pass
6
+
7
+ class OritaNotFoundError(OritaError):
8
+ pass
9
+
10
+ class OritaSlotUnavailableError(OritaError):
11
+ pass
@@ -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
+ [![PyPI version](https://img.shields.io/pypi/v/orita-sdk.svg)](https://pypi.org/project/orita-sdk/)
20
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
21
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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
+ 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"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+