Documentation Index
Fetch the complete documentation index at: https://mintlify.com/sakaiproject/sakai/llms.txt
Use this file to discover all available pages before exploring further.
This page provides complete, working examples of common REST API operations in Sakai.
Prerequisites
All examples assume:
- You have a running Sakai instance at
https://sakai.example.edu
- You have valid user credentials
- HTTPS is enabled (required for production)
Basic Authentication Example
Login and Get User Announcements
# 1. Login and save session cookie
curl -X POST https://sakai.example.edu/api/login \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=instructor@university.edu&password=your-password" \
-c cookies.txt
# 2. Get announcements using the session cookie
curl https://sakai.example.edu/api/users/me/announcements \
-b cookies.txt
# 3. Logout when done
curl -X POST https://sakai.example.edu/api/logout \
-b cookies.txt
Dashboard Data Example
Get a complete dashboard view with announcements, calendar events, and grades:
# Login first
curl -X POST https://sakai.example.edu/api/login \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=student@university.edu&password=your-password" \
-c cookies.txt
# Get dashboard data
curl https://sakai.example.edu/api/users/me/dashboard \
-b cookies.txt \
| jq '.'
Calendar Events Example
Retrieve and display upcoming calendar events:
async function getUpcomingEvents() {
// Assuming already logged in
const response = await fetch(
'https://sakai.example.edu/api/users/me/calendar',
{ credentials: 'include' }
);
const data = await response.json();
const events = data.events;
// Filter events happening in the next 7 days
const now = new Date();
const weekFromNow = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);
const upcomingEvents = events.filter(event => {
const eventDate = new Date(event.startTime);
return eventDate >= now && eventDate <= weekFromNow;
});
// Display events
upcomingEvents.forEach(event => {
console.log(`${event.title} - ${new Date(event.startTime).toLocaleString()}`);
});
return upcomingEvents;
}
Site Grades Example
Get gradebook information for a specific site:
def get_site_grades(session, site_id):
"""Get grades for a specific site"""
response = session.get(
f'https://sakai.example.edu/api/sites/{site_id}/grades'
)
if response.ok:
grades = response.json()
# Display grade items
for item in grades.get('items', []):
print(f"{item['name']}: {item['score']}/{item['points']}")
# Display course grade
course_grade = grades.get('courseGrade')
if course_grade:
print(f"\nCourse Grade: {course_grade['displayGrade']}")
return grades
else:
print(f"Error: {response.status_code}")
return None
# Usage
session = create_authenticated_session()
grades = get_site_grades(session, 'chemistry-101-site-id')
Error Handling Example
Robust error handling for REST API calls:
class SakaiAPIClient {
constructor(baseUrl) {
this.baseUrl = baseUrl;
this.isAuthenticated = false;
}
async login(username, password) {
try {
const response = await fetch(`${this.baseUrl}/api/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({ username, password }),
credentials: 'include'
});
if (!response.ok) {
if (response.status === 401) {
throw new Error('Invalid credentials');
}
throw new Error(`Login failed: ${response.statusText}`);
}
this.isAuthenticated = true;
const data = await response.json();
return data;
} catch (error) {
console.error('Login error:', error);
this.isAuthenticated = false;
throw error;
}
}
async makeRequest(endpoint, options = {}) {
if (!this.isAuthenticated) {
throw new Error('Not authenticated. Call login() first.');
}
try {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
...options,
credentials: 'include'
});
// Handle session timeout
if (response.status === 401) {
this.isAuthenticated = false;
throw new Error('Session expired. Please login again.');
}
// Handle permission errors
if (response.status === 403) {
throw new Error('Permission denied');
}
// Handle not found
if (response.status === 404) {
throw new Error('Resource not found');
}
// Handle server errors
if (response.status >= 500) {
throw new Error('Server error. Please try again later.');
}
if (!response.ok) {
throw new Error(`Request failed: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error(`API request error (${endpoint}):`, error);
throw error;
}
}
async getAnnouncements() {
return this.makeRequest('/api/users/me/announcements');
}
async getCalendar() {
return this.makeRequest('/api/users/me/calendar');
}
async logout() {
try {
await this.makeRequest('/api/logout', { method: 'POST' });
this.isAuthenticated = false;
} catch (error) {
console.error('Logout error:', error);
}
}
}
// Usage
const client = new SakaiAPIClient('https://sakai.example.edu');
async function example() {
try {
await client.login('user@example.com', 'password');
const announcements = await client.getAnnouncements();
console.log(announcements);
} catch (error) {
console.error('Error:', error.message);
} finally {
await client.logout();
}
}
Reusable Python Client
A complete Python client with session management:
import requests
from typing import Optional, Dict, Any
class SakaiClient:
"""Reusable Sakai REST API client"""
def __init__(self, base_url: str):
self.base_url = base_url.rstrip('/')
self.session = requests.Session()
self.is_authenticated = False
def login(self, username: str, password: str) -> Dict[str, Any]:
"""Login and establish a session"""
response = self.session.post(
f'{self.base_url}/api/login',
data={'username': username, 'password': password}
)
if response.ok:
self.is_authenticated = True
return response.json()
else:
raise Exception(f"Login failed: {response.status_code}")
def _request(self, method: str, endpoint: str, **kwargs) -> requests.Response:
"""Make an authenticated request"""
if not self.is_authenticated:
raise Exception("Not authenticated. Call login() first.")
url = f'{self.base_url}{endpoint}'
response = self.session.request(method, url, **kwargs)
# Handle session timeout
if response.status_code == 401:
self.is_authenticated = False
raise Exception("Session expired")
response.raise_for_status()
return response
def get_announcements(self) -> Dict[str, Any]:
"""Get user announcements"""
response = self._request('GET', '/api/users/me/announcements')
return response.json()
def get_calendar(self) -> Dict[str, Any]:
"""Get user calendar events"""
response = self._request('GET', '/api/users/me/calendar')
return response.json()
def get_site_grades(self, site_id: str) -> Dict[str, Any]:
"""Get grades for a site"""
response = self._request('GET', f'/api/sites/{site_id}/grades')
return response.json()
def logout(self):
"""Logout and end session"""
if self.is_authenticated:
self.session.post(f'{self.base_url}/api/logout')
self.is_authenticated = False
# Usage
if __name__ == '__main__':
client = SakaiClient('https://sakai.example.edu')
try:
# Login
user_info = client.login('user@example.com', 'password')
print(f"Logged in as: {user_info['displayName']}")
# Get announcements
announcements = client.get_announcements()
print(f"Found {len(announcements['announcements'])} announcements")
# Get calendar
calendar = client.get_calendar()
print(f"Found {len(calendar['events'])} events")
except Exception as e:
print(f"Error: {e}")
finally:
client.logout()
Best Practices
Session Management
- Reuse sessions for multiple requests
- Handle session timeouts gracefully
- Always logout when done
Error Handling
- Check HTTP status codes
- Handle 401 (unauthorized) and 403 (forbidden) errors
- Implement retry logic for temporary failures
Security
- Always use HTTPS in production
- Never log or expose credentials
- Store credentials securely
Performance
- Reuse HTTP connections/sessions
- Cache responses when appropriate
- Use pagination for large datasets
Next Steps
API Endpoints
Explore all available REST endpoints
Authentication
Learn more about authentication methods