(Morgen) Tasks
Morgen allows you to manage tasks through a unified API. The following documentations explains how to operate on first-party Morgen tasks. Tasks follow a data model inspired by the JSCalendar standard .
List tasks
Returns a list of tasks, optionally filtered by update time.
fetch(
"https://api.morgen.so/v3/tasks/list?limit=<LIMIT>&updatedAfter=<UPDATED_AFTER>",
{
method: "GET",
headers: {
accept: "application/json",
Authorization: "ApiKey <API_KEY>",
},
},
);| Parameter | Type | Default | Required | Description |
|---|---|---|---|---|
limit | Query | 1 | No | Maximum number of tasks to return (max 100). |
updatedAfter | Query | - | No | Only return tasks updated/created after this ISO 8601 datetime. |
limit defaults to 1, not to the maximum. Omitting it returns a single
task, which is easy to misread as an almost empty result. Always set it
explicitly.
The Tasks API is primarily designed for creating tasks in Morgen from unsupported integrations using apps like Zapier. Listing tasks costs 10 points per request. See Rate Limits for details.
Task schema
Here is an example response for the /tasks/list endpoint:
{
"data": {
"tasks": [
{
"@type": "Task",
"id": "WyJBUU1rQURaa1lXWXpOel...",
"accountId": "640a62c9aa5b7e06cf420000",
"integrationId": "morgen",
"taskListId": "default",
"created": "2023-02-28T11:50:57",
"updated": "2023-02-28T17:56:44",
"title": "Review quarterly report",
"description": "Review and provide feedback on Q4 report",
"descriptionContentType": "text/plain",
"due": "2023-03-15T17:00:00",
"timeZone": "Europe/Berlin",
"estimatedDuration": "PT2H",
"priority": 1,
"progress": "needs-action",
"position": 0,
"relatedTo": {
"parent-task-id": {
"@type": "Relation",
"relation": { "parent": true }
}
},
"tags": ["550e8400-e29b-41d4-a716-446655440000"],
"morgen.so:derived": {
"scheduled": true
}
}
],
"labelDefs": [],
"spaces": []
}
}Task Fields Reference
| Field | Type | Description |
|---|---|---|
@type | String | Always "Task" |
id | String | Morgen’s unique identifier for the task |
accountId | String | ID of the account the task belongs to |
integrationId | String | Integration provider ("morgen") |
taskListId | String | ID of the task list containing this task |
title | String | Task title/summary |
description | String | Task description (plain text or HTML) |
descriptionContentType | String | Either "text/plain" or "text/html" |
due | String | Due date in LocalDateTime format (e.g., "2023-03-15T17:00:00") |
timeZone | String | IANA timezone for the due date |
estimatedDuration | String | Estimated duration in ISO 8601 format (e.g., "PT2H") |
priority | Number | Priority level: 0 (undefined), 1 (highest) to 9 (lowest) |
progress | String | Status: "needs-action", "in-process", "completed", "failed", "cancelled" |
position | Number | Sort order within the task list |
relatedTo | Object | Relations to other tasks (for subtask hierarchies) |
tags | Array | Array of tag IDs assigned to this task (see Tags) |
created | String | Creation timestamp (UTC) |
updated | String | Last update timestamp (UTC) |
links | Object | Links attached to the task, keyed by link ID |
morgen.so:derived | Object | Read-only fields derived from the rest of the task, for example scheduled |
Alongside tasks, the response envelope carries two more arrays: labelDefs,
the label definitions referenced by task metadata, and spaces, the workspaces
the tasks belong to. Both are siblings of tasks under data, not fields on a
task.
Additional fields might be added in the future. If you are storing tasks in your database, please make sure to ignore unknown fields.
Get a task
Retrieve a single task by its ID.
fetch("https://api.morgen.so/v3/tasks?id=<TASK_ID>", {
method: "GET",
headers: {
accept: "application/json",
Authorization: "ApiKey <API_KEY>",
},
});| Parameter | Type | Default | Required | Description |
|---|---|---|---|---|
id | Query | - | Yes | The Morgen ID of the task to retrieve. |
Response
{
"data": {
"task": {
"@type": "Task",
"id": "WyJBUU1rQURaa1lXWXpOel...",
"taskListId": "default",
"title": "Review quarterly report",
"progress": "needs-action",
"created": "2023-02-28T11:50:57",
"updated": "2023-02-28T17:56:44"
},
"labelDefs": []
}
}The task object has the same shape as an entry in /tasks/list, see Task
Fields Reference. Fields with no value are omitted
rather than returned as null, so the exact set of keys varies by task.
Deleted and unknown tasks
This endpoint returns HTTP 200 in every case, including for a task that no
longer exists. It never returns 404. The three outcomes are distinguished by
the body:
| Case | Response |
|---|---|
| The task exists | 200, the task under data.task, with no deleted field |
| The task was deleted | 200, the task under data.task, with "deleted": true |
| The ID is unknown, or not yours | 200, data.task set to null |
{
"data": {
"task": {
"@type": "Task",
"id": "fb0ff56a-24f0-40d0-844a-14c6de79c576",
"title": "Review quarterly report",
"progress": "needs-action",
"deleted": true,
"integrationId": "morgen"
},
"labelDefs": []
}
}Deleting a task does not remove the record, so a deleted task stays readable
indefinitely and keeps the field values it had, including its progress. The
deleted flag is what marks it, and it is added alongside the existing fields
rather than replacing them.
Verifying that a task was deleted
Read the task back by ID and check deleted:
POST /v3/tasks/deletereturns 204.GET /v3/tasks?id=<same id>returns 200 with"deleted": true.
Do not verify a deletion by a 404, and do not verify it by absence from
/tasks/list. This endpoint never returns 404, so a check written that way
reports every successful deletion as a failure. Absence from /tasks/list does
not prove deletion either, since that endpoint returns only open tasks: a
merely completed task is missing too, as is anything past the limit.
Note the asymmetry with events, where deletion is applied at the connected
calendar rather than recorded on a Morgen document: there, a deleted event
returns 404 from Get an event. Tasks return 200 with
a flag, events return 404.
Create a task
Create a new task in Morgen.
fetch("https://api.morgen.so/v3/tasks/create", {
method: "POST",
headers: {
accept: "application/json",
Authorization: "ApiKey <API_KEY>",
},
body: JSON.stringify({
title: "Review quarterly report",
description: "Review and provide feedback",
due: "2023-03-15T17:00:00",
timeZone: "Europe/Berlin",
priority: 1,
}),
});Request Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
title | String | Yes | Task title (minimum 1 character). |
description | String | No | Task description. |
descriptionContentType | String | No | "text/plain" or "text/html". Default: "text/plain". |
due | String | No | Due date in LocalDateTime format (exactly 19 characters: YYYY-MM-DDTHH:mm:ss). |
timeZone | String | No | IANA timezone for the due date. |
estimatedDuration | String | No | Estimated duration in ISO 8601 format. |
taskListId | String | No | ID of the task list to add the task to. |
priority | Number | No | Priority: 0 (undefined), 1 (highest) to 9 (lowest). |
progress | String | No | Initial status: "needs-action" or "completed". |
relatedTo | Object | No | Relations for creating subtasks. |
tags | Array | No | Array of tag IDs to assign to the task (see Tags). |
Response
Returns HTTP 200 OK on success, with the ID of the new task. Note that this is
200, not 201, and that the created task is not echoed back. If you need the
full object, follow up with Get a task.
{
"data": {
"id": "WyJBUU1rQURaa1lXWXpOel..."
}
}Creating Subtasks
To create a subtask, use the relatedTo field to specify the parent task:
{
"title": "Subtask title",
"relatedTo": {
"<PARENT_TASK_ID>": {
"@type": "Relation",
"relation": { "parent": true }
}
}
}Update a task
Update an existing task. Only include the fields you want to change.
fetch("https://api.morgen.so/v3/tasks/update", {
method: "POST",
headers: {
accept: "application/json",
Authorization: "ApiKey <API_KEY>",
},
body: JSON.stringify({
id: "<TASK_ID>",
title: "Updated title",
}),
});Request Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
id | String | Yes | The Morgen ID of the task to update. |
title | String | No | Updated task title. |
description | String | No | Updated description. |
due | String | No | Updated due date. |
timeZone | String | No | Updated timezone. |
taskListId | String | No | Move task to a different task list. |
priority | Number | No | Updated priority (0-9). |
progress | String | No | Updated status. |
tags | Array | No | Array of tag IDs (see Tags). |
Updates are patch updates. Only the fields you provide will be updated. All other fields remain unchanged.
Response
Returns HTTP 204 No Content on success.
Move a task
Reorder a task within its list or change its parent (for subtask hierarchies).
fetch("https://api.morgen.so/v3/tasks/move", {
method: "POST",
headers: {
accept: "application/json",
Authorization: "ApiKey <API_KEY>",
},
body: JSON.stringify({
id: "<TASK_ID>",
previousId: "<PREVIOUS_TASK_ID>",
parentId: "<PARENT_TASK_ID>",
}),
});Request Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
id | String | Yes | The Morgen ID of the task to move. |
previousId | String | No | ID of the task this should appear after. Use null to move to first position. |
parentId | String | No | ID of the parent task. Use null to move to root level. |
Response
Returns HTTP 204 No Content on success.
Delete a task
Delete a task. The record is retained and marked as deleted rather than being
removed, so the task remains readable by ID with a deleted flag. See Deleted
and unknown tasks.
fetch("https://api.morgen.so/v3/tasks/delete", {
method: "POST",
headers: {
accept: "application/json",
Authorization: "ApiKey <API_KEY>",
},
body: JSON.stringify({
id: "<TASK_ID>",
}),
});Request Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
id | String | Yes | The Morgen ID of the task to delete. |
Response
Returns HTTP 204 No Content on success, with an empty response body.
Deleting is idempotent. Deleting a task that is already deleted returns
204 again, so a repeated delete is not a way to test whether a task still
exists. An ID that never existed is rejected with 400 "Invalid task id",
not 404. Deleting a parent task also deletes its subtasks.
Close a task
Mark a task as completed.
fetch("https://api.morgen.so/v3/tasks/close", {
method: "POST",
headers: {
accept: "application/json",
Authorization: "ApiKey <API_KEY>",
},
body: JSON.stringify({
id: "<TASK_ID>",
}),
});Request Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
id | String | Yes | The Morgen ID of the task to close. |
occurrenceStart | String | No | For recurring tasks: ISO 8601 datetime of the specific occurrence to close. |
Response
Returns HTTP 204 No Content on success.
Reopen a task
Mark a completed task as not completed.
fetch("https://api.morgen.so/v3/tasks/reopen", {
method: "POST",
headers: {
accept: "application/json",
Authorization: "ApiKey <API_KEY>",
},
body: JSON.stringify({
id: "<TASK_ID>",
}),
});Request Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
id | String | Yes | The Morgen ID of the task to reopen. |
occurrenceStart | String | No | For recurring tasks: ISO 8601 datetime of the specific occurrence to reopen. |
Response
Returns HTTP 204 No Content on success.
Common Validation Errors
Invalid Due Date Format
Error Message:
due must be a valid ISO 8601 date stringCause: The due date is not in the correct format.
Solution: Use exactly 19 characters in LocalDateTime format: YYYY-MM-DDTHH:mm:ss
Example: "2023-03-15T17:00:00" (not "2023-03-15" or "2023-03-15T17:00:00Z")
Invalid Priority Value
Error Message:
priority must not be greater than 9
priority must not be less than 0Cause: Priority value is outside the valid range.
Solution: Use a value between 0 and 9, where 0 is undefined and 1 is highest priority.
Missing Required Title
Error Message:
title must be longer than or equal to 1 charactersCause: Task title is empty or missing.
Solution: Provide a title with at least 1 character when creating a task.