POST /api/v1/enrollments/{enrollmentId}/interventions
Turns an alert into a tracked action on one enrollment: a reason, an owner, a deadline, a channel and the outcome it must achieve. Once the deadline passes without a closure, the intervention appears in the owner's work queue. An intervention is not the member's next action, and linking a blocker never changes that blocker's status.
| 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 | coach scoped to this enrollment, or program_director scoped to the organization |
| Resource scope | Enrollment of the organization attached to the API key |
| Participation | The coach must be assigned to this enrollment |
| Resource state | The enrollment must be active |
The chosen ownerUserId must satisfy the same rule: an account that could not
write on this enrollment cannot own an intervention on it. A platform-wide role
grants nothing here. An enrollment of another organization, or one outside the
caller's scope, 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 planning intention |
X-Correlation-Id | no | Caller correlation id, returned or generated |
| Field | Type | Required | Nullable | Description |
|---|---|---|---|---|
reason | string | yes | no | late, at_risk, blocker, absence, no_intervention or other |
title | string | yes | no | 1 to 200 characters |
channel | string | yes | no | call, message, email, live or other |
ownerUserId | UUID | yes | no | Account responsible for the intervention; must be allowed on this enrollment |
dueAt | ISO 8601 string | yes | no | Must be in the future |
expectedOutcome | string | yes | no | 1 to 2000 characters; what the intervention must achieve |
blockerId | UUID | no | yes | An open blocker of the same enrollment; the link is informative only |
{
"reason": "late",
"title": "Catch-up call after two weeks without progress",
"channel": "call",
"ownerUserId": "1b7d2f30-0000-4000-8000-000000000000",
"dueAt": "2026-09-05T16:00:00.000Z",
"expectedOutcome": "A prospecting slot booked again for this week."
}curl -X POST "$COHORTE_API_BASE_URL/api/v1/enrollments/ENROLLMENT_ID/interventions" \
-H "x-api-key: $COHORTE_API_KEY" \
-H "Idempotency-Key: intervention-2026-09-02" \
-H 'Content-Type: application/json' \
-d '{"reason":"late","title":"Catch-up call","channel":"call","ownerUserId":"OWNER_ID","dueAt":"2026-09-05T16:00:00.000Z","expectedOutcome":"A prospecting slot booked again."}'201 Created — the intervention was planned.200 OK — the same Idempotency-Key replayed the original planning.400 Bad Request — missing Idempotency-Key, invalid JSON, an unknown reason or channel, a past deadline, an owner who is not allowed on this enrollment, a blocker that is closed or belongs to another enrollment, or an enrollment that is not active.401 Unauthorized — the key is missing, invalid, or no longer attached to a member.403 Forbidden — the bearer cannot plan an intervention on this enrollment.404 Not Found — the enrollment is unknown or belongs to another organization.| Field | Type | Nullable | Description |
|---|---|---|---|
success | boolean | no | true for a successful request |
replayed | boolean | no | true when the idempotency key replayed a planning |
data.id | UUID | no | Intervention identifier |
data.enrollmentId | UUID | no | Enrollment the intervention belongs to |
data.reason | string | no | Reason it was planned |
data.title | string | no | Short title of the intervention |
data.channel | string | no | Channel the owner intends to use |
data.ownerUserId | UUID | no | Account responsible for it |
data.ownerName | string | yes | Display name of that account |
data.dueAt | ISO 8601 string | no | Deadline, in UTC |
data.expectedOutcome | string | no | What the intervention must achieve |
data.status | string | no | planned, completed or cancelled |
data.outcome | string | yes | The outcome reached, once closed |
data.closedAt | ISO 8601 string | yes | Closure time in UTC |
data.closedBy | UUID | yes | Account that closed it |
data.closedByName | string | yes | Display name of that account |
data.blockerId | UUID | yes | Linked blocker, when one was given |
data.plannedBy | UUID | no | Account that planned the intervention |
data.plannedByName | string | yes | Display name of that account |
data.plannedAt | ISO 8601 string | no | Planning time in UTC |
correlationId | UUID | no | Correlation id for support |
{
"success": true,
"replayed": false,
"data": {
"id": "9f2c0a1e-0000-4000-8000-000000000000",
"enrollmentId": "4af4cd8d-0000-4000-8000-000000000000",
"reason": "late",
"title": "Catch-up call after two weeks without progress",
"channel": "call",
"ownerUserId": "1b7d2f30-0000-4000-8000-000000000000",
"ownerName": "Ines Fabre",
"dueAt": "2026-09-05T16:00:00.000Z",
"expectedOutcome": "A prospecting slot booked again for this week.",
"status": "planned",
"outcome": null,
"closedAt": null,
"closedBy": null,
"closedByName": null,
"blockerId": null,
"plannedBy": "6d41ab55-0000-4000-8000-000000000000",
"plannedByName": "Paul Vidal",
"plannedAt": "2026-09-02T09:14:00.000Z"
},
"correlationId": "0f6b1d92-0000-4000-8000-000000000000"
}Idempotency-Key returns the original intervention and
creates no second record, audit entry or event.intervention.planned event are
written in one transaction.