Skip to main content
New Expensify integrates task management directly into chat, allowing you to create actionable items from discussions, assign them to team members, and track progress—all without leaving the conversation.

What Are Tasks?

Tasks in New Expensify are actionable items that:
  • Have a clear title and optional description
  • Can be assigned to specific team members
  • Are linked to a parent chat or report thread
  • Track completion status
  • Send notifications to assignees
  • Generate chat messages in relevant conversations
Every task is actually a special type of report with its own chat thread. This means you can discuss task details in a dedicated space.

Creating Tasks

From Chat

Create a task directly from any conversation:
1

Open the Chat

Navigate to the 1-on-1, group chat, or report thread where you want to create a task.
2

Create Task

Click the + button and select Assign Task, or use the /task command.
3

Fill Details

  • Title: Clear, actionable task description
  • Assignee: Who will complete the task
  • Description: Optional details or context
4

Submit

Click Create Task. The task appears in both the current chat and the assignee’s chat.

Task Creation Code

Here’s how tasks are created programmatically:
// From src/libs/actions/Task.ts
type CreateTaskAndNavigateParams = {
    parentReport: OnyxEntry<OnyxTypes.Report>;
    title: string;
    description: string;
    assigneeEmail: string;
    currentUserAccountID: number;
    currentUserEmail: string;
    assigneeAccountID?: number;
    assigneeChatReport?: OnyxEntry<OnyxTypes.Report>;
    policyID?: string;
};

function createTaskAndNavigate(params: CreateTaskAndNavigateParams) {
    const {
        parentReport,
        title,
        description,
        assigneeEmail,
        currentUserAccountID,
        assigneeAccountID = 0,
        assigneeChatReport,
        policyID = CONST.POLICY.OWNER_EMAIL_FAKE,
    } = params;
    
    const parentReportID = parentReport?.reportID;
    if (!parentReportID) {
        return;
    }
    
    // Build optimistic task report
    const optimisticTaskReport = ReportUtils.buildOptimisticTaskReport(
        currentUserAccountID,
        parentReportID,
        assigneeAccountID,
        title,
        description,
        policyID
    );
    
    // Create task action in parent report
    const optimisticTaskCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(
        currentUserEmail
    );
    
    const optimisticAddCommentReport = ReportUtils.buildOptimisticTaskCommentReportAction(
        taskReportID,
        title,
        assigneeAccountID,
        `task for ${title}`,
        parentReportID
    );
    
    // Optimistic Onyx updates
    const optimisticData = [
        {
            onyxMethod: Onyx.METHOD.SET,
            key: `${ONYXKEYS.COLLECTION.REPORT}${optimisticTaskReport.reportID}`,
            value: completeOptimisticTaskReport,
        },
        {
            onyxMethod: Onyx.METHOD.SET,
            key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticTaskReport.reportID}`,
            value: {
                [optimisticTaskCreatedAction.reportActionID]: optimisticTaskCreatedAction,
            },
        },
        {
            onyxMethod: Onyx.METHOD.MERGE,
            key: `${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`,
            value: {
                lastMessageText: `task for ${title}`,
                hasOutstandingChildTask: assigneeAccountID === currentUserAccountID,
            },
        },
    ];
    
    API.write(WRITE_COMMANDS.CREATE_TASK, parameters, {
        optimisticData,
        successData,
        failureData,
    });
    
    playSound(SOUNDS.DONE);
    notifyNewAction(parentReportID, optimisticAddCommentReport.reportAction, true);
}
When you create a task, New Expensify automatically creates:
  1. The task report itself
  2. A comment in the parent chat
  3. A message in the assignee’s chat (if different from creator)
  4. All associated Onyx state updates for offline-first functionality

Managing Tasks

Completing Tasks

Mark tasks as complete when finished:
1

Open Task

Click on the task from your task list or from the chat where it was created.
2

Mark Complete

Click the Complete button or check the checkbox.
3

Confirmation

The task is marked complete and all participants are notified.
// Complete task implementation from src/libs/actions/Task.ts
function completeTask(
    taskReport: OnyxEntry<OnyxTypes.Report>,
    hasOutstandingChildTaskInParentReport: boolean,
    hasOutstandingChildTask: boolean,
    parentReportAction: OnyxEntry<ReportAction> | undefined,
): OnyxData {
    const taskReportID = taskReport?.reportID;
    if (!taskReportID) {
        return {};
    }
    
    const message = `marked as complete`;
    const completedTaskReportAction = ReportUtils.buildOptimisticTaskReportAction(
        taskReportID,
        CONST.REPORT.ACTIONS.TYPE.TASK_COMPLETED,
        message
    );
    
    const optimisticData = [
        {
            onyxMethod: Onyx.METHOD.MERGE,
            key: `${ONYXKEYS.COLLECTION.REPORT}${taskReportID}`,
            value: {
                stateNum: CONST.REPORT.STATE_NUM.APPROVED,
                statusNum: CONST.REPORT.STATUS_NUM.APPROVED,
                lastReadTime: DateUtils.getDBTime(),
            },
        },
        {
            onyxMethod: Onyx.METHOD.MERGE,
            key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${taskReportID}`,
            value: {
                [completedTaskReportAction.reportActionID]: completedTaskReportAction,
            },
        },
    ];
    
    // Update parent report to reflect task completion
    if (hasOutstandingChildTaskInParentReport) {
        optimisticData.push({
            onyxMethod: Onyx.METHOD.MERGE,
            key: `${ONYXKEYS.COLLECTION.REPORT}${taskReport?.parentReportID}`,
            value: {
                hasOutstandingChildTask,
            },
        });
    }
    
    playSound(SOUNDS.SUCCESS);
    API.write(WRITE_COMMANDS.COMPLETE_TASK, parameters, {
        optimisticData,
        successData,
        failureData,
    });
    
    return {optimisticData, successData, failureData};
}

