POST /api/v1/enrollments/{enrollmentId}/close
Records how one participation ended. Closing is final: it fixes the real outcome and prepares what comes next. The history stays intact and stays readable to the member according to the access policy you pick. The current action and its reminder are cancelled, planned interventions are closed, an open pause is ended. No other enrollment is affected.
| Role in the organization | This endpoint |
|---|---|
OWNER | Allowed |
ADMIN | Allowed |
MEMBER | Conditional — only with a business role below |
| Requirement | Rule for this endpoint |
|---|---|
| Business role | program_director scoped to the organization, or coach assigned to this enrollment |
| Resource scope | Enrollment of the organization attached to the API key |
| Participation | An assigned coach can close the participation they accompany |
| Resource state | The enrollment must be pending, active or paused |
Closing is wider than pausing, and deliberately so: interrupting a participation decides someone's access to the product, while stating that it ended — and writing the summary — is the gesture of whoever accompanied it. A platform-wide role grants nothing here. An enrollment of another organization is refused without revealing whether it exists.
| Parameter | Type | Required | Description |
|---|---|---|---|
enrollmentId | UUID | yes | Enrollment visible from the key's organization |
| Header | Required | Description |
|---|---|---|
x-api-key | yes | API key created for this organization |
Idempotency-Key | yes | Stable key for this closing intention |
X-Correlation-Id | no | Caller correlation id, returned or generated |
| Field | Type | Required | Nullable | Description |
|---|---|---|---|---|
result | string | yes | no | completed, early_exit or cancelled |
reason | string | yes | no | 1 to 2000 characters; visible to your team and in the enrollment history |
outcomeAchieved | boolean | only for completion | no | Whether the initial objective was reached. Refused for the other results |
summary | string | only for completion | no | 1 to 4000 characters. Refused for the other results |
finalMilestoneId | UUID | no | yes | A milestone of the program's primary path. Refused for the other results |
nextRecommendation | string | no | yes | Up to 2000 characters; the only closing text the member reads |
accessPolicy | string | yes | no | read_only or suspended |
result decides which transition is allowed:
active or paused enrollment can be closed with any of the three;pending enrollment can only be cancelled — a participation that never
started can neither have completed nor stopped along the way;accessPolicy decides what the member can still read:
read_only — the member re-reads their journey and history; they no longer
write;suspended — the journey leaves the member's view. Their account remains.Both policies refuse the same writes. Only reading differs.
Closing does not require any completion rate. A completion states the real outcome, not a percentage of lessons.
{
"result": "completed",
"reason": "Commercial objective reached, final call done.",
"outcomeAchieved": true,
"summary": "Six clients signed over the period, two of them recurring.",
"nextRecommendation": "Scale program next quarter.",
"accessPolicy": "read_only"
}curl -X POST "$COHORTE_API_BASE_URL/api/v1/enrollments/ENROLLMENT_ID/close" \
-H "x-api-key: $COHORTE_API_KEY" \
-H "Idempotency-Key: close-2026-09-03" \
-H 'Content-Type: application/json' \
-d '{"result":"completed","reason":"Commercial objective reached.","outcomeAchieved":true,"summary":"Six clients signed.","accessPolicy":"read_only"}'201 Created — the participation is closed.200 OK — the same Idempotency-Key replayed the original closure.400 Bad Request — missing Idempotency-Key, invalid JSON, an empty reason, an unknown result or policy, a completion without outcomeAchieved or summary, one of those fields sent with another result, a final milestone outside the program's primary path, or a pending enrollment closed with something other than cancelled.401 Unauthorized — the key is missing, invalid, or no longer attached to a member.403 Forbidden — the bearer cannot close this enrollment.404 Not Found — the enrollment is unknown or belongs to another organization.409 Conflict — the enrollment is already closed.| Field | Type | Nullable | Description |
|---|---|---|---|
success | boolean | no | true for a successful request |
replayed | boolean | no | true when the idempotency key replayed a closure |
data.id | UUID | no | Closure identifier |
data.enrollmentId | UUID | no | Enrollment that is closed |
data.result | string | no | completed, early_exit or cancelled |
data.reason | string | no | Reason given for the closure |
data.outcomeAchieved | boolean | yes | null outside a completion |
data.summary | string | yes | null outside a completion |
data.nextRecommendation | string | yes | null when no next step was recorded |
data.finalMilestoneId | UUID | yes | null when no final milestone was named |
data.finalMilestoneTitle | string | yes | Title of that milestone, resolved for display |
data.accessPolicy | string | no | read_only or suspended |
data.closedBy | UUID | no | Account that closed the participation |
data.closedByName | string | yes | Display name of that account |
data.closedAt | ISO 8601 string | no | When the participation was closed, in UTC |
data.createdAt | ISO 8601 string | no | When the closure was recorded, in UTC |
correlationId | UUID | no | Correlation id for support |
{
"success": true,
"replayed": false,
"data": {
"id": "8b71f4c3-0000-4000-8000-000000000000",
"enrollmentId": "4af4cd8d-0000-4000-8000-000000000000",
"result": "completed",
"reason": "Commercial objective reached, final call done.",
"outcomeAchieved": true,
"summary": "Six clients signed over the period, two of them recurring.",
"nextRecommendation": "Scale program next quarter.",
"finalMilestoneId": null,
"finalMilestoneTitle": null,
"accessPolicy": "read_only",
"closedBy": "6d41ab55-0000-4000-8000-000000000000",
"closedByName": "Paul Vidal",
"closedAt": "2026-09-03T09:00:00.000Z",
"createdAt": "2026-09-03T09:00:00.000Z"
},
"correlationId": "0f6b1d92-0000-4000-8000-000000000000"
}pending one can only be cancelled.nextRecommendation.Idempotency-Key returns the original closure and
writes no second record, audit entry, event or cancellation.nextRecommendation — never those two texts.enrollment.closed event are written in
one transaction, together with the cancellation of the current action and its
reminder, the closing of planned interventions, and the end of an open pause.