POST /api/v1/enrollments/{enrollmentId}/deadline-extensions
Grants one member extra time on one action of their program. The program
rule does not move, and no other enrollment is affected: the exception is
attached to this enrollment and this action only.
From the moment it is granted, the new date is the one everything reads — the
member's plan, your work queue, the cohort dashboard, the current action and its
reminder, and the deadline check that refuses a late deliverable. Cancel the
extension and the program rule applies again.
| 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 grant and cancel an extension on their own enrollments |
| Resource state | The enrollment must not be closed; the action must be published, dated and not yet completed |
Giving someone more time is the gesture of whoever accompanies them, so it takes
the same authorization as closing a participation: an assigned coach can do it,
for their own enrollment and no other. 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 extension intention |
X-Correlation-Id | no | Caller correlation id, returned or generated |
Request body
| Field | Type | Required | Nullable | Description |
|---|
memberTaskId | UUID | yes | no | Program action to extend: published, with a deadline, not completed by this member |
dueAt | ISO 8601 string | yes | no | New deadline. Must be in the future and strictly after the inherited deadline |
reason | string | yes | no | 1 to 2000 characters; visible to your team and in the enrollment history, not to the member |
An action can be extended when all three hold:
- it is published — a draft is not proposed to the member yet, an archived one no longer is;
- it has a deadline, either a fixed date or a relative one. Granting a date where the program asks for none would invent a constraint instead of lifting one;
- it is not completed by this member. The fact is acquired; pushing its deadline changes nothing.
{
"memberTaskId": "3b0e51a4-0000-4000-8000-000000000000",
"dueAt": "2026-09-20T18:00:00.000Z",
"reason": "Short hospital stay, resumption agreed."
}
curl -X POST "$COHORTE_API_BASE_URL/api/v1/enrollments/ENROLLMENT_ID/deadline-extensions" \
-H "x-api-key: $COHORTE_API_KEY" \
-H "Idempotency-Key: extend-2026-09-05" \
-H 'Content-Type: application/json' \
-d '{"memberTaskId":"ACTION_ID","dueAt":"2026-09-20T18:00:00.000Z","reason":"Short hospital stay, resumption agreed."}'
201 Created — the member now has until the new date on that action.
200 OK — the same Idempotency-Key replayed the original extension; nothing was written a second time.
400 Bad Request — missing Idempotency-Key, invalid JSON, an empty reason, a date in the past, or a date that is not after the inherited deadline.
401 Unauthorized — the key is missing, invalid, or no longer attached to a member.
403 Forbidden — the bearer cannot extend deadlines for this enrollment.
404 Not Found — the enrollment is unknown or belongs to another organization.
409 Conflict — the participation is closed, or the action does not belong to this enrollment's program, has no deadline, or is already completed by this member.
| Field | Type | Nullable | Description |
|---|
success | boolean | no | true for a successful request |
replayed | boolean | no | true when the idempotency key replayed an extension |
data.id | UUID | no | Extension identifier |
data.enrollmentId | UUID | no | Enrollment the exception belongs to |
data.memberTaskId | UUID | no | Program action whose deadline moved |
data.memberTaskTitle | string | yes | Title of that action |
data.dueAt | ISO 8601 string | no | Deadline granted to this member, in UTC |
data.reason | string | no | Reason given, for your team |
data.createdBy | UUID | no | Account that granted the extension |
data.createdByName | string | yes | Display name of that account |
data.createdAt | ISO 8601 string | no | When the extension was recorded, in UTC |
data.revokedAt | ISO 8601 string | yes | When it was cancelled or replaced; null while it applies |
data.revokedBy | UUID | yes | Account that cancelled it |
data.revokedByName | string | yes | Display name of that account |
data.revokeReason | string | yes | replaced when a newer extension took over, otherwise null |
data.active | boolean | no | true while this extension is the one that applies |
correlationId | UUID | no | Correlation id for support |
{
"success": true,
"replayed": false,
"data": {
"id": "7c1d9a20-0000-4000-8000-000000000000",
"enrollmentId": "4af4cd8d-0000-4000-8000-000000000000",
"memberTaskId": "3b0e51a4-0000-4000-8000-000000000000",
"memberTaskTitle": "Record ten discovery calls",
"dueAt": "2026-09-20T18:00:00.000Z",
"reason": "Short hospital stay, resumption agreed.",
"createdBy": "6d41ab55-0000-4000-8000-000000000000",
"createdByName": "Ines Fabre",
"createdAt": "2026-09-05T10:00:00.000Z",
"revokedAt": null,
"revokedBy": null,
"revokedByName": null,
"revokeReason": null,
"active": true
},
"correlationId": "0f6b1d92-0000-4000-8000-000000000000"
}
- Read the enrollment's program actions and pick the one the member needs time on.
- Grant the extension with a date after the current deadline and a reason your team will read later.
- Read the enrollment timeline to confirm — it now carries a "deadline extended" entry naming the action and both dates.
- Cancel it when the exception no longer applies; the program rule takes over again.
- The date you grant is absolute. If the participation is paused and resumed
later, the pause shifts the program rule for that member — it does not
move the date you granted, and it does not move the reminder that follows it.
Cancel the extension and the member lands on the shifted program rule.
- Extending again replaces the previous extension. One
POST on an action
that already has one cancels it (revokeReason: "replaced") and writes the new
one, in a single transaction. The whole history is kept.
- The new date must be after the inherited one, which is computed from the
program rule at the moment of the call — never from the extension currently in
place. An extension never shortens a deadline.
- The current action and its reminder follow. When the member's current
action is the derived one for that program action, its deadline is updated and
its reminder is rescheduled on the day before the new date, inside the same
transaction.
- The member sees the date, and only the date. Neither the reason, nor the
author, nor the inherited deadline reaches their screens.
- A deliverable becomes submittable again until the new deadline: the
late-submission refusal reads the same date.
- No notification, email or message is sent by this endpoint, to the member
or to anyone else.
- Retrying with the same
Idempotency-Key returns the original extension and
writes no second record, audit entry or event.
- The extension, the current action update, the reminder events, the audit entry
and the domain event are written in one transaction.