Perfxcel API Reference
Base URL: https://api.perfxcel.com/api/v1
All admin endpoints require Authorization: Bearer <supabase-jwt>. Public endpoints have no auth requirement but are rate-limited.
Courses
GET /courses
Returns all published, active courses. Supports filtering via query params.
Query params:
| Param | Type | Description |
|---|---|---|
category |
string | Filter by category taxonomy slug |
city |
string | Filter by city taxonomy slug |
association |
string | Filter by association taxonomy slug |
delivery_mode |
string | Filter by delivery mode slug |
search |
string | Full-text search on title / description |
Response:
{
"status": "success",
"results": 12,
"data": [
{
"id": "uuid",
"title": "ISO 9001 Foundation",
"slug": "iso-9001-foundation",
"description": "...",
"is_published": true,
"status": "active",
"delivery_mode": "online",
"duration_days": 2,
"schedule": [...],
"categories": [...],
"cities": [...],
"associations": [...]
}
]
}
POST /courses (Admin)
Create a new course.
Body:
{
"title": "ISO 9001 Foundation",
"slug": "iso-9001-foundation",
"description": "...",
"delivery_mode": "online",
"duration_days": 2,
"is_published": false,
"status": "active",
"category_ids": ["uuid"],
"city_ids": ["uuid"],
"association_ids": ["uuid"]
}
Response: 201 Created with the created course object.
PATCH /courses/bulk (Admin)
Bulk-update is_published or status across multiple courses.
Body:
{
"ids": ["uuid1", "uuid2"],
"updates": { "is_published": true }
}
GET /courses/:id
Get a single course by id or slug.
PUT /courses/:id (Admin)
Full update of a course record. Same shape as POST /courses.
DELETE /courses/:id (Admin)
Soft-delete a course (sets status: 'deleted', deleted_at).
POST /courses/:id/interest (Public, Turnstile required)
Register a learner's interest in a course.
Body:
{
"name": "Jane Smith",
"email": "[email protected]",
"phone": "+91-9876543210",
"organisation": "Acme Ltd",
"turnstileToken": "<cf-turnstile-token>"
}
Response: 201 Created
Taxonomies
GET /taxonomies
Returns all taxonomy items grouped by type.
Response:
{
"status": "success",
"data": {
"categories": [{ "id": "uuid", "name": "Quality Management", "slug": "quality-management" }],
"cities": [...],
"associations": [...],
"delivery_modes": [...]
}
}
POST /taxonomies (Admin)
Create a taxonomy item.
Body:
{
"name": "Quality Management",
"slug": "quality-management",
"type": "category"
}
PUT /taxonomies/:id (Admin)
Update a taxonomy item.
DELETE /taxonomies/:id (Admin)
Delete a taxonomy item.
Interests
GET /interests (Admin)
List all interest submissions with pagination.
Query params: page, limit, status, course_id
Response:
{
"status": "success",
"results": 5,
"total": 42,
"page": 1,
"limit": 20,
"data": [...]
}
PATCH /interests/:id (Admin)
Update the status of an interest submission (new, contacted, converted, closed).
Enrollments
GET /enrollments (Admin)
List enrollments with pagination and filters.
POST /enrollments (Admin)
Create an enrollment (typically from converting an interest).
Body:
{
"course_id": "uuid",
"interest_id": "uuid",
"learner_name": "Jane Smith",
"learner_email": "[email protected]",
"organisation": "Acme Ltd",
"scheduled_date": "2026-10-15"
}
PATCH /enrollments/:id (Admin)
Update enrollment status (pending, confirmed, completed, cancelled).
Enquiries
GET /enquiries (Admin)
List enquiries with pagination.
PATCH /enquiries/:id (Admin)
Update enquiry status (new, in_progress, resolved, closed).
Training Plan
GET /training-plan (Admin)
List training plan requests.
POST /training-plan (Public, Turnstile required)
Submit a training plan request.
Body:
{
"organisation": "Acme Ltd",
"contact_name": "John Doe",
"contact_email": "[email protected]",
"contact_phone": "+91-9876543210",
"requirements": "We need ISO 9001 training for 50 staff across 3 sites.",
"turnstileToken": "<cf-turnstile-token>"
}
Contact
POST /contact (Public, Turnstile required)
Submit a contact form message.
Body:
{
"name": "Jane Smith",
"email": "[email protected]",
"message": "I'd like to know more about your courses.",
"turnstileToken": "<cf-turnstile-token>"
}
Verify
GET /verify?certificate_number=PX-2026-00123
Verify a certificate by number.
Response (found):
{
"status": "success",
"data": {
"valid": true,
"learner_name": "Jane Smith",
"course_title": "ISO 9001 Foundation",
"completion_date": "2026-08-01",
"certificate_number": "PX-2026-00123"
}
}
Response (not found): 404 Not Found
Metrics
GET /metrics (Admin)
Returns platform-wide metrics for the admin dashboard.
Response:
{
"status": "success",
"data": {
"total_courses": 24,
"published_courses": 18,
"total_interests": 342,
"total_enrollments": 89,
"total_enquiries": 56,
"recent_interests": [...]
}
}
Audit Logs
GET /audit-logs (Admin)
Paginated, filterable log of admin actions.
Query params: page, limit, action, entity_type, from, to
Error Format
All errors follow the standard @dotevolve/error-utils shape:
{
"status": "error",
"message": "Course not found",
"correlationId": "uuid"
}
HTTP status codes: 400 validation, 401 unauthenticated, 403 unauthorized, 404 not found, 409 conflict, 500 server error.