akiflow.task API documentation

# akiflow.task

Task API — create, update, delete, and list Akiflow tasks.

View Source

      1"""Task API — create, update, delete, and list Akiflow tasks."""
      2
      3from __future__ import annotations
      4
      5import uuid
      6from datetime import datetime, timezone
      7from typing import TYPE_CHECKING, Any
      8
      9if TYPE_CHECKING:
     10    from .client import Akiflow
     11
     12
     13class Task:
     14    """Operations on Akiflow tasks, available as `client.task`.
     15
     16    All mutations go through `PATCH /v5/tasks` (Akiflow uses upsert semantics).
     17    Deletion is a soft-delete via `trashed_at`.
     18
     19    Example:
     20        ```python
     21        from akiflow import Akiflow
     22
     23        client = Akiflow(refresh_token="def50200...")
     24
     25        # Create
     26        task = client.task.create("Buy groceries")
     27
     28        # Update
     29        client.task.update(task["id"], title="Buy organic groceries")
     30
     31        # Mark done
     32        client.task.done(task["id"])
     33
     34        # Delete
     35        client.task.delete(task["id"])
     36
     37        # List all tasks
     38        result = client.task.list()
     39        for t in result["data"]:
     40            print(t["title"], t["done"])
     41        ```
     42    """
     43
     44    def __init__(self, client: Akiflow):
     45        self._client = client
     46
     47    def list(self, *, sync_token: str | None = None, limit: int = 2500) -> dict:
     48        """Fetch tasks, with optional incremental sync.
     49
     50        Args:
     51            sync_token: Cursor from a previous `list()` response. Pass this
     52                to get only tasks changed since the last call.
     53            limit: Max tasks per page (default 2500).
     54
     55        Returns:
     56            Dict with `data` (list of task dicts), `sync_token` (cursor for
     57            next call), and `has_next_page`.
     58
     59        Example:
     60            ```python
     61            # Full sync
     62            result = client.task.list()
     63            tasks = result["data"]
     64            cursor = result["sync_token"]
     65
     66            # Incremental sync (only changes since last call)
     67            result = client.task.list(sync_token=cursor)
     68            ```
     69        """
     70        params: dict[str, Any] = {"limit": str(limit)}
     71        if sync_token:
     72            params["sync_token"] = sync_token
     73        return self._client._get("/v5/tasks", params=params)
     74
     75    def create(
     76        self,
     77        title: str,
     78        *,
     79        description: str | None = None,
     80        date: str | None = None,
     81        datetime_: str | None = None,
     82        datetime_tz: str | None = None,
     83        duration: int | None = None,
     84        due_date: str | None = None,
     85        priority: int | None = None,
     86        tags_ids: list[str] | None = None,
     87        label: str | None = None,
     88        list_id: str | None = None,
     89        section_id: str | None = None,
     90        links: list[str] | None = None,
     91        **extra: Any,
     92    ) -> dict:
     93        """Create a new task.
     94
     95        By default, tasks land in the **inbox** (`status=1`). To schedule a
     96        task on a specific date/time, pass `date` and `datetime_`.
     97
     98        Args:
     99            title: Task title.
    100            description: HTML description body.
    101            date: Planned date (`"2026-03-27"`).
    102            datetime_: Planned datetime in UTC (`"2026-03-27T09:00:00.000Z"`).
    103            datetime_tz: Timezone for display (default `"Europe/Zurich"`).
    104            duration: Duration in seconds (e.g. `1800` for 30 min).
    105            due_date: Hard due date (`"2026-03-28"`).
    106            priority: Priority level.
    107            tags_ids: List of tag UUIDs.
    108            label: Label/project name **or** UUID. Resolved automatically
    109                via `client.label.resolve_id()`. Takes precedence over `list_id`.
    110            list_id: Project/list UUID (use `label` for name-based lookup).
    111            section_id: Section within a project.
    112            links: List of URL strings.
    113            **extra: Additional fields passed directly to the API.
    114
    115        Returns:
    116            The created task dict as returned by the API.
    117
    118        Example:
    119            ```python
    120            # Inbox task
    121            task = client.task.create("Buy groceries")
    122
    123            # Task assigned to a label by name
    124            task = client.task.create("Review PR", label="Work")
    125
    126            # Scheduled task with duration
    127            task = client.task.create(
    128                "Team standup",
    129                date="2026-03-27",
    130                datetime_="2026-03-27T09:00:00.000Z",
    131                duration=1800,
    132            )
    133            ```
    134        """
    135        if label is not None:
    136            list_id = self._client.label.resolve_id(label)
    137
    138        now = datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
    139        task_id = str(uuid.uuid4())
    140        sorting = int(datetime.now(timezone.utc).timestamp() * 1000)
    141
    142        task: dict[str, Any] = {
    143            "id": task_id,
    144            "title": title,
    145            "description": description,
    146            "status": 1,
    147            "done": False,
    148            "done_at": None,
    149            "date": date,
    150            "datetime": datetime_,
    151            "datetime_tz": datetime_tz or "Europe/Zurich",
    152            "original_date": None,
    153            "original_datetime": None,
    154            "duration": duration,
    155            "due_date": due_date,
    156            "priority": priority,
    157            "sorting": sorting,
    158            "sorting_label": sorting,
    159            "tags_ids": tags_ids,
    160            "links": links or [],
    161            "listId": list_id,
    162            "section_id": section_id,
    163            "calendar_id": None,
    164            "time_slot_id": None,
    165            "recurring_id": None,
    166            "recurrence": None,
    167            "recurrence_version": None,
    168            "plan_unit": None,
    169            "plan_period": None,
    170            "origin": None,
    171            "connector_id": None,
    172            "origin_id": None,
    173            "origin_account_id": None,
    174            "doc": None,
    175            "content": None,
    176            "data": {},
    177            "search_text": "",
    178            "trashed_at": None,
    179            "deleted_at": None,
    180            "global_created_at": now,
    181            "global_updated_at": now,
    182            "global_list_id_updated_at": now if list_id else None,
    183            "global_tags_ids_updated_at": None,
    184            **extra,
    185        }
    186
    187        resp = self._client._patch("/v5/tasks", json=[task])
    188        # Return the single created task
    189        if resp.get("data"):
    190            return resp["data"][0]
    191        return resp
    192
    193    def update(self, task_id: str, **fields: Any) -> dict:
    194        """Update a task by ID.
    195
    196        Only pass the fields you want to change. The `global_updated_at`
    197        timestamp is set automatically.
    198
    199        Args:
    200            task_id: UUID of the task to update.
    201            **fields: Any task fields to update. Use `label` for name-based
    202                project lookup, `list_id` for UUID-based, and `datetime_`
    203                for the datetime field.
    204
    205        Returns:
    206            The updated task dict.
    207
    208        Example:
    209            ```python
    210            # Rename
    211            client.task.update(task_id, title="New title")
    212
    213            # Assign to a label by name
    214            client.task.update(task_id, label="Work")
    215
    216            # Reschedule
    217            client.task.update(
    218                task_id,
    219                date="2026-04-01",
    220                datetime_="2026-04-01T14:00:00.000Z",
    221            )
    222            ```
    223        """
    224        now = datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
    225        payload: dict[str, Any] = {
    226            "id": task_id,
    227            "global_updated_at": now,
    228        }
    229
    230        # Resolve label name/UUID -> listId
    231        if "label" in fields:
    232            label = fields.pop("label")
    233            if label is not None:
    234                fields["list_id"] = self._client.label.resolve_id(label)
    235            else:
    236                fields["list_id"] = None
    237
    238        # Map python-friendly names to API names
    239        if "list_id" in fields:
    240            payload["listId"] = fields.pop("list_id")
    241            payload["global_list_id_updated_at"] = now
    242        if "datetime_" in fields:
    243            payload["datetime"] = fields.pop("datetime_")
    244
    245        payload.update(fields)
    246
    247        resp = self._client._patch("/v5/tasks", json=[payload])
    248        if resp.get("data"):
    249            return resp["data"][0]
    250        return resp
    251
    252    def delete(self, task_id: str) -> dict:
    253        """Soft-delete a task.
    254
    255        Sets `trashed_at` to the current time. The task can still be
    256        recovered in Akiflow's trash.
    257
    258        Args:
    259            task_id: UUID of the task to delete.
    260
    261        Returns:
    262            The updated task dict with `trashed_at` set.
    263
    264        Example:
    265            ```python
    266            client.task.delete("59442bbd-a57d-464f-9fa2-2cb9678379ee")
    267            ```
    268        """
    269        now = datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
    270        payload = {
    271            "id": task_id,
    272            "status": 10,
    273            "trashed_at": now,
    274            "global_updated_at": now,
    275        }
    276        resp = self._client._patch("/v5/tasks", json=[payload])
    277        if resp.get("data"):
    278            return resp["data"][0]
    279        return resp
    280
    281    def done(self, task_id: str, *, date: str | None = None) -> dict:
    282        """Mark a task as done.
    283
    284        Args:
    285            task_id: UUID of the task to complete.
    286            date: Date in ``YYYY-MM-DD`` format. Defaults to today.
    287                A date is required for the task to appear in Akiflow's
    288                done list.
    289
    290        Returns:
    291            The updated task dict with `done=True`.
    292
    293        Example:
    294            ```python
    295            client.task.done("59442bbd-a57d-464f-9fa2-2cb9678379ee")
    296            client.task.done(task_id, date="2026-03-20")
    297            ```
    298        """
    299        now = datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
    300        if date is None:
    301            date = datetime.now(timezone.utc).strftime("%Y-%m-%d")
    302        return self.update(task_id, done=True, done_at=now, date=date, status=2)