Reopening Tasks

Reopen a completed task if more work is needed:
// Reopen task from src/libs/actions/Task.ts
function reopenTask(
    taskReport: OnyxEntry<OnyxTypes.Report>,
    parentReport: OnyxEntry<OnyxTypes.Report>,
    currentUserAccountID: number,
) {
    const taskReportID = taskReport?.reportID;
    if (!taskReportID) {
        return;
    }
    
    const message = `marked as incomplete`;
    const reopenedTaskReportAction = ReportUtils.buildOptimisticTaskReportAction(
        taskReportID,
        CONST.REPORT.ACTIONS.TYPE.TASK_REOPENED,
        message
    );
    
    const hasOutstandingChildTask = 
        taskReport?.managerID === currentUserAccountID 
        ? true 
        : parentReport?.hasOutstandingChildTask;
    
    const optimisticData = [
        {
            onyxMethod: Onyx.METHOD.MERGE,
            key: `${ONYXKEYS.COLLECTION.REPORT}${taskReportID}`,
            value: {
                stateNum: CONST.REPORT.STATE_NUM.OPEN,
                statusNum: CONST.REPORT.STATUS_NUM.OPEN,
                lastVisibleActionCreated: reopenedTaskReportAction.created,
                lastMessageText: message,
            },
        },
        {
            onyxMethod: Onyx.METHOD.MERGE,
            key: `${ONYXKEYS.COLLECTION.REPORT}${taskReport?.parentReportID}`,
            value: {
                hasOutstandingChildTask,
            },
        },
    ];
    
    API.write(WRITE_COMMANDS.REOPEN_TASK, parameters, {
        optimisticData,
        successData,
        failureData,
    });
}

Editing Tasks

Update task details after creation:
1

Open Task

Navigate to the task you want to edit.
2

Edit Mode

Click the Edit button or any editable field.
3

Update Fields

Change the title, description, or assignee as needed.
4

Save

