> ## Documentation Index
> Fetch the complete documentation index at: https://beebole.com/llms.txt
> Use this file to discover all available pages before exploring further.

# GraphQL Mutations to Create and Update

> Reference for Beebole GraphQL mutations: log time and expenses, submit and approve timesheets, and manage people, projects, tasks, and organization settings.

Mutations are the GraphQL write operations of the Beebole API. They create, update, archive, or delete data in your Beebole account — logging time and expenses, running the timesheet approval workflow, and managing people, projects, tasks, and organization settings. Send every mutation as a `POST` request to `https://app.beebole.com/graphql` with your key in the `apikey` HTTP header (see [Introduction](/help/api/introduction#authentication)).

Each mutation returns the affected object so you can read back fields in the same request. Object IDs are exposed as `id` (not `_id`). For read-only operations, see [Queries](/help/api/queries).

<Info>
  Time durations in Beebole are expressed in **milliseconds**. To log 2 hours, pass `7200000` (2 × 60 × 60 × 1000), not `120`.
</Info>

***

## Time records

A time record is one logged entry for a person on a given day, linked to projects, a task, or an absence type. Durations are in milliseconds.

| Mutation                    | Key arguments                                                                                                  |
| --------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `addTimeRecord`             | `startTime`, `duration`, `personId`, optional `endTime`, `taskId`, `absenceId`, `projectIds`                   |
| `editTimeRecordStartTime`   | `id`, `startTime`                                                                                              |
| `editTimeRecordEndTime`     | `id`, `endTime`                                                                                                |
| `editTimeRecordDuration`    | `id`, `duration` (milliseconds)                                                                                |
| `editTimeRecordProjects`    | `id`, `projectIds`                                                                                             |
| `editTimeRecordTask`        | `id`, `taskId`                                                                                                 |
| `editTimeRecordAbsence`     | `id`, `absenceId`                                                                                              |
| `editTimeRecordComment`     | `id`, `comment`                                                                                                |
| `editTimeRecordNonBillable` | `id`, `nonBillable`                                                                                            |
| `editTimeRecordWfh`         | `id`, `wfh`                                                                                                    |
| `deleteTimeRecords`         | `ids` (array)                                                                                                  |
| `cloneTimeRecords`          | `personId`, `sourceStartTime`, `sourceEndTime`, `targetStartTime`, `targetEndTime`, optional `replaceExisting` |

`startTime` and `endTime` are Unix timestamps in milliseconds. `duration` is an integer count of **milliseconds**.

### Log a time entry

This logs 2 hours (`7200000` milliseconds) for a person against a task:

```graphql theme={null}
mutation {
  addTimeRecord(
    startTime: 1717200000000
    duration: 7200000
    personId: "64a1b2c3d4e5f6a7b8c9d0e1"
    taskId: "64a1b2c3d4e5f6a7b8c9d0e3"
  ) {
    id
    duration
    startTime {
      ts
      iso
    }
  }
}
```

### Clone a week of time records

`cloneTimeRecords` copies a person's entries from a source period to a target period. Set `replaceExisting: true` to clear the target period first.

```graphql theme={null}
mutation {
  cloneTimeRecords(
    personId: "64a1b2c3d4e5f6a7b8c9d0e1"
    sourceStartTime: 1717200000000
    sourceEndTime: 1717718400000
    targetStartTime: 1717804800000
    targetEndTime: 1718323200000
    replaceExisting: true
  ) {
    id
    startTime {
      ts
      iso
    }
    duration
  }
}
```

***

## Expense records

An expense record is an actual expense logged against a person and/or a project. The amount is sent as `value` with an optional `currency`; if you omit the currency, Beebole uses the person's or organization's default.

| Mutation                          | Key arguments                                                                             |
| --------------------------------- | ----------------------------------------------------------------------------------------- |
| `addExpenseRecord`                | `date`, `expenseTypeId`, `value`, optional `currency`, `personId`, `projectId`, `comment` |
| `deleteExpenseRecord`             | `id`                                                                                      |
| `editExpenseRecordDate`           | `id`, `date`                                                                              |
| `editExpenseRecordExpense`        | `id`, `expenseTypeId`                                                                     |
| `editExpenseRecordPerson`         | `id`, `personId` (null to remove)                                                         |
| `editExpenseRecordProject`        | `id`, `projectId` (null to remove)                                                        |
| `editExpenseRecordAmountValue`    | `id`, `value`                                                                             |
| `editExpenseRecordAmountCurrency` | `id`, `currency`                                                                          |
| `editExpenseRecordComment`        | `id`, `comment`                                                                           |

<Info>
  At least one of `personId` or `projectId` is required on `addExpenseRecord` — both are otherwise optional.
</Info>

### Log an expense

```graphql theme={null}
mutation {
  addExpenseRecord(
    date: 1717200000000
    expenseTypeId: "64a1b2c3d4e5f6a7b8c9d0e4"
    value: 4250
    currency: "EUR"
    personId: "64a1b2c3d4e5f6a7b8c9d0e1"
  ) {
    id
    amount {
      value
      currency
    }
    date {
      ts
      iso
    }
  }
}
```

***

## Timesheets and approvals

The approval workflow runs through three mutations. There is no single "add approval event" call — submitting, approving, and rejecting are distinct operations that return a `BeeboleApprovalEvent`.

| Mutation                | Key arguments                                  |
| ----------------------- | ---------------------------------------------- |
| `submitTimesheet`       | `personId`, `startTime`, `endTime`             |
| `approveTimesheet`      | `id` (the submit event ID)                     |
| `rejectTimesheet`       | `id` (the submit event ID), `stage`, `comment` |
| `sendTimesheetReminder` | `personIds` (array), `startTime`, `endTime`    |

Beebole determines the current approval stage automatically. `submitTimesheet` returns the submit event whose `id` you pass to `approveTimesheet` or `rejectTimesheet`.

### Submit a timesheet for a period

```graphql theme={null}
mutation {
  submitTimesheet(
    personId: "64a1b2c3d4e5f6a7b8c9d0e1"
    startTime: 1717200000000
    endTime: 1719792000000
  ) {
    id
    status
    stage
  }
}
```

### Approve or reject

```graphql theme={null}
mutation {
  approveTimesheet(id: "64a1b2c3d4e5f6a7b8c9d0e5") {
    id
    status
  }
}
```

```graphql theme={null}
mutation {
  rejectTimesheet(
    id: "64a1b2c3d4e5f6a7b8c9d0e5"
    stage: 0
    comment: "Please add the client meeting on Tuesday."
  ) {
    id
    status
    comment
  }
}
```

***

## People

| Mutation                              | Key arguments                               |
| ------------------------------------- | ------------------------------------------- |
| `addPerson`                           | `name`, `email`, `roleId`, optional `color` |
| `deletePerson`                        | `id`, optional `check` (dry-run)            |
| `duplicatePerson`                     | `id`, `name`, optional `email`, `roleId`    |
| `archivePerson` / `unarchivePerson`   | `id`                                        |
| `archivePersons` / `unarchivePersons` | `ids` (array)                               |
| `editPersonName`                      | `id`, `name`                                |
| `editPersonEmail`                     | `id`, `email`                               |
| `editPersonRole`                      | `id`, `roleId`                              |
| `editPersonStartDate`                 | `id`, `startDate`                           |
| `editPersonColor`                     | `id`, `color`                               |
| `editPersonLang`                      | `id`, `lang`                                |

`deletePerson` accepts `check: true` to validate references without deleting.

### Create a person

```graphql theme={null}
mutation {
  addPerson(
    name: "Alice Martin"
    email: "alice@example.com"
    roleId: "64a1b2c3d4e5f6a7b8c9d0e1"
  ) {
    id
    name
  }
}
```

***

## Projects

| Mutation                                | Key arguments                                      |
| --------------------------------------- | -------------------------------------------------- |
| `addProject`                            | `name`, optional `categoryId`, `parentId`, `color` |
| `deleteProject`                         | `id`, optional `check` (dry-run)                   |
| `deleteProjects`                        | `ids` (array)                                      |
| `duplicateProject`                      | `id`, `name`, optional `parentId`                  |
| `archiveProject` / `unarchiveProject`   | `id`                                               |
| `archiveProjects` / `unarchiveProjects` | `ids` (array)                                      |
| `editProjectName`                       | `id`, `name`                                       |
| `editProjectParent`                     | `id`, `parentId`                                   |
| `editProjectColor`                      | `id`, `color`                                      |
| `editProjectAvailability`               | `id`, `availability`                               |
| `addProjectCategory`                    | `name`                                             |
| `deleteProjectCategory`                 | `id`                                               |
| `editProjectCategoryName`               | `id`, `name`                                       |
| `editProjectCategoryLevelNames`         | `id`, `levelNames`                                 |

To create a subproject, pass the parent project's `parentId`. The subproject inherits the parent's category unless you set a different `categoryId`.

### Create a project

```graphql theme={null}
mutation {
  addProject(
    name: "Website Redesign"
    categoryId: "64a1b2c3d4e5f6a7b8c9d0e2"
  ) {
    id
    name
  }
}
```

***

## Tasks

Tasks are independent planning entities you create, schedule, and track time against. Categories organize tasks into workflow statuses.

| Mutation                          | Key arguments                                                  |
| --------------------------------- | -------------------------------------------------------------- |
| `addTask`                         | `name`, optional `categoryId`, `parentId`, `color`, `statusId` |
| `deleteTask`                      | `id`, optional `check` (dry-run)                               |
| `archiveTask` / `unarchiveTask`   | `id`                                                           |
| `archiveTasks` / `unarchiveTasks` | `ids` (array)                                                  |
| `editTaskName`                    | `id`, `name`                                                   |
| `editTaskParent`                  | `id`, `parentId`                                               |
| `editTaskColor`                   | `id`, `color`                                                  |
| `editTaskStatus`                  | `id`, `statusId`                                               |
| `editTaskPeriod`                  | `id`, optional `startTime`, `endTime`, `effort`                |
| `editTaskDuration`                | `id`, `effort` (minutes)                                       |
| `moveTasksToStatus`               | `taskIds`, `targetStatusId`, `orderedTaskIds`                  |

Task categories and their workflow statuses are managed with the category mutations:

| Mutation                                   | Key arguments                    |
| ------------------------------------------ | -------------------------------- |
| `addTaskCategoryStatus`                    | `categoryId`, `name`             |
| `deleteTaskCategoryStatus`                 | `categoryId`, `statusId`         |
| `editTaskCategoryStatusName`               | `statusId`, `name`               |
| `editTaskCategoryStatusColor`              | `statusId`, `color`              |
| `editTaskCategoryStatusMaxConcurrentTasks` | `statusId`, `maxConcurrentTasks` |
| `editCategoryTaskMove`                     | `categoryId`, `taskMove`         |

To move a task between categories or statuses, use `editTaskStatus` (single task) or `moveTasksToStatus` (one or more tasks). `editCategoryTaskMove` sets a category's task-move behavior.

### Create a task

```graphql theme={null}
mutation {
  addTask(
    name: "Write copy"
    categoryId: "64a1b2c3d4e5f6a7b8c9d0e3"
  ) {
    id
    name
  }
}
```

***

## Organization

Organization settings are edited on the single current organization, so these mutations take no `id`. The GraphQL field names keep the British spelling `Organisation`.

| Mutation                | Key arguments                      |
| ----------------------- | ---------------------------------- |
| `editOrganisationName`  | `name`                             |
| `editOrganisationColor` | `color`                            |
| `editGoogleSSO`         | `google` (input)                   |
| `editMicrosoftSSO`      | `microsoft` (input)                |
| `editCustomSSO`         | `customSSO` (input, null to clear) |

### Rename the organization

```graphql theme={null}
mutation {
  editOrganisationName(name: "Acme Corp") {
    id
    name
  }
}
```

***

<Warning>
  Mutations that delete data cannot be undone through the API. Where a `check` argument is available (`deletePerson`, `deleteProject`, `deleteTask`), pass `check: true` first to validate references without deleting anything.
</Warning>

<Tip>
  When creating records, store the returned `id` values so you can reference or update them in follow-up mutations. Batch mutations such as `archivePersons`, `deleteProjects`, and `deleteTimeRecords` accept arrays — prefer them over looping single-item calls.
</Tip>

***

## Related content

<CardGroup cols={3}>
  <Card title="API Introduction" icon="book" href="/help/api/introduction">
    Authenticate with your API key and send your first request.
  </Card>

  <Card title="Queries" icon="magnifying-glass" href="/help/api/queries">
    Read time records, people, projects, and tasks over GraphQL.
  </Card>

  <Card title="Schema explorer" icon="diagram-project" href="/help/api/schema-explorer">
    Browse the full GraphQL schema interactively.
  </Card>
</CardGroup>