class Task: View Source

     14class Task:
     15    """Operations on Akiflow tasks, available as `client.task`.
     16
     17    All mutations go through `PATCH /v5/tasks` (Akiflow uses upsert semantics).
     18    Deletion is a soft-delete via `trashed_at`.
     19
     20    Example:
     21        ```python
     22        from akiflow import Akiflow
     23
     24        client = Akiflow(refresh_token="def50200...")
     25
     26        # Create
     27        task = client.task.create("Buy groceries")
     28
     29        # Update
     30        client.task.update(task["id"], title="Buy organic groceries")
     31
     32        # Mark done
     33        client.task.done(task["id"])
     34
     35        # Delete
     36        client.task.delete(task["id"])
     37
     38        # List all tasks
     39        result = client.task.list()
     40        for t in result["data"]:
     41            print(t["title"], t["done"])
     42        ```
     43    """
     44
     45    def __init__(self, client: Akiflow):
     46        self._client = client
     47
     48    def list(self, *, sync_token: str | None = None, limit: int = 2500) -> dict:
     49        """Fetch tasks, with optional incremental sync.
     50
     51        Args:
     52            sync_token: Cursor from a previous `list()` response. Pass this
     53                to get only tasks changed since the last call.
     54            limit: Max tasks per page (default 2500).
     55
     56        Returns:
     57            Dict with `data` (list of task dicts), `sync_token` (cursor for
     58            next call), and `has_next_page`.
     59
     60        Example:
     61            ```python
     62            # Full sync
     63            result = client.task.list()
     64            tasks = result["data"]
     65            cursor = result["sync_token"]
     66
     67            # Incremental sync (only changes since last call)
     68            result = client.task.list(sync_token=cursor)
     69            ```
     70        """
     71        params: dict[str, Any] = {"limit": str(limit)}
     72        if sync_token:
     73            params["sync_token"] = sync_token
     74        return self._client._get("/v5/tasks", params=params)
     75
     76    def create(
     77        self,
     78        title: str,
     79        *,
     80        description: str | None = None,
     81        date: str | None = None,
     82        datetime_: str | None = None,
     83        datetime_tz: str | None = None,
     84        duration: int | None = None,
     85        due_date: str | None = None,
     86        priority: int | None = None,
     87        tags_ids: list[str] | None = None,
     88        label: str | None = None,
     89        list_id: str | None = None,
     90        section_id: str | None = None,
     91        links: list[str] | None = None,
     92        **extra: Any,
     93    ) -> dict:
     94        """Create a new task.
     95
     96        By default, tasks land in the **inbox** (`status=1`). To schedule a
     97        task on a specific date/time, pass `date` and `datetime_`.
     98
     99        Args:
    100            title: Task title.
    101            description: HTML description body.
    102            date: Planned date (`"2026-03-27"`).
    103            datetime_: Planned datetime in UTC (`"2026-03-27T09:00:00.000Z"`).
    104            datetime_tz: Timezone for display (default `"Europe/Zurich"`).
    105            duration: Duration in seconds (e.g. `1800` for 30 min).
    106            due_date: Hard due date (`"2026-03-28"`).
    107            priority: Priority level.
    108            tags_ids: List of tag UUIDs.
    109            label: Label/project name **or** UUID. Resolved automatically
    110                via `client.label.resolve_id()`. Takes precedence over `list_id`.
    111            list_id: Project/list UUID (use `label` for name-based lookup).
    112            section_id: Section within a project.
    113            links: List of URL strings.
    114            **extra: Additional fields passed directly to the API.
    115
    116        Returns:
    117            The created task dict as returned by the API.
    118
    119        Example:
    120            ```python
    121            # Inbox task
    122            task = client.task.create("Buy groceries")
    123
    124            # Task assigned to a label by name
    125            task = client.task.create("Review PR", label="Work")
    126
    127            # Scheduled task with duration
    128            task = client.task.create(
    129                "Team standup",
    130                date="2026-03-27",
    131                datetime_="2026-03-27T09:00:00.000Z",
    132                duration=1800,
    133            )
    134            ```
    135        """
    136        if label is not None:
    137            list_id = self._client.label.resolve_id(label)
    138
    139        now = datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
    140        task_id = str(uuid.uuid4())
    141        sorting = int(datetime.now(timezone.utc).timestamp() * 1000)
    142
    143        task: dict[str, Any] = {
    144            "id": task_id,
    145            "title": title,
    146            "description": description,
    147            "status": 1,
    148            "done": False,
    149            "done_at": None,
    150            "date": date,
    151            "datetime": datetime_,
    152            "datetime_tz": datetime_tz or "Europe/Zurich",
    153            "original_date": None,
    154            "original_datetime": None,
    155            "duration": duration,
    156            "due_date": due_date,
    157            "priority": priority,
    158            "sorting": sorting,
    159            "sorting_label": sorting,
    160            "tags_ids": tags_ids,
    161            "links": links or [],
    162            "listId": list_id,
    163            "section_id": section_id,
    164            "calendar_id": None,
    165            "time_slot_id": None,
    166            "recurring_id": None,
    167            "recurrence": None,
    168            "recurrence_version": None,
    169            "plan_unit": None,
    170            "plan_period": None,
    171            "origin": None,
    172            "connector_id": None,
    173            "origin_id": None,
    174            "origin_account_id": None,
    175            "doc": None,
    176            "content": None,
    177            "data": {},
    178            "search_text": "",
    179            "trashed_at": None,
    180            "deleted_at": None,
    181            "global_created_at": now,
    182            "global_updated_at": now,
    183            "global_list_id_updated_at": now if list_id else None,
    184            "global_tags_ids_updated_at": None,
    185            **extra,
    186        }
    187
    188        resp = self._client._patch("/v5/tasks", json=[task])
    189        # Return the single created task
    190        if resp.get("data"):
    191            return resp["data"][0]
    192        return resp
    193
    194    def update(self, task_id: str, **fields: Any) -> dict:
    195        """Update a task by ID.
    196
    197        Only pass the fields you want to change. The `global_updated_at`
    198        timestamp is set automatically.
    199
    200        Args:
    201            task_id: UUID of the task to update.
    202            **fields: Any task fields to update. Use `label` for name-based
    203                project lookup, `list_id` for UUID-based, and `datetime_`
    204                for the datetime field.
    205
    206        Returns:
    207            The updated task dict.
    208
    209        Example:
    210            ```python
    211            # Rename
    212            client.task.update(task_id, title="New title")
    213
    214            # Assign to a label by name
    215            client.task.update(task_id, label="Work")
    216
    217            # Reschedule
    218            client.task.update(
    219                task_id,
    220                date="2026-04-01",
    221                datetime_="2026-04-01T14:00:00.000Z",
    222            )
    223            ```
    224        """
    225        now = datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
    226        payload: dict[str, Any] = {
    227            "id": task_id,
    228            "global_updated_at": now,
    229        }
    230
    231        # Resolve label name/UUID -> listId
    232        if "label" in fields:
    233            label = fields.pop("label")
    234            if label is not None:
    235                fields["list_id"] = self._client.label.resolve_id(label)
    236            else:
    237                fields["list_id"] = None
    238
    239        # Map python-friendly names to API names
    240        if "list_id" in fields:
    241            payload["listId"] = fields.pop("list_id")
    242            payload["global_list_id_updated_at"] = now
    243        if "datetime_" in fields:
    244            payload["datetime"] = fields.pop("datetime_")
    245
    246        payload.update(fields)
    247
    248        resp = self._client._patch("/v5/tasks", json=[payload])
    249        if resp.get("data"):
    250            return resp["data"][0]
    251        return resp
    252
    253    def delete(self, task_id: str) -> dict:
    254        """Soft-delete a task.
    255
    256        Sets `trashed_at` to the current time. The task can still be
    257        recovered in Akiflow's trash.
    258
    259        Args:
    260            task_id: UUID of the task to delete.
    261
    262        Returns:
    263            The updated task dict with `trashed_at` set.
    264
    265        Example:
    266            ```python
    267            client.task.delete("59442bbd-a57d-464f-9fa2-2cb9678379ee")
    268            ```
    269        """
    270        now = datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
    271        payload = {
    272            "id": task_id,
    273            "status": 10,
    274            "trashed_at": now,
    275            "global_updated_at": now,
    276        }
    277        resp = self._client._patch("/v5/tasks", json=[payload])
    278        if resp.get("data"):
    279            return resp["data"][0]
    280        return resp
    281
    282    def done(self, task_id: str, *, date: str | None = None) -> dict:
    283        """Mark a task as done.
    284
    285        Args:
    286            task_id: UUID of the task to complete.
    287            date: Date in ``YYYY-MM-DD`` format. Defaults to today.
    288                A date is required for the task to appear in Akiflow's
    289                done list.
    290
    291        Returns:
    292            The updated task dict with `done=True`.
    293
    294        Example:
    295            ```python
    296            client.task.done("59442bbd-a57d-464f-9fa2-2cb9678379ee")
    297            client.task.done(task_id, date="2026-03-20")
    298            ```
    299        """
    300        now = datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
    301        if date is None:
    302            date = datetime.now(timezone.utc).strftime("%Y-%m-%d")
    303        return self.update(task_id, done=True, done_at=now, date=date, status=2)

