- Getting Started
- Authentication
- Making API Requests
- Common Operations
- Error Handling
- Rate Limiting
- Best Practices
Getting Started
1. Obtaining API Access
To access the SELMA API, your SELMA administrator will create an API user account for you. They will provide you with:
- Your API username in the format: selma_{environment}_{random_string} (e.g., selma_prod_abc123def4)
2. Generating Your Initial API Secret
After your administrator creates your account, you must generate your API secret:
- Navigate to /api/docs in your SELMA instance
- Login with your SELMA credentials
- Find your API username in the API Users section
- Check the safety checkbox to enable the Regenerate button
- Click Regenerate
- Confirm the action when prompted
- IMPORTANT: Copy the generated secret immediately – it will only be shown once!
- Click “I have saved the key” to close the dialog
- Store the secret securely in your application’s configuration
3. Regenerating Your API Secret
If you need to regenerate your secret (for security rotation or if compromised):
- Follow steps 1-9 above
- Warning: Regenerating immediately invalidates your previous secret
- Update all applications using the old secret before regenerating to avoid downtime
4. API Documentation Access
The Swagger UI at /api/docs provides:
- Interactive API documentation
- Request/response schema examples
- API specification downloads (JSON, JSON-LD formats)
Authentication
Obtaining a JWT Token
To authenticate with the API, exchange your credentials for a JWT token:
Request:
POST /api/auth
Content-Type: application/json
{
“username”: “selma_prod_abc123def4”,
“secret”: “your-secret”
}
Response:
{
“token”: “eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9…”
}
Using the JWT Token
Include the token in the Authorization header for all subsequent requests:
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9…
Token Expiration: JWT tokens expire after a set period. When expired, simply request a new token using the same authentication endpoint.
Making API Requests
Request Format
All API endpoints follow RESTful conventions:
- GET – Retrieve resources
- POST – Create new resources
- PATCH – Update existing resources
- PUT – Replace existing resources
- DELETE – Remove resources
Response Format
By default, responses are in JSON-LD format. You can request different formats:
- /api/students – JSON-LD (default)
- /api/students.json – JSON
- /api/students.jsonld – JSON-LD
Pagination
Collection endpoints are paginated by default (30 items per page):
GET /api/students?page=2
Pagination Response:
{
“@context”: “/api/contexts/Student”,
“@id”: “/api/students”,
“@type”: “hydra:Collection”,
“hydra:totalItems”: 150,
“hydra:member”: […],
“hydra:view”: {
“@id”: “/api/students?page=2”,
“@type”: “hydra:PartialCollectionView”,
“hydra:first”: “/api/students?page=1”,
“hydra:last”: “/api/students?page=5”,
“hydra:previous”: “/api/students?page=1”,
“hydra:next”: “/api/students?page=3”
}
}
Filtering
Most collections support filtering:
# Search by partial name
GET /api/students?name=john
# Search by exact code
GET /api/students?code=STU001
# Filter by date range
GET /api/intakes?start_date[after]=2025-01-01&start_date[before]=2025-12-31
# Multiple filters
GET /api/organisations?name=university&city=auckland
Common Operations
Creating a Student
POST /api/students
Content-Type: application/json
Authorization: Bearer {token}
{
“code”: “STU12345”,
“first_name”: “John”,
“last_name”: “Smith”,
“email”: “john.smith@example.com”,
“date_of_birth”: “1995-06-15”,
“gender”: “/api/genders/1”,
“student_status”: “/api/student_statuses/1”
}
Creating an Enrolment
POST /api/enrolments
Content-Type: application/json
Authorization: Bearer {token}
{
“student”: “/api/students/123”,
“intake”: “/api/intakes/45”,
“start_date”: “2025-02-01T00:00:00+13:00”,
“end_date”: “2025-11-30T23:59:59+13:00”,
“enrolment_status”: “/api/enrolment_statuses/1”
}
Retrieving Resources by Alternative ID
Some resources support alternative identifiers:
# Get student by other_id_1
GET /api/students/other_id_1/{other_id_1}
# Get organisation by tax number
GET /api/organisations?tax_number=123456789
Error Handling
Error Response Format
Errors follow RFC 7807 Problem Details standard:
{
“@context”: “/api/contexts/Error”,
“@type”: “hydra:Error”,
“hydra:title”: “An error occurred”,
“hydra:description”: “student is already enrolled in this intake”,
“trace”: […]
}
Common Error Codes
- 400 Bad Request – Invalid input data
- 401 Unauthorized – Missing or invalid authentication
- 403 Forbidden – Insufficient permissions or accessing another account’s data
- 404 Not Found – Resource doesn’t exist
- 422 Unprocessable Entity – Validation errors
- 429 Too Many Requests – Rate limit exceeded
Rate Limiting
The API implements two-tier rate limiting:
- Per Minute: 100 requests per minute (sliding window)
- Per Day: 20,000 requests per day (fixed window)
When you exceed a rate limit, you’ll receive a 429 Too Many Requests response. The response headers will indicate when you can retry:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640995200
Best Practices
1. Secure Your Credentials
- Never commit API secrets to version control
- Use environment variables or secure key management systems
- Rotate secrets regularly (recommended: every 90 days)
2. Handle Errors Gracefully
- Implement retry logic with exponential backoff for 5xx errors
- Log error responses for debugging
- Don’t retry 4xx errors without fixing the request
3. Optimize API Usage
- Use filtering to reduce payload sizes
- Cache responses where appropriate
- Batch operations when possible
- Request only the fields you need
4. Monitor Your Usage
- Track your API usage against rate limits
- Set up alerts for approaching limits
- Monitor the “Last API Access” information in the UI
5. Date/Time Handling
- Always include timezone information in date inputs
- Expect UTC times in responses
- Use ISO 8601 format (e.g., 2025-06-06T12:00:00+13:00)
6. IRI References
When referencing related resources, use their IRI (Internationalized Resource Identifier):
- Correct: “student”: “/api/students/123”
- Incorrect: “student”: 123 or “student_id”: 123