POST /api/v1/member-tasks/{memberTaskId}/deliverables
Defines the proof a task expects. A task has at most one active deliverable: archive the current one before defining another.
| Role in the organization | This endpoint |
|---|---|
OWNER | Allowed |
ADMIN | Allowed |
MEMBER | Refused — 403 |
| Requirement | Rule for this endpoint |
|---|---|
| Business role | None — expectedReviewerRole names who reviews, it grants no right to define |
| Resource scope | Task of the school attached to the API key |
| Participation | None |
| Resource state | An archived task answers 403; a task that already has an active deliverable answers 400 |
| Parameter | Type | Required | Description |
|---|---|---|---|
memberTaskId | UUID | yes | Task visible from the key's school |
| Header | Required | Description |
|---|---|---|
x-api-key | yes | API key created for this school |
Idempotency-Key | yes | Stable key for this creation intention |
X-Correlation-Id | no | Caller correlation id, returned or generated |
| Field | Type | Required | Nullable | Description |
|---|---|---|---|---|
title | string | yes | no | 3 to 100 characters |
acceptedFormats | array | yes | no | At least one entry, each kind at most once |
acceptedFormats[].kind | string | yes | no | file or link |
acceptedFormats[].mimeTypes | array of string | no | no | For a file: up to 20 types such as application/pdf. A link accepts none |
expectedReviewerRole | string | yes | no | program_director, coach or reviewer |
expectations | string | no | yes | What the member must produce, up to 2000 characters |
criteria | string | no | yes | How it is judged, up to 2000 characters |
{
"title": "Landing page URL",
"expectations": "The public URL of your landing page.",
"criteria": "The page states the offer, the price and one call to action.",
"acceptedFormats": [{"kind": "link"}],
"expectedReviewerRole": "coach"
}curl -X POST "$COHORTE_API_BASE_URL/api/v1/member-tasks/$TASK_ID/deliverables" \
-H "x-api-key: $COHORTE_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H 'Content-Type: application/json' \
-d '{"title":"Landing page URL","acceptedFormats":[{"kind":"link"}],"expectedReviewerRole":"coach"}'201 Created — the deliverable exists, active.200 OK — the same Idempotency-Key replayed the original creation; replayed is true.400 Bad Request — missing Idempotency-Key, invalid JSON, a field outside its limits, no format, a format kind given twice, a MIME type on a link, or a task that already has an active deliverable.401 Unauthorized — the key is missing, invalid, or no longer attached to a member.403 Forbidden — the bearer is not OWNER or ADMIN, or the task is archived.404 Not Found — the task is unknown or belongs to another school.| success | boolean | no | true for a successful request |
| data.id | UUID | no | Deliverable identifier |
| data.memberTaskId | UUID | no | Task that expects it |
| data.title | string | no | What is handed in |
| data.expectations | string | yes | What the member must produce |
| data.criteria | string | yes | How it is judged |
| data.acceptedFormats | array | no | One entry per accepted kind |
| data.acceptedFormats[].kind | string | no | file or link |
| data.acceptedFormats[].mimeTypes | array of string | no | Accepted file types for a file; always empty for a link |
| data.expectedReviewerRole | string | no | program_director, coach or reviewer |
| data.status | string | no | active or archived |
| data.archivedAt | ISO 8601 string | yes | When the archival took effect, in UTC |
| data.archiveReason | string | yes | Why it was archived |
| data.createdAt | ISO 8601 string | no | Creation time, in UTC |
| data.updatedAt | ISO 8601 string | no | Last change, in UTC |
| correlationId | UUID | no | Correlation id for support |
| replayed | boolean | no | true when this answer replays an earlier call |
{
"success": true,
"data": {
"id": "e5b2a7c1-0000-4000-8000-000000000000",
"memberTaskId": "c3f1d2a4-0000-4000-8000-000000000000",
"title": "Landing page URL",
"expectations": "The public URL of your landing page.",
"criteria": "The page states the offer, the price and one call to action.",
"acceptedFormats": [{"kind": "link", "mimeTypes": []}],
"expectedReviewerRole": "coach",
"status": "active",
"archivedAt": null,
"archiveReason": null,
"createdAt": "2026-09-24T08:00:00.000Z",
"updatedAt": "2026-09-24T08:00:00.000Z"
},
"correlationId": "b0e7f1c2-1d3a-4a52-9f77-52a4b7c3d1e0"
}deliverable.created event are
written in one transaction. A replay writes none of them a second time.