mirror of https://github.com/docusealco/docuseal
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
646 lines
16 KiB
646 lines
16 KiB
# API Design and Integration
|
|
|
|
## API Integration Strategy
|
|
|
|
**API Integration Strategy:** Extend existing DocuSeal API v1 with new cohort-specific endpoints under `/api/v1/cohorts/*`. All endpoints follow existing RESTful patterns, authentication mechanisms, and response formats.
|
|
|
|
**Authentication:** Reuse existing Devise + JWT authentication. No changes to auth flow required. New endpoints will require the same bearer token authentication as existing endpoints.
|
|
|
|
**Versioning:** No new API version required. New endpoints extend v1 following existing patterns. All new endpoints return consistent JSON response formats matching existing endpoints.
|
|
|
|
## New API Endpoints
|
|
|
|
### **Cohort Management Endpoints**
|
|
|
|
#### **Create Cohort**
|
|
- **Method:** `POST`
|
|
- **Endpoint:** `/api/v1/cohorts`
|
|
- **Purpose:** Create new cohort with templates and configuration
|
|
- **Integration:** Uses existing Template APIs for template management
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"cohort": {
|
|
"name": "Q1 2025 Learnership",
|
|
"program_type": "learnership",
|
|
"sponsor_email": "sponsor@company.com",
|
|
"student_count": 50,
|
|
"main_template_id": 123,
|
|
"supporting_template_ids": [124, 125],
|
|
"start_date": "2025-02-01",
|
|
"end_date": "2025-07-31"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"id": 1,
|
|
"name": "Q1 2025 Learnership",
|
|
"state": "draft",
|
|
"created_at": "2025-01-02T10:00:00Z",
|
|
"links": {
|
|
"self": "/api/v1/cohorts/1",
|
|
"enrollments": "/api/v1/cohorts/1/enrollments"
|
|
}
|
|
}
|
|
```
|
|
|
|
#### **List Cohorts**
|
|
- **Method:** `GET`
|
|
- **Endpoint:** `/api/v1/cohorts`
|
|
- **Purpose:** Get paginated list of cohorts for current institution
|
|
- **Integration:** Filters by current user's institution
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"data": [
|
|
{
|
|
"id": 1,
|
|
"name": "Q1 2025 Learnership",
|
|
"program_type": "learnership",
|
|
"state": "active",
|
|
"completion_percentage": 65,
|
|
"student_count": 50,
|
|
"completed_students": 32
|
|
}
|
|
],
|
|
"meta": {
|
|
"page": 1,
|
|
"per_page": 20,
|
|
"total": 1
|
|
}
|
|
}
|
|
```
|
|
|
|
#### **Get Cohort Details**
|
|
- **Method:** `GET`
|
|
- **Endpoint:** `/api/v1/cohorts/:id`
|
|
- **Purpose:** Get detailed cohort information with enrollment status
|
|
- **Integration:** Aggregates data from existing Submission APIs
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"id": 1,
|
|
"name": "Q1 2025 Learnership",
|
|
"program_type": "learnership",
|
|
"state": "active",
|
|
"sponsor_email": "sponsor@company.com",
|
|
"admin_signed_at": "2025-01-02T10:30:00Z",
|
|
"templates": {
|
|
"main": { "id": 123, "name": "Learnership Agreement" },
|
|
"supporting": [{ "id": 124, "name": "Code of Conduct" }]
|
|
},
|
|
"enrollments": {
|
|
"waiting": 5,
|
|
"in_progress": 13,
|
|
"complete": 32
|
|
}
|
|
}
|
|
```
|
|
|
|
#### **Invite Students**
|
|
- **Method:** `POST`
|
|
- **Endpoint:** `/api/v1/cohorts/:id/invitations`
|
|
- **Purpose:** Generate invite links or send email invitations
|
|
- **Integration:** Uses existing email system and user creation
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"students": [
|
|
{ "email": "student1@example.com", "first_name": "John", "last_name": "Doe" },
|
|
{ "email": "student2@example.com", "first_name": "Jane", "last_name": "Smith" }
|
|
],
|
|
"send_email": true
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"invitations_sent": 2,
|
|
"invite_links": [
|
|
{ "email": "student1@example.com", "link": "https://flo.doc/invite/abc123" },
|
|
{ "email": "student2@example.com", "link": "https://flo.doc/invite/def456" }
|
|
]
|
|
}
|
|
```
|
|
|
|
#### **Export Cohort Data (FR23)**
|
|
- **Method:** `GET`
|
|
- **Endpoint:** `/api/v1/cohorts/:id/export`
|
|
- **Purpose:** Export cohort data to Excel format
|
|
- **Integration:** Uses existing rubyXL gem for Excel generation
|
|
|
|
**Query Parameters:**
|
|
- `format=xlsx`
|
|
- `include=student_demographics,program_details,sponsor_info`
|
|
|
|
**Response:** Excel file download with columns: cohort_name, student_name, student_surname, student_age, student_race, student_city, program_type, sponsor_company_name, disability_status, gender
|
|
|
|
## Web Portal Routes
|
|
|
|
### **Admin Portal Routes**
|
|
|
|
| Route | Method | Purpose | Authentication | Component |
|
|
|-------|--------|---------|----------------|-----------|
|
|
| `/cohorts/admin` | GET | Cohort dashboard | Devise + Role | `AdminPortal.vue` |
|
|
| `/cohorts/admin/new` | GET | Create cohort wizard | Devise + Role | `CohortWizard.vue` |
|
|
| `/cohorts/admin/:id` | GET | Cohort details | Devise + Role | `CohortDashboard.vue` |
|
|
| `/cohorts/admin/:id/verify` | GET | Document verification | Devise + Role | `VerificationInterface.vue` |
|
|
| `/cohorts/admin/:id/sponsors` | GET | Sponsor management | Devise + Role | `SponsorCoordinator.vue` |
|
|
| `/cohorts/admin/:id/analytics` | GET | Analytics view | Devise + Role | `AnalyticsView.vue` |
|
|
| `/cohorts/admin/:id/export` | GET | Excel export (FR23) | Devise + Role | `ExcelExport.vue` |
|
|
| `/cohorts/admin/:id/invite` | POST | Student invitations | Devise + Role | API call |
|
|
|
|
### **Student Portal Routes**
|
|
|
|
| Route | Method | Purpose | Authentication | Component |
|
|
|-------|--------|---------|----------------|-----------|
|
|
| `/cohorts/student/:token` | GET | Portal entry (token) | Token-based | `StudentPortal.vue` |
|
|
| `/cohorts/student/:token/welcome` | GET | Welcome screen | Token-based | `CohortWelcome.vue` |
|
|
| `/cohorts/student/:token/upload` | GET | Document upload | Token-based | `DocumentUpload.vue` |
|
|
| `/cohorts/student/:token/agreement` | GET | Main agreement | Token-based | `AgreementForm.vue` |
|
|
| `/cohorts/student/:token/supporting` | GET | Supporting docs | Token-based | `AgreementForm.vue` |
|
|
| `/cohorts/student/:token/status` | GET | Progress dashboard | Token-based | `StatusDashboard.vue` |
|
|
| `/cohorts/student/:token/resubmit` | GET | Re-submission flow | Token-based | `ResubmissionFlow.vue` |
|
|
|
|
### **Sponsor Portal Routes**
|
|
|
|
| Route | Method | Purpose | Authentication | Component |
|
|
|-------|--------|---------|----------------|-----------|
|
|
| `/cohorts/sponsor/:token` | GET | Sponsor dashboard | Token-based | `SponsorPortal.vue` |
|
|
| `/cohorts/sponsor/:token/overview` | GET | Cohort overview | Token-based | `SponsorDashboard.vue` |
|
|
| `/cohorts/sponsor/:token/student/:student_id` | GET | Student review | Token-based | `StudentReview.vue` |
|
|
| `/cohorts/sponsor/:token/bulk-sign` | POST | Bulk signing | Token-based | `BulkSigning.vue` |
|
|
| `/cohorts/sponsor/:token/finalize` | POST | Cohort finalization | Token-based | `CohortFinalization.vue` |
|
|
|
|
### **Enrollment Management Endpoints**
|
|
|
|
#### **List Enrollments**
|
|
- **Method:** `GET`
|
|
- **Endpoint:** `/api/v1/cohorts/:id/enrollments`
|
|
- **Purpose:** Get all student enrollments with status
|
|
- **Integration:** Aggregates from CohortEnrollment + existing User/Submission data
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"data": [
|
|
{
|
|
"id": 1,
|
|
"student": { "name": "John Doe", "email": "john@example.com" },
|
|
"state": "complete",
|
|
"verification_state": "verified",
|
|
"documents": { "uploaded": 5, "signed": 3 },
|
|
"created_at": "2025-01-01T10:00:00Z"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
#### **Verify Document**
|
|
- **Method:** `POST`
|
|
- **Endpoint:** `/api/v1/enrollments/:id/verify`
|
|
- **Purpose:** Admin document verification (approve/reject)
|
|
- **Integration:** Creates DocumentVerification records
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"action": "reject",
|
|
"document_type": "matric_certificate",
|
|
"reason": "Certificate is not certified by SAQA"
|
|
}
|
|
```
|
|
|
|
### **Sponsor Endpoints**
|
|
|
|
#### **Get Sponsor Cohort Overview**
|
|
- **Method:** `GET`
|
|
- **Endpoint:** `/api/v1/sponsors/cohorts/:token`
|
|
- **Purpose:** Sponsor access to cohort overview (token-based auth)
|
|
- **Integration:** Validates token, checks all students complete
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"cohort": { "name": "Q1 2025 Learnership", "student_count": 50 },
|
|
"students": [
|
|
{ "id": 1, "name": "John Doe", "state": "complete", "signed": true }
|
|
],
|
|
"can_sign": true,
|
|
"bulk_sign_available": true
|
|
}
|
|
```
|
|
|
|
#### **Bulk Sign**
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"signature": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...",
|
|
"initials": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...",
|
|
"sign_all": true,
|
|
"timestamp": "2025-01-02T15:30:00Z"
|
|
}
|
|
```
|
|
|
|
**Success Response (200):**
|
|
```json
|
|
{
|
|
"signed_count": 50,
|
|
"failed_count": 0,
|
|
"signatures_applied": [
|
|
{
|
|
"enrollment_id": 1,
|
|
"submission_id": 100,
|
|
"status": "signed",
|
|
"signed_at": "2025-01-02T15:30:00Z"
|
|
}
|
|
],
|
|
"cohort_finalized": true,
|
|
"next_step": "Admin can now finalize cohort and download documents"
|
|
}
|
|
```
|
|
|
|
**Error Responses:**
|
|
```json
|
|
// 422 Validation Error
|
|
{
|
|
"error": {
|
|
"code": "VALIDATION_ERROR",
|
|
"message": "Signature data is invalid or corrupted",
|
|
"timestamp": "2025-01-02T15:30:00Z"
|
|
}
|
|
}
|
|
|
|
// 403 Forbidden
|
|
{
|
|
"error": {
|
|
"code": "STATE_ERROR",
|
|
"message": "Cannot sign - some students are not ready",
|
|
"details": {
|
|
"ready": 32,
|
|
"total": 50,
|
|
"pending": 18
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Complete API Response Schemas
|
|
|
|
### **Cohort Endpoints**
|
|
|
|
**POST /api/v1/cohorts - Request:**
|
|
```json
|
|
{
|
|
"cohort": {
|
|
"name": "Q1 2025 Learnership",
|
|
"program_type": "learnership",
|
|
"sponsor_email": "sponsor@company.com",
|
|
"student_count": 50,
|
|
"main_template_id": 123,
|
|
"supporting_template_ids": [124, 125],
|
|
"start_date": "2025-02-01",
|
|
"end_date": "2025-07-31"
|
|
}
|
|
}
|
|
```
|
|
|
|
**POST /api/v1/cohorts - Success Response (201):**
|
|
```json
|
|
{
|
|
"id": 1,
|
|
"uuid": "550e8400-e29b-41d4-a716-446655440000",
|
|
"name": "Q1 2025 Learnership",
|
|
"program_type": "learnership",
|
|
"state": "draft",
|
|
"sponsor_email": "sponsor@company.com",
|
|
"student_count": 50,
|
|
"main_template_id": 123,
|
|
"supporting_template_ids": [124, 125],
|
|
"start_date": "2025-02-01",
|
|
"end_date": "2025-07-31",
|
|
"admin_signed_at": null,
|
|
"created_at": "2025-01-02T10:00:00Z",
|
|
"updated_at": "2025-01-02T10:00:00Z",
|
|
"links": {
|
|
"self": "/api/v1/cohorts/1",
|
|
"enrollments": "/api/v1/cohorts/1/enrollments",
|
|
"invitations": "/api/v1/cohorts/1/invitations"
|
|
}
|
|
}
|
|
```
|
|
|
|
**POST /api/v1/cohorts - Error Responses:**
|
|
```json
|
|
// 422 Validation Error
|
|
{
|
|
"errors": {
|
|
"name": ["can't be blank"],
|
|
"sponsor_email": ["is invalid"],
|
|
"main_template_id": ["must exist"]
|
|
}
|
|
}
|
|
|
|
// 403 Forbidden (wrong institution)
|
|
{
|
|
"error": {
|
|
"code": "AUTHORIZATION_ERROR",
|
|
"message": "Access denied"
|
|
}
|
|
}
|
|
```
|
|
|
|
**GET /api/v1/cohorts/:id - Success Response (200):**
|
|
```json
|
|
{
|
|
"id": 1,
|
|
"uuid": "550e8400-e29b-41d4-a716-446655440000",
|
|
"name": "Q1 2025 Learnership",
|
|
"program_type": "learnership",
|
|
"state": "active",
|
|
"sponsor_email": "sponsor@company.com",
|
|
"student_count": 50,
|
|
"admin_signed_at": "2025-01-02T10:30:00Z",
|
|
"created_at": "2025-01-02T10:00:00Z",
|
|
"updated_at": "2025-01-02T10:30:00Z",
|
|
"templates": {
|
|
"main": {
|
|
"id": 123,
|
|
"name": "Learnership Agreement",
|
|
"uuid": "abc123..."
|
|
},
|
|
"supporting": [
|
|
{
|
|
"id": 124,
|
|
"name": "Code of Conduct",
|
|
"uuid": "def456..."
|
|
}
|
|
]
|
|
},
|
|
"enrollment_summary": {
|
|
"total": 50,
|
|
"waiting": 5,
|
|
"in_progress": 13,
|
|
"complete": 32,
|
|
"rejected": 0
|
|
},
|
|
"completion_percentage": 64,
|
|
"links": {
|
|
"self": "/api/v1/cohorts/1",
|
|
"enrollments": "/api/v1/cohorts/1/enrollments",
|
|
"export": "/api/v1/cohorts/1/export"
|
|
}
|
|
}
|
|
```
|
|
|
|
**GET /api/v1/cohorts/:id/enrollments - Success Response (200):**
|
|
```json
|
|
{
|
|
"data": [
|
|
{
|
|
"id": 1,
|
|
"uuid": "550e8400-e29b-41d4-a716-446655440001",
|
|
"student": {
|
|
"id": 100,
|
|
"name": "John Doe",
|
|
"email": "john@example.com",
|
|
"phone": "+27123456789"
|
|
},
|
|
"state": "complete",
|
|
"verification_state": "verified",
|
|
"rejection_reason": null,
|
|
"student_data": {
|
|
"age": 23,
|
|
"race": "Black",
|
|
"city": "Johannesburg",
|
|
"gender": "Male",
|
|
"disability": "None"
|
|
},
|
|
"documents": {
|
|
"uploaded": 5,
|
|
"signed": 3,
|
|
"rejected": 0
|
|
},
|
|
"created_at": "2025-01-01T10:00:00Z",
|
|
"updated_at": "2025-01-02T14:30:00Z",
|
|
"links": {
|
|
"self": "/api/v1/enrollments/1",
|
|
"verify": "/api/v1/enrollments/1/verify"
|
|
}
|
|
}
|
|
],
|
|
"meta": {
|
|
"page": 1,
|
|
"per_page": 20,
|
|
"total": 50,
|
|
"filters": {
|
|
"state": ["complete"],
|
|
"verification_state": ["verified"]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**POST /api/v1/cohorts/:id/invitations - Request:**
|
|
```json
|
|
{
|
|
"students": [
|
|
{
|
|
"email": "student1@example.com",
|
|
"first_name": "John",
|
|
"last_name": "Doe",
|
|
"phone": "+27123456789",
|
|
"age": 23,
|
|
"race": "Black",
|
|
"city": "Johannesburg",
|
|
"gender": "Male",
|
|
"disability": "None"
|
|
}
|
|
],
|
|
"send_email": true,
|
|
"message": "Welcome to our Q1 2025 Learnership program!"
|
|
}
|
|
```
|
|
|
|
**POST /api/v1/cohorts/:id/invitations - Success Response (201):**
|
|
```json
|
|
{
|
|
"invitations_sent": 1,
|
|
"invite_links": [
|
|
{
|
|
"email": "student1@example.com",
|
|
"token": "abc123def456",
|
|
"link": "https://flo.doc/cohorts/student/abc123def456",
|
|
"expires_at": "2025-02-01T10:00:00Z"
|
|
}
|
|
],
|
|
"errors": []
|
|
}
|
|
```
|
|
|
|
**GET /api/v1/cohorts/:id/export - Query Parameters:**
|
|
- `format=xlsx` (required)
|
|
- `include=student_demographics,program_details,sponsor_info` (optional)
|
|
|
|
**GET /api/v1/cohorts/:id/export - Response:**
|
|
- Returns Excel file (.xlsx) as binary download
|
|
- **Headers:**
|
|
```
|
|
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
|
Content-Disposition: attachment; filename="cohort_1_export_20250102.xlsx"
|
|
```
|
|
|
|
**Excel Columns:**
|
|
```
|
|
cohort_name | student_name | student_surname | student_age | student_race | student_city | program_type | sponsor_company_name | disability_status | gender
|
|
```
|
|
|
|
### **Enrollment Endpoints**
|
|
|
|
**POST /api/v1/enrollments/:id/verify - Request:**
|
|
```json
|
|
{
|
|
"action": "reject",
|
|
"document_type": "matric_certificate",
|
|
"reason": "Certificate is not certified by SAQA. Please provide SAQA verification letter.",
|
|
"metadata": {
|
|
"reviewed_by": "admin@institution.com",
|
|
"review_notes": "Checked against SAQA database"
|
|
}
|
|
}
|
|
```
|
|
|
|
**POST /api/v1/enrollments/:id/verify - Success Response (200):**
|
|
```json
|
|
{
|
|
"id": 1,
|
|
"enrollment_id": 1,
|
|
"action": "rejected",
|
|
"document_type": "matric_certificate",
|
|
"reason": "Certificate is not certified by SAQA. Please provide SAQA verification letter.",
|
|
"admin_id": 50,
|
|
"created_at": "2025-01-02T15:00:00Z",
|
|
"metadata": {
|
|
"reviewed_by": "admin@institution.com",
|
|
"review_notes": "Checked against SAQA database"
|
|
}
|
|
}
|
|
```
|
|
|
|
**POST /api/v1/enrollments/:id/verify - Error Responses:**
|
|
```json
|
|
// 422 Invalid State Transition
|
|
{
|
|
"error": {
|
|
"code": "STATE_ERROR",
|
|
"message": "Cannot reject enrollment that is already complete"
|
|
}
|
|
}
|
|
|
|
// 404 Not Found
|
|
{
|
|
"error": {
|
|
"code": "NOT_FOUND",
|
|
"message": "Enrollment not found"
|
|
}
|
|
}
|
|
```
|
|
|
|
### **Sponsor Endpoints**
|
|
|
|
**GET /api/v1/sponsors/cohorts/:token - Success Response (200):**
|
|
```json
|
|
{
|
|
"cohort": {
|
|
"id": 1,
|
|
"name": "Q1 2025 Learnership",
|
|
"program_type": "learnership",
|
|
"student_count": 50,
|
|
"sponsor_email": "sponsor@company.com"
|
|
},
|
|
"students": [
|
|
{
|
|
"id": 1,
|
|
"name": "John Doe",
|
|
"email": "john@example.com",
|
|
"state": "complete",
|
|
"verification_state": "verified",
|
|
"signed": true,
|
|
"signed_at": "2025-01-02T10:00:00Z",
|
|
"documents": {
|
|
"main_agreement": {
|
|
"id": 100,
|
|
"status": "signed",
|
|
"preview_url": "/api/v1/submissions/100/preview"
|
|
},
|
|
"supporting_docs": [
|
|
{
|
|
"id": 101,
|
|
"name": "Code of Conduct",
|
|
"status": "signed"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
],
|
|
"summary": {
|
|
"total": 50,
|
|
"completed": 32,
|
|
"pending": 18,
|
|
"signed": 32
|
|
},
|
|
"can_sign": true,
|
|
"bulk_sign_available": true,
|
|
"token_expires_at": "2025-01-16T23:59:59Z"
|
|
}
|
|
```
|
|
|
|
**GET /api/v1/sponsors/cohorts/:token - Error Responses:**
|
|
```json
|
|
// 403 Forbidden (students not complete)
|
|
{
|
|
"error": {
|
|
"code": "STATE_ERROR",
|
|
"message": "All students must complete their submissions before sponsor access",
|
|
"details": {
|
|
"completed": 32,
|
|
"total": 50,
|
|
"remaining": 18
|
|
}
|
|
}
|
|
}
|
|
|
|
// 401 Unauthorized (invalid/expired token)
|
|
{
|
|
"error": {
|
|
"code": "AUTHENTICATION_ERROR",
|
|
"message": "Invalid or expired sponsor token"
|
|
}
|
|
}
|
|
```
|
|
|
|
#### **Bulk Sign**
|
|
- **Method:** `POST`
|
|
- **Endpoint:** `/api/v1/sponsors/cohorts/:token/bulk-sign`
|
|
- **Purpose:** Sign all student agreements at once
|
|
- **Integration:** Uses existing submission signing APIs
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"signature": "data:image/png;base64,...",
|
|
"initials": "data:image/png;base64,..."
|
|
}
|
|
```
|
|
|
|
---
|