Operations on Akiflow tasks, available as `client.task`.

All mutations go through `PATCH /v5/tasks` (Akiflow uses upsert semantics). Deletion is a soft-delete via `trashed_at`.

Example:

    from akiflow import Akiflow

    client = Akiflow(refresh_token="def50200...")

    # Create
    task = client.task.create("Buy groceries")

    # Update
    client.task.update(task["id"], title="Buy organic groceries")

    # Mark done
    client.task.done(task["id"])

    # Delete
    client.task.delete(task["id"])

    # List all tasks
    result = client.task.list()
    for t in result["data"]:
        print(t["title"], t["done"])

Task(client: akiflow.Akiflow) View Source

    45    def __init__(self, client: Akiflow):
    46        self._client = client

def list(self, *, sync_token: str | None = None, limit: int = 2500) -> dict: View Source

    48    def list(self, *, sync_token: str | None = None, limit: int = 2500) -> dict:
    49        """Fetch tasks, with optional incremental sync.
    50
    51        Args:
    52            sync_token: Cursor from a previous `list()` response. Pass this
    53                to get only tasks changed since the last call.
    54            limit: Max tasks per page (default 2500).
    55
    56        Returns:
    57            Dict with `data` (list of task dicts), `sync_token` (cursor for
    58            next call), and `has_next_page`.
    59
    60        Example:
    61            ```python
    62            # Full sync
    63            result = client.task.list()
    64            tasks = result["data"]
    65            cursor = result["sync_token"]
    66
    67            # Incremental sync (only changes since last call)
    68            result = client.task.list(sync_token=cursor)
    69            ```
    70        """
    71        params: dict[str, Any] = {"limit": str(limit)}
    72        if sync_token:
    73            params["sync_token"] = sync_token
    74        return self._client._get("/v5/tasks", params=params)

