API Endpoints
Base URL: https://api.perfxcel.com/api/v1
All protected endpoints require Authorization: Bearer <token> and the caller must have an active user_tenant_roles entry for the perfxcel app.
Courses
| Method |
Path |
Auth |
Description |
GET |
/courses |
Public |
List published, active, public courses. Supports category_ids, city_ids, association_ids, delivery_mode_ids, search, sort, page, limit query params. |
GET |
/courses/:id |
Public |
Get a single course by UUID or slug. Returns full course including course_outline, overview, brochure_url. |
POST |
/courses |
Admin |
Create a course. Accepts full courseInputSchema payload. |
PUT |
/courses/:id |
Admin |
Update a course. |
PATCH |
/courses/bulk |
Admin |
Bulk update is_published or status for an array of course IDs. |
DELETE |
/courses/:id |
Admin |
Soft-delete a course (status = 'deleted'). |
POST |
/courses/:id/interest |
Public (Turnstile) |
Register interest in a course. Pass request_brochure: true to also trigger a brochure email. |
Interests
| Method |
Path |
Auth |
Description |
GET |
/interests |
Admin |
Paginated list. Supports search, status, course_id, date_from, date_to, sort, page, limit. |
POST |
/interests |
Admin |
Manually create an interest record (no Turnstile). Pass send_brochure: true to trigger a brochure email. |
PATCH |
/interests/:id |
Admin |
Update interest status (new / contacted / enrolled / rejected). |
DELETE |
/interests |
Admin |
Bulk soft-delete. Body: { ids: string[] }. |
POST |
/interests/:id/resend-brochure |
Admin |
Resend brochure email. Regenerates token if expired. Returns { regenerated: boolean }. |
POST |
/interests/:id/hard-delete |
Admin |
GDPR erasure — anonymises PII fields. Irreversible. |
GET |
/interests/brochure/:token |
Public |
Validate token and stream brochure PDF. Returns 403 if expired. |
Enrollments
| Method |
Path |
Auth |
Description |
GET |
/enrollments |
Admin |
Paginated list with search, status, sort, page, limit. |
POST |
/enrollments |
Admin |
Create enrollment from an interest ID. |
PATCH |
/enrollments/:id |
Admin |
Update status (pending / in_progress / achieved / dropped). Marking achieved triggers certificate generation and email. |
POST |
/enrollments/:id/resend-certificate |
Admin |
Resend certificate email. Fetches existing PDF from storage. |
Training Plans
| Method |
Path |
Auth |
Description |
GET |
/training-plan |
Admin |
Paginated list. Supports search, date_from, date_to, page, limit. |
POST |
/training-plan/request |
Public (Turnstile) |
Public form submission. Sends download email. |
POST |
/training-plan |
Admin |
Manually create a training plan request and send email (no Turnstile). |
GET |
/training-plan/download/:token |
Public |
Validate token and stream training plan PDF. Returns 403 if expired. |
DELETE |
/training-plan |
Admin |
Bulk soft-delete. Body: { ids: string[] }. |
POST |
/training-plan/:id/resend |
Admin |
Resend download email. Regenerates token if expired. Returns { regenerated: boolean }. |
POST |
/training-plan/:id/hard-delete |
Admin |
GDPR erasure. |
Enquiries
| Method |
Path |
Auth |
Description |
GET |
/enquiries |
Admin |
Paginated list. Supports search, status, date_from, date_to, page, limit. |
PATCH |
/enquiries/:id |
Admin |
Update status (new / responded). |
POST |
/contact |
Public (Turnstile) |
Submit a contact enquiry. |
Settings
| Method |
Path |
Auth |
Description |
GET |
/settings |
Admin |
Returns current settings as a flat object: { training_plan_expiry_days: 180, brochure_expiry_days: 180 }. |
PUT |
/settings |
Admin |
Update one or both expiry settings. Body: { training_plan_expiry_days?: number, brochure_expiry_days?: number }. |
Upload (File Proxy)
All file uploads go through these endpoints — never directly to Supabase Storage from the frontend.
| Method |
Path |
Auth |
Description |
POST |
/upload/training-plan |
Admin |
Upload a new training plan PDF (multipart/form-data, field file). Writes to assets bucket as training_plan.pdf. |
GET |
/upload/training-plan/download |
Admin |
Generate a 60-second signed URL for the current training plan PDF. |
POST |
/upload/course-brochure |
Admin |
Upload a course brochure PDF (multipart/form-data, fields file + short_code). Writes to course-brochures bucket. |
Verify (Certificates)
| Method |
Path |
Auth |
Description |
POST |
/verify |
Public (Turnstile) |
Verify a certificate by credential_id. Returns candidate name, course, and issue date. |
GET |
/verify/:id/pdf |
Public |
Stream certificate PDF by credential ID. |
Metrics & Audit
| Method |
Path |
Auth |
Description |
GET |
/metrics |
Admin |
Dashboard metrics (course counts, interests, enrollments, etc.). |
GET |
/audit-logs |
Admin |
Query audit log entries. Proxied to portal-api. Supports action, entityType, from, to, page, pageSize. |
POST |
/audit-logs |
Admin |
Write audit log entries. Proxied to portal-api. |
Taxonomies
| Method |
Path |
Auth |
Description |
GET |
/taxonomies |
Public |
Returns all categories, cities, associations, and delivery modes. |
POST |
/taxonomies/:type |
Admin |
Create a taxonomy item. type = categories / cities / associations / delivery_modes. |
PUT |
/taxonomies/:type/:id |
Admin |
Update a taxonomy item name. |
DELETE |
/taxonomies/:type/:id |
Admin |
Delete a taxonomy item (hard delete — only used for admin-managed reference data). |