Click Save to apply changes.
// Edit task from src/libs/actions/Task.ts
function editTask(report: OnyxTypes.Report, {title, description}: OnyxTypes.Task) {
    // Create the EditedReportAction on the task
    const editTaskReportAction = ReportUtils.buildOptimisticEditedTaskFieldReportAction({
        title,
        description,
    });
    
    // Parse title and description
    const reportName = title 
        ? ReportUtils.getParsedComment(
            title,
            undefined,
            undefined,
            [...CONST.TASK_TITLE_DISABLED_RULES]
          ) 
        : (report?.reportName ?? '');
    
    const parsedTitle = (reportName ?? '').trim();
    const newDescription = typeof description === 'string' 
        ? ReportUtils.getParsedComment(description) 
        : report.description;
    const reportDescription = (newDescription ?? '').trim();
    
    const optimisticData = [
        {
            onyxMethod: Onyx.METHOD.MERGE,
            key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`,
            value: {
                [editTaskReportAction.reportActionID]: editTaskReportAction,
            },
        },
        {
            onyxMethod: Onyx.METHOD.MERGE,
            key: `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`,
            value: {
                reportName: parsedTitle,
                description: reportDescription,
                pendingFields: {
                    ...(title && {reportName: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}),
                    ...(description && {description: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}),
                },
            },
        },
    ];
    
    API.write(WRITE_COMMANDS.EDIT_TASK, parameters, {
        optimisticData,
        successData,
        failureData,
    });
}

Changing Assignee

Reassign a task to a different team member:
// Change task assignee from src/libs/actions/Task.ts
function editTaskAssignee(
    report: OnyxTypes.Report,
    parentReport: OnyxEntry<OnyxTypes.Report>,
    sessionAccountID: number,
    assigneeEmail: string,
    currentUserAccountID: number,
    assigneeAccountID: number | null = 0,
    assigneeChatReport?: OnyxEntry<OnyxTypes.Report>,
) {
    const editTaskReportAction = ReportUtils.buildOptimisticChangedTaskAssigneeReportAction(
        assigneeAccountID ?? CONST.DEFAULT_NUMBER_ID
    );
    
    const optimisticReport = {
        managerID: assigneeAccountID ?? report.managerID,
        pendingFields: {
            ...(assigneeAccountID && {managerID: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}),
        },
        participants: {
            [currentUserAccountID]: {
                notificationPreference: 
                    [assigneeAccountID, taskOwnerAccountID].includes(currentUserAccountID)
                    ? CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS
                    : CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN,
            },
        },
    };
    
    // If assignee changed, create chat report action
    if (assigneeAccountID && assigneeAccountID !== report.managerID) {
        assigneeChatReportOnyxData = ReportUtils.getTaskAssigneeChatOnyxData(
            currentUserAccountID,
            assigneeAccountID,
            report.reportID,
            assigneeChatReportID,
            report.parentReportID,
            reportName ?? '',
            assigneeChatReport,
        );
    }
    
    API.write(WRITE_COMMANDS.EDIT_TASK_ASSIGNEE, parameters, {
        optimisticData,
        successData,
        failureData,
    });
}

Task States

Tasks can be in one of several states:
Open: Task is active and awaiting completion
stateNum: CONST.REPORT.STATE_NUM.OPEN
statusNum: CONST.REPORT.STATUS_NUM.OPEN

Task Notifications

Stay updated on task activity:

Assignment

Receive a notification when a task is assigned to you

Comments

Get notified when someone comments on your task

Completion

Notification when your assigned task is completed

Reopening

Alert when a completed task is reopened

Viewing Tasks

Task List

Access all your tasks from the main task view:
  • My Tasks: Tasks assigned to you
  • Created by Me: Tasks you’ve created
  • All Tasks: All workspace tasks (admins only)

Filtering Tasks

Filter tasks by:
  • Status (Open, Completed)
  • Assignee
  • Creation date
  • Parent report or conversation

Best Practices

Use action verbs and be specific:
  • Good: “Review Q3 expense report by Friday”
  • Bad: “Expense report”
Include relevant details, links, or background information in the task description to help the assignee understand requirements.
Assign tasks to individuals who have the authority and ability to complete them. Avoid assigning to generic roles.
Convert meeting notes and discussion points into tasks to ensure follow-through. Don’t let action items get lost in chat.
Mark tasks complete as soon as they’re done. This keeps everyone informed and maintains accurate task lists.

Quick Action Tasks

New Expensify suggests tasks based on your activity:
// Quick action task creation from src/libs/actions/Task.ts
function startOutCreateTaskQuickAction(
    currentUserAccountID: number,
    reportID: string,
    targetAccountPersonalDetails: OnyxTypes.PersonalDetails,
) {
    clearOutTaskInfoAndNavigate(
        currentUserAccountID,
        targetAccountPersonalDetails,
        reportID,
        undefined,
        true // skipConfirmation
    );
}

// Track quick actions in Onyx
optimisticData.push({
    onyxMethod: Onyx.METHOD.SET,
    key: ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE,
    value: {
        action: CONST.QUICK_ACTIONS.ASSIGN_TASK,
        chatReportID: parentReportID,
        isFirstQuickAction: isEmptyObject(quickAction),
        targetAccountID: assigneeAccountID,
    },
});

Next Steps

Mentions & Notifications

Learn about @mentions and notification settings

Messaging & Threads

Back to messaging features

Approval Workflows

Understand how tasks integrate with approval workflows

Build docs developers (and LLMs) love