Fetch tasks, with optional incremental sync.

Args: sync_token: Cursor from a previous `list()` response. Pass this to get only tasks changed since the last call. limit: Max tasks per page (default 2500).

Returns: Dict with `data` (list of task dicts), `sync_token` (cursor for next call), and `has_next_page`.

Example:

    # Full sync
    result = client.task.list()
    tasks = result["data"]
    cursor = result["sync_token"]

    # Incremental sync (only changes since last call)
    result = client.task.list(sync_token=cursor)

def create( self, title: str, *, description: str | None = None, date: str | None = None, datetime_: str | None = None, datetime_tz: str | None = None, duration: int | None = None, due_date: str | None = None, priority: int | None = None, tags_ids: 'list[str] | None' = None, label: str | None = None, list_id: str | None = None, section_id: str | None = None, links: 'list[str] | None' = None, **extra: Any) -> dict: View Source

     76    def create(
     77        self,
     78        title: str,
     79        *,
     80        description: str | None = None,
     81        date: str | None = None,
     82        datetime_: str | None = None,
     83        datetime_tz: str | None = None,
     84        duration: int | None = None,
     85        due_date: str | None = None,
     86        priority: int | None = None,
     87        tags_ids: list[str] | None = None,
     88        label: str | None = None,
     89        list_id: str | None = None,
     90        section_id: str | None = None,
     91        links: list[str] | None = None,
     92        **extra: Any,
     93    ) -> dict:
     94        """Create a new task.
     95
     96        By default, tasks land in the **inbox** (`status=1`). To schedule a
     97        task on a specific date/time, pass `date` and `datetime_`.
     98
     99        Args:
    100            title: Task title.
    101            description: HTML description body.
    102            date: Planned date (`"2026-03-27"`).
    103            datetime_: Planned datetime in UTC (`"2026-03-27T09:00:00.000Z"`).
    104            datetime_tz: Timezone for display (default `"Europe/Zurich"`).
    105            duration: Duration in seconds (e.g. `1800` for 30 min).
    106            due_date: Hard due date (`"2026-03-28"`).
    107            priority: Priority level.
    108            tags_ids: List of tag UUIDs.
    109            label: Label/project name **or** UUID. Resolved automatically
    110                via `client.label.resolve_id()`. Takes precedence over `list_id`.
    111            list_id: Project/list UUID (use `label` for name-based lookup).
    112            section_id: Section within a project.
    113            links: List of URL strings.
    114            **extra: Additional fields passed directly to the API.
    115
    116        Returns:
    117            The created task dict as returned by the API.
    118
    119        Example:
    120            ```python
    121            # Inbox task
    122            task = client.task.create("Buy groceries")
    123
    124            # Task assigned to a label by name
    125            task = client.task.create("Review PR", label="Work")
    126
    127            # Scheduled task with duration
    128            task = client.task.create(
    129                "Team standup",
    130                date="2026-03-27",
    131                datetime_="2026-03-27T09:00:00.000Z",
    132                duration=1800,
    133            )
    134            ```
    135        """
    136        if label is not None:
    137            list_id = self._client.label.resolve_id(label)
    138
    139        now = datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
    140        task_id = str(uuid.uuid4())
    141        sorting = int(datetime.now(timezone.utc).timestamp() * 1000)
    142
    143        task: dict[str, Any] = {
    144            "id": task_id,
    145            "title": title,
    146            "description": description,
    147            "status": 1,
    148            "done": False,
    149            "done_at": None,
    150            "date": date,
    151            "datetime": datetime_,
    152            "datetime_tz": datetime_tz or "Europe/Zurich",
    153            "original_date": None,
    154            "original_datetime": None,
    155            "duration": duration,
    156            "due_date": due_date,
    157            "priority": priority,
    158            "sorting": sorting,
    159            "sorting_label": sorting,
    160            "tags_ids": tags_ids,
    161            "links": links or [],
    162            "listId": list_id,
    163            "section_id": section_id,
    164            "calendar_id": None,
    165            "time_slot_id": None,
    166            "recurring_id": None,
    167            "recurrence": None,
    168            "recurrence_version": None,
    169            "plan_unit": None,
    170            "plan_period": None,
    171            "origin": None,
    172            "connector_id": None,
    173            "origin_id": None,
    174            "origin_account_id": None,
    175            "doc": None,
    176            "content": None,
    177            "data": {},
    178            "search_text": "",
    179            "trashed_at": None,
    180            "deleted_at": None,
    181            "global_created_at": now,
    182            "global_updated_at": now,
    183            "global_list_id_updated_at": now if list_id else None,
    184            "global_tags_ids_updated_at": None,
    185            **extra,
    186        }
    187
    188        resp = self._client._patch("/v5/tasks", json=[task])
    189        # Return the single created task
    190        if resp.get("data"):
    191            return resp["data"][0]
    192        return resp

Create a new task.

By default, tasks land in the inbox (`status=1`). To schedule a task on a specific date/time, pass `date` and `datetime_`.

Args: title: Task title. description: HTML description body. date: Planned date (`"2026-03-27"`). datetime_: Planned datetime in UTC (`"2026-03-27T09:00:00.000Z"`). datetime_tz: Timezone for display (default `"Europe/Zurich"`). duration: Duration in seconds (e.g. `1800` for 30 min). due_date: Hard due date (`"2026-03-28"`). priority: Priority level. tags_ids: List of tag UUIDs. label: Label/project name or UUID. Resolved automatically via `client.label.resolve_id()`. Takes precedence over `list_id`. list_id: Project/list UUID (use `label` for name-based lookup). section_id: Section within a project. links: List of URL strings. **extra: Additional fields passed directly to the API.

Returns: The created task dict as returned by the API.

Example:

    # Inbox task
    task = client.task.create("Buy groceries")

    # Task assigned to a label by name
    task = client.task.create("Review PR", label="Work")

    # Scheduled task with duration
    task = client.task.create(
        "Team standup",
        date="2026-03-27",
        datetime_="2026-03-27T09:00:00.000Z",
        duration=1800,
    )

def update(self, task_id: str, **fields: Any) -> dict: View Source

    194    def update(self, task_id: str, **fields: Any) -> dict:
    195        """Update a task by ID.
    196
    197        Only pass the fields you want to change. The `global_updated_at`
    198        timestamp is set automatically.
    199
    200        Args:
    201            task_id: UUID of the task to update.
    202            **fields: Any task fields to update. Use `label` for name-based
    203                project lookup, `list_id` for UUID-based, and `datetime_`
    204                for the datetime field.
    205
    206        Returns:
    207            The updated task dict.
    208
    209        Example:
    210            ```python
    211            # Rename
    212            client.task.update(task_id, title="New title")
    213
    214            # Assign to a label by name
    215            client.task.update(task_id, label="Work")
    216
    217            # Reschedule
    218            client.task.update(
    219                task_id,
    220                date="2026-04-01",
    221                datetime_="2026-04-01T14:00:00.000Z",
    222            )
    223            ```
    224        """
    225        now = datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
    226        payload: dict[str, Any] = {
    227            "id": task_id,
    228            "global_updated_at": now,
    229        }
    230
    231        # Resolve label name/UUID -> listId
    232        if "label" in fields:
    233            label = fields.pop("label")
    234            if label is not None:
    235                fields["list_id"] = self._client.label.resolve_id(label)
    236            else:
    237                fields["list_id"] = None
    238
    239        # Map python-friendly names to API names
    240        if "list_id" in fields:
    241            payload["listId"] = fields.pop("list_id")
    242            payload["global_list_id_updated_at"] = now
    243        if "datetime_" in fields:
    244            payload["datetime"] = fields.pop("datetime_")
    245
    246        payload.update(fields)
    247
    248        resp = self._client._patch("/v5/tasks", json=[payload])
    249        if resp.get("data"):
    250            return resp["data"][0]
    251        return resp

Update a task by ID.

Only pass the fields you want to change. The `global_updated_at` timestamp is set automatically.

Args: task_id: UUID of the task to update. **fields: Any task fields to update. Use `label` for name-based project lookup, `list_id` for UUID-based, and `datetime_` for the datetime field.

Returns: The updated task dict.

Example:

    # Rename
    client.task.update(task_id, title="New title")

    # Assign to a label by name
    client.task.update(task_id, label="Work")

    # Reschedule
    client.task.update(
        task_id,
        date="2026-04-01",
        datetime_="2026-04-01T14:00:00.000Z",
    )

def delete(self, task_id: str) -> dict: View Source

    253    def delete(self, task_id: str) -> dict:
    254        """Soft-delete a task.
    255
    256        Sets `trashed_at` to the current time. The task can still be
    257        recovered in Akiflow's trash.
    258
    259        Args:
    260            task_id: UUID of the task to delete.
    261
    262        Returns:
    263            The updated task dict with `trashed_at` set.
    264
    265        Example:
    266            ```python
    267            client.task.delete("59442bbd-a57d-464f-9fa2-2cb9678379ee")
    268            ```
    269        """
    270        now = datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
    271        payload = {
    272            "id": task_id,
    273            "status": 10,
    274            "trashed_at": now,
    275            "global_updated_at": now,
    276        }
    277        resp = self._client._patch("/v5/tasks", json=[payload])
    278        if resp.get("data"):
    279            return resp["data"][0]
    280        return resp

Soft-delete a task.

Sets `trashed_at` to the current time. The task can still be recovered in Akiflow's trash.

Args: task_id: UUID of the task to delete.

Returns: The updated task dict with `trashed_at` set.

Example:

    client.task.delete("59442bbd-a57d-464f-9fa2-2cb9678379ee")

def done(self, task_id: str, *, date: str | None = None) -> dict: View Source

    282    def done(self, task_id: str, *, date: str | None = None) -> dict:
    283        """Mark a task as done.
    284
    285        Args:
    286            task_id: UUID of the task to complete.
    287            date: Date in ``YYYY-MM-DD`` format. Defaults to today.
    288                A date is required for the task to appear in Akiflow's
    289                done list.
    290
    291        Returns:
    292            The updated task dict with `done=True`.
    293
    294        Example:
    295            ```python
    296            client.task.done("59442bbd-a57d-464f-9fa2-2cb9678379ee")
    297            client.task.done(task_id, date="2026-03-20")
    298            ```
    299        """
    300        now = datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
    301        if date is None:
    302            date = datetime.now(timezone.utc).strftime("%Y-%m-%d")
    303        return self.update(task_id, done=True, done_at=now, date=date, status=2)

Mark a task as done.

Args: task_id: UUID of the task to complete. date: Date in `YYYY-MM-DD` format. Defaults to today. A date is required for the task to appear in Akiflow's done list.

Returns: The updated task dict with `done=True`.

Example:

    client.task.done("59442bbd-a57d-464f-9fa2-2cb9678379ee")
    client.task.done(task_id, date="2026-03-20")