POST /api/v1/enrollments
Enrolls one person into one program. The person is either one the school
already knows (personId) or a new one created in the same call. The
enrollment is born pending, and the program's primary path is assigned
automatically — there is no path to choose here.
| Role in the organization | This endpoint |
|---|---|
OWNER | Allowed |
ADMIN | Allowed |
MEMBER | Refused — 403 |
| Requirement | Rule for this endpoint |
|---|---|
| Business role | None |
| Resource scope | Program, cohort and person belong to the key's organization |
| Participation | The person has no enrollment in progress on this program |
| Resource state | The cohort is not completed or archived, and has a seat left unless you override capacity |
| Header | Required | Description |
|---|---|---|
x-api-key | yes | API key created for this organization |
Idempotency-Key | yes | Stable key for this enrollment; a replay returns the first result |
X-Correlation-Id | no | Caller correlation id, returned or generated |
| Field | Type | Required | Nullable | Description |
|---|---|---|---|---|
programId | UUID | yes | no | Program of the key's organization |
cohortId | UUID | no | yes | Cohort of that same program |
enrolledAt | string | no | yes | Calendar date YYYY-MM-DD, may be in the past. Defaults to now |
person | object | yes | no | {"kind": "existing", "personId": "…"} or {"kind": "new", "person": {…}} |
person.person.firstName | string | with new | no | 1 to 100 characters |
person.person.lastName | string | no | yes | Up to 100 characters |
person.person.email | string | with new | no | Valid email address |
person.person.phone | string | no | yes | Up to 50 characters |
capacityOverride | object | no | no | {"enabled": true, "reason": "…"} to enroll into a full cohort; reason 1 to 500 characters |
curl -X POST "$COHORTE_API_BASE_URL/api/v1/enrollments" \
-H "x-api-key: $COHORTE_API_KEY" \
-H "Idempotency-Key: enroll-ada-autumn-2026" \
-H "Content-Type: application/json" \
-d '{
"programId": "9f2c4a10-8f7e-4a3b-9c1d-2e5f6a7b8c9d",
"cohortId": "3a1f7c2e-5b6d-4e8f-9a0b-1c2d3e4f5a6b",
"person": {
"kind": "new",
"person": {"firstName": "Ada", "lastName": "Lovelace", "email": "ada@example.com"}
}
}'201 Created — the enrollment, pending.200 OK — replayed: true: the key was already used, the original enrollment comes back.400 Bad Request — missing Idempotency-Key, invalid JSON, an unknown program, person or cohort, a cohort of another program, a person already enrolled on this program, or an email already carried by another person of the school.401 Unauthorized — missing, unknown or expired key.403 Forbidden — the bearer is not OWNER or ADMIN, or the cohort is closed.409 Conflict — the cohort is full and no capacityOverride was sent.| Field | Type | Description |
|---|---|---|
data.id | UUID | Enrollment id |
data.programId | UUID | Program |
data.cohortId | UUID | Cohort, or null |
data.pathId | UUID | The program's primary path, or null when it has none |
data.personId | UUID | Person enrolled — the new one's id when you sent kind: new |
data.status | string | pending |
data.enrolledAt | string | ISO 8601, the member's Day 0 |
{
"success": true,
"replayed": false,
"data": {
"id": "6a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
"programId": "9f2c4a10-8f7e-4a3b-9c1d-2e5f6a7b8c9d",
"cohortId": "3a1f7c2e-5b6d-4e8f-9a0b-1c2d3e4f5a6b",
"pathId": "7d8e9f0a-1b2c-4d3e-8f4a-5b6c7d8e9f0a",
"personId": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
"status": "pending",
"enrolledAt": "2026-10-05T09:12:44.000Z"
},
"correlationId": "c0ffee00-1111-4222-8333-444455556666"
}enrollment.created is delivered to your webhook subscriptions. The cohort and path assignments are recorded in the enrollment's timeline.Idempotency-Key creates neither a second enrollment nor a second person.