# Export a Month of Time Records Source: https://beebole.com/help/api/examples/example-1 End-to-end Beebole GraphQL example: authenticate, query getTimeRecords with a typed filter and date window, and read durations in milliseconds. This example walks through a complete read workflow with the [Beebole GraphQL API](/help/api/introduction): exporting one month of logged time for reporting. You authenticate with your API key, query `getTimeRecords` for a date window, narrow the results with a typed filter, select the fields you need, and read the returned durations — which Beebole expresses in milliseconds. This is a read-only workflow. `getTimeRecords` never modifies data. To create or edit entries, see [Log time entries programmatically](/help/api/examples/example-2). *** ## What this example does It fetches every worked time record for one person across a calendar month, returning each record's duration, day, billable flag, and the related person and projects. The result is the raw data you would feed into a report, an invoice, or a data warehouse. The query uses three pieces of the API: * The `startTime` and `endTime` top-level arguments to bound the period (Unix timestamps in milliseconds). * The `time: true` argument to return only worked time, excluding absence entries. * The `filter` argument — a list of typed input objects — to restrict results to one person. *** ## Step 1 — Send the request with your API key Every request is a `POST` to `https://app.beebole.com/graphql` with your key in the `apikey` HTTP header. Here is the full call with `curl`: ```bash theme={null} curl -X POST https://app.beebole.com/graphql \ -H "Content-Type: application/json" \ -H "apikey: YOUR_API_KEY" \ -d '{ "query": "query ($start: BeeboleTimestamp, $end: BeeboleTimestamp, $filter: [BeeboleTimeRecordFilter]) { getTimeRecords(startTime: $start, endTime: $end, time: true, filter: $filter) { id duration startTime { ts iso } nonBillable person { id name } projects { id name } } }", "variables": { "start": 1717200000000, "end": 1719791999999, "filter": [{ "personId": "64a1b2c3d4e5f6a7b8c9d0e1" }] } }' ``` The two timestamps bracket June 2024: `1717200000000` is June 1 at 00:00 UTC and `1719791999999` is the last millisecond of June 30. *** ## Step 2 — The query The same operation written as a standalone GraphQL document: ```graphql theme={null} query ExportTimeRecords { getTimeRecords( startTime: 1717200000000 endTime: 1719791999999 time: true filter: [{ personId: "64a1b2c3d4e5f6a7b8c9d0e1" }] ) { id duration startTime { ts iso } nonBillable person { id name } projects { id name } } } ``` Each object in the `filter` array sets a single matching field. To combine conditions — for example, one person on a specific project — chain them with a `following` value of `AND` or `OR`: ```graphql theme={null} query { getTimeRecords( startTime: 1717200000000 endTime: 1719791999999 time: true filter: [ { personId: "64a1b2c3d4e5f6a7b8c9d0e1", following: AND } { projectIds: ["64a1b2c3d4e5f6a7b8c9d0e2"] } ] ) { id duration projects { id name } } } ``` The Beebole query API has no pagination — there are no `limit`, `offset`, or cursor arguments. `getTimeRecords` returns every record matching its arguments. Size the result first with `countTimeRecords`, which takes the same arguments and returns an integer. *** ## Step 3 — Read the response A successful response returns the matching records under `data.getTimeRecords`: ```json theme={null} { "data": { "getTimeRecords": [ { "id": "665b1f8e0a1c2d3e4f5a6b70", "duration": 7200000, "startTime": { "ts": 1717372800000, "iso": "2024-06-03T00:00:00.000Z" }, "nonBillable": false, "person": { "id": "64a1b2c3d4e5f6a7b8c9d0e1", "name": "Alice Martin" }, "projects": [ { "id": "64a1b2c3d4e5f6a7b8c9d0e2", "name": "Website Redesign" } ] }, { "id": "665b1f8e0a1c2d3e4f5a6b71", "duration": 27000000, "startTime": { "ts": 1717459200000, "iso": "2024-06-04T00:00:00.000Z" }, "nonBillable": true, "person": { "id": "64a1b2c3d4e5f6a7b8c9d0e1", "name": "Alice Martin" }, "projects": [ { "id": "64a1b2c3d4e5f6a7b8c9d0e3", "name": "Internal" } ] } ] } } ``` *** ## Reading the milliseconds duration `duration` is an integer count of **milliseconds**. To present it as hours, divide by `3600000` (1000 × 60 × 60): * `7200000` ms ÷ `3600000` = **2 hours** * `27000000` ms ÷ `3600000` = **7.5 hours** `startTime` is an object with two fields: `ts` (a Unix timestamp in milliseconds, marking the day the time was logged) and `iso` (the same instant as an ISO 8601 string). Use `ts` with your language's date utilities (for example, `new Date(record.startTime.ts)` in JavaScript), or read `iso` directly. The `nonBillable` flag tells you whether the entry counts as billable when you build invoicing reports. *** ## Related content Authenticate with your API key and send your first request. Every read operation, with the typed filter input and date arguments. The write operations that create and update records. # Log Time Entries Programmatically Source: https://beebole.com/help/api/examples/example-2 End-to-end Beebole GraphQL example: add a time record with a millisecond duration, adjust it, clone a week, and submit the timesheet for approval. This example walks through a complete write workflow with the [Beebole GraphQL API](/help/api/introduction): logging time on behalf of a team member and moving it through the approval flow. You authenticate with your API key, create a time record with `addTimeRecord`, adjust it, clone a full week with `cloneTimeRecords`, and submit the period with `submitTimesheet`. Every duration in Beebole is expressed in milliseconds. These are write operations — they create and change data. For read-only reporting, see [Export a month of time records](/help/api/examples/example-1). *** ## Step 1 — Log a time entry `addTimeRecord` creates one logged entry for a person on a given day. The required arguments are `startTime` (a Unix timestamp in milliseconds), `duration` (milliseconds), and `personId`. Pass `taskId`, `absenceId`, or `projectIds` to link the entry. Here the call logs 2 hours against a task. Durations are in **milliseconds**. To log 2 hours, pass `7200000` (2 × 60 × 60 × 1000) — not `120` and not `2`. ```bash theme={null} curl -X POST https://app.beebole.com/graphql \ -H "Content-Type: application/json" \ -H "apikey: YOUR_API_KEY" \ -d '{ "query": "mutation { addTimeRecord(startTime: 1717200000000, duration: 7200000, personId: \"64a1b2c3d4e5f6a7b8c9d0e1\", taskId: \"64a1b2c3d4e5f6a7b8c9d0e3\") { id duration startTime { ts iso } } }" }' ``` The same operation as a GraphQL document: ```graphql theme={null} mutation LogTime { addTimeRecord( startTime: 1717200000000 duration: 7200000 personId: "64a1b2c3d4e5f6a7b8c9d0e1" taskId: "64a1b2c3d4e5f6a7b8c9d0e3" ) { id duration startTime { ts iso } } } ``` The mutation returns the created record, including its `id`: ```json theme={null} { "data": { "addTimeRecord": { "id": "665b1f8e0a1c2d3e4f5a6b70", "duration": 7200000, "startTime": { "ts": 1717200000000, "iso": "2024-06-01T00:00:00.000Z" } } } } ``` Store the returned `id` — you will need it to update the record. *** ## Step 2 — Adjust the entry Each editable field has its own mutation. To correct the logged duration, call `editTimeRecordDuration` with the record `id` and the new millisecond value. This changes the entry from 2 hours to 3 hours (`10800000` ms): ```graphql theme={null} mutation { editTimeRecordDuration( id: "665b1f8e0a1c2d3e4f5a6b70" duration: 10800000 ) { id duration } } ``` Related per-field mutations follow the same shape: `editTimeRecordStartTime`, `editTimeRecordProjects`, `editTimeRecordTask`, `editTimeRecordComment`, and `editTimeRecordNonBillable`. Each takes the record `id` plus the one value to change and returns the updated record. *** ## Step 3 — Clone a full week When a person's week repeats a regular pattern, `cloneTimeRecords` copies their entries from a source period to a target period in one call. Set `replaceExisting: true` to clear the target period before cloning. ```graphql theme={null} mutation CloneWeek { cloneTimeRecords( personId: "64a1b2c3d4e5f6a7b8c9d0e1" sourceStartTime: 1717200000000 sourceEndTime: 1717718400000 targetStartTime: 1717804800000 targetEndTime: 1718323200000 replaceExisting: true ) { id startTime { ts iso } duration } } ``` The mutation returns the list of newly created records: ```json theme={null} { "data": { "cloneTimeRecords": [ { "id": "665b2a110a1c2d3e4f5a6b80", "startTime": { "ts": 1717804800000, "iso": "2024-06-08T00:00:00.000Z" }, "duration": 10800000 }, { "id": "665b2a110a1c2d3e4f5a6b81", "startTime": { "ts": 1717891200000, "iso": "2024-06-09T00:00:00.000Z" }, "duration": 7200000 } ] } } ``` *** ## Step 4 — Submit the timesheet Once the period is complete, `submitTimesheet` sends it into the approval workflow. It takes the `personId` and the `startTime` / `endTime` of the period, and returns a submit event whose `id` you pass to `approveTimesheet` or `rejectTimesheet` later. ```graphql theme={null} mutation SubmitMonth { submitTimesheet( personId: "64a1b2c3d4e5f6a7b8c9d0e1" startTime: 1717200000000 endTime: 1719791999999 ) { id status stage } } ``` ```json theme={null} { "data": { "submitTimesheet": { "id": "665b3c920a1c2d3e4f5a6b90", "status": "s", "stage": 0 } } } ``` The `status` field reports the period's approval state — `d` (draft), `s` (submitted), `a` (approved), or `r` (rejected). Beebole determines the current approval `stage` automatically. Deletions made through the API cannot be undone. `cloneTimeRecords` with `replaceExisting: true` first deletes the existing records in the target period — make sure the target window is correct before sending it. *** ## Related content Authenticate with your API key and send your first request. Every write operation, with full argument signatures. Read back the records you create with these mutations. # API Documentation Source: https://beebole.com/help/api/introduction Get started with the Beebole GraphQL API: how the endpoint works, how to authenticate with an API key, and how to send your first query or mutation. Beebole exposes a [GraphQL](https://graphql.org/) API that gives you full programmatic access to your account data. You can read time records, people, projects, and tasks, and you can create or update records directly — all over a single HTTP endpoint. For a guided overview of what you can build with the API, see [Custom Integrations](/help/integrations/custom-integrations). *** ## Endpoint All API requests go to: ```text theme={null} POST https://app.beebole.com/graphql ``` The API accepts `application/json` bodies containing a `query` string and an optional `variables` object. *** ## Authentication Beebole uses API key authentication. Include your API key as a request header named `apikey`: ```http theme={null} apikey: YOUR_API_KEY ``` In Beebole, click the button with your initials at the bottom of the left sidebar, then click **API Key**. Go to **Settings** > **API** in your Beebole account. Your **API Key** is displayed on this page. Click **Copy** to copy it to the clipboard. Add the `apikey` header to all HTTP requests you send to the endpoint. The panel shows a single active API key, and it does not expire. The key authenticates as the person it belongs to, so requests inherit that person's role and permissions. Keep your API key secure. Do not include it in client-side code or commit it to public repositories. Store it in an environment variable or a secrets manager. If a key is compromised, open the **API Key** panel and click **Reset** to revoke the current key and generate a new one. *** ## Making a request A GraphQL request is a POST with a JSON body containing a `query` field (and optionally `variables`). Here is a minimal example using `curl`: ```bash theme={null} curl -X POST https://app.beebole.com/graphql \ -H "Content-Type: application/json" \ -H "apikey: YOUR_API_KEY" \ -d '{"query": "{ currentPerson { name email } }"}' ``` A successful response looks like this: ```json theme={null} { "data": { "currentPerson": { "name": "Alice Martin", "email": "alice@example.com" } } } ``` *** ## Queries and mutations The Beebole API uses standard GraphQL conventions: * **Queries** read data without side effects. Use them to fetch people, projects, tasks, time records, expense records, and more. * **Mutations** write data. Use them to create, update, archive, or delete entities. See the full reference pages for details: * [Queries](/help/api/queries) — all available read operations * [Mutations](/help/api/mutations) — all available write operations * [Schema explorer](/help/api/schema-explorer) — how to explore the schema with introspection and a GraphQL client *** ## Error handling GraphQL responses follow the standard shape: results come back under `data`, and any operation-level errors come back in an `errors` array. ```json theme={null} { "errors": [ { "message": "AccountIsInactive" } ] } ``` ### Authentication errors If the `apikey` header is missing or invalid, Beebole cannot resolve the linked person. You may see one of these authentication messages in the `errors` array: | Message | Cause | | ---------------------------------------- | ---------------------------------------------------- | | `APIKeyError:InvalidKey` | The API key is missing or not recognized | | `APIKeyError:CantFindLinkedAccount` | The person linked to this key no longer exists | | `APIKeyError:CantFindLinkedOrganisation` | The organization linked to this key no longer exists | ### Permission handling Beebole resolves permissions per operation. If a request asks for data or an action the linked person is not authorized for, Beebole omits the affected fields from `data` and lists their paths in a `permissionsErrors` array, rather than failing the whole request: ```json theme={null} { "data": {}, "permissionsErrors": ["Query.currentPerson", "BeebolePerson.*"] } ``` When an account's subscription is inactive, API-key requests are blocked with HTTP `402 Payment Required` and an `AccountIsInactive` error. Browser sessions are not blocked the same way, so this only affects API access. *** ## Rate limits Beebole does not apply a general rate limit to GraphQL API traffic. Rate limiting is reserved for a small set of sensitive operations — such as sign-in, sign-up, inviting people, and loading public holidays — which are not part of a typical integration workflow. For very high request volumes, batch related operations into fewer requests where possible, and contact [support@beebole.com](mailto:support@beebole.com) if you have specific throughput needs. *** ## Related content All available GraphQL read operations in the Beebole API. All available GraphQL write operations in the Beebole API. Let Claude, ChatGPT, or Claude Code work with your Beebole data. Receive signed, real-time event notifications on your own endpoints. ## Frequently asked questions Yes. The API is standard GraphQL over HTTPS. Any language with an HTTP client — Python, JavaScript, Ruby, Go, Java, and others — can make requests. In Beebole, click the button with your initials at the bottom of the left sidebar, then click **API Key**. Beebole creates the key automatically, and you can **Copy** or **Reset** it from that panel. Yes. The API supports both queries (reading time records) and mutations (creating and editing time entries). See [Queries](/help/api/queries) and [Mutations](/help/api/mutations) for the full list of operations. The Beebole API supports GraphQL introspection, so any standard GraphQL client can fetch the full schema — types, queries, mutations, and their arguments. See the [Schema explorer](/help/api/schema-explorer) page for how to connect a GraphQL client. # GraphQL Mutations to Create and Update Source: https://beebole.com/help/api/mutations 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). Time durations in Beebole are expressed in **milliseconds**. To log 2 hours, pass `7200000` (2 × 60 × 60 × 1000), not `120`. *** ## 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` | At least one of `personId` or `projectId` is required on `addExpenseRecord` — both are otherwise optional. ### 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 } } ``` *** 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. 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. *** ## Related content Authenticate with your API key and send your first request. Read time records, people, projects, and tasks over GraphQL. Browse the full GraphQL schema interactively. # Querying time records, projects, and people Source: https://beebole.com/help/api/queries Reference for Beebole GraphQL queries — read organizations, people, projects, tasks, tags, time records, and expenses, with typed filter inputs. Beebole queries are the read operations of the [Beebole GraphQL API](/help/api/introduction). They return data from your account — your organization, people, projects, tasks, tags, time records, and expenses — without modifying anything. Every query is sent as a `POST` request to `https://app.beebole.com/graphql` with your API key in the `apikey` HTTP header. Queries never change data. To create, update, archive, or delete records, use [Mutations](/help/api/mutations) instead. For authentication setup, see the [API introduction](/help/api/introduction#authentication). Field names follow GraphQL conventions exactly as defined in the schema. Every entity exposes its identifier as `id` (not `_id`), and the organization-level query is spelled `currentOrganisation` (British spelling, as in the schema). *** ## Organization Read account-level data with `currentOrganisation` and `getAuditTrails`. | Query | Arguments | Returns | | --------------------- | --------------------------------------------------------------------------- | ---------------------------------------------------- | | `currentOrganisation` | none | The organization linked to the authenticated API key | | `getAuditTrails` | `startTime`, `fromPersonId`, `operations`, `argumentMatch`, `responseMatch` | Audit trail entries for the organization | ```graphql theme={null} query { currentOrganisation { id name locked } } ``` `getAuditTrails` returns mutations performed in the account. With no `startTime`, it covers the last seven days; `startTime` selects a different seven-day window. ```graphql theme={null} query { getAuditTrails(operations: ["addTimeRecord", "deleteTimeRecords"]) { operationName timestamp person { id name } arguments { name value } } } ``` *** ## People Read team members with `currentPerson`, `getPerson`, `getPersons`, `countPersons`, and `filterPersonIds`. `getPotentialProjectOwners` lists the people allowed to own a given project. | Query | Arguments | Returns | | --------------------------- | -------------------- | ---------------------------------- | | `currentPerson` | none | The person linked to the API key | | `getPerson` | `id` | A single person | | `getPersons` | `filter`, `archived` | A list of people | | `countPersons` | `filter`, `archived` | The number of matching people | | `filterPersonIds` | `filter`, `archived` | Only the IDs of matching people | | `getPotentialProjectOwners` | `projectId` | People eligible to own the project | ```graphql theme={null} query { getPersons { id name email archived } } ``` ```graphql theme={null} query { getPerson(id: "64a1b2c3d4e5f6a7b8c9d0e1") { id name email role { id name } } } ``` By default `getPersons` returns only active people. Pass `archived: true` to include archived ones. ```graphql theme={null} query { getPersons(archived: true) { id name archived } } ``` *** ## Projects Read projects with `getProject`, `getProjects`, `countProjects`, and `filterProjectIds`. `getProjectCategories` lists the top-level groupings projects are organised into. | Query | Arguments | Returns | | ---------------------- | ---------------------------------- | --------------------------------- | | `getProject` | `id` | A single project | | `getProjects` | `filter`, `archived`, `categoryId` | A list of projects | | `countProjects` | `filter`, `archived`, `categoryId` | The number of matching projects | | `filterProjectIds` | `filter`, `archived`, `categoryId` | Only the IDs of matching projects | | `getProjectCategories` | none | All project categories | ```graphql theme={null} query { getProjects { id name level category { id name } } } ``` The `level` field is the project's depth in the hierarchy, where `0` is a top-level project. Restrict results to one category with `categoryId`: ```graphql theme={null} query { getProjects(categoryId: "64a1b2c3d4e5f6a7b8c9d0e1") { id name } } ``` ```graphql theme={null} query { getProjectCategories { id name levelNames } } ``` *** ## Tasks Read tasks with `getTask`, `getTasks`, `countTasks`, and `filterTaskIds`. `getTaskCategories` lists the categories (boards) tasks belong to. | Query | Arguments | Returns | | ------------------- | ---------------------------------- | ------------------------------ | | `getTask` | `id` | A single task | | `getTasks` | `filter`, `archived`, `categoryId` | A list of tasks | | `countTasks` | `filter`, `archived`, `categoryId` | The number of matching tasks | | `filterTaskIds` | `filter`, `archived`, `categoryId` | Only the IDs of matching tasks | | `getTaskCategories` | none | All task categories | ```graphql theme={null} query { getTasks { id name startTime { ts iso } endTime { ts iso } status { id name } } } ``` ```graphql theme={null} query { getTask(id: "64a1b2c3d4e5f6a7b8c9d0e1") { id name effort dependencies { id name } } } ``` *** ## Tags Read tags with `getTag`, `getTags`, `countTags`, and `filterTagIds`. `getTagCategories` lists the categories tags are grouped into. | Query | Arguments | Returns | | ------------------ | ---------------------------------- | ----------------------------- | | `getTag` | `id` | A single tag | | `getTags` | `filter`, `archived`, `categoryId` | A list of tags | | `countTags` | `filter`, `archived`, `categoryId` | The number of matching tags | | `filterTagIds` | `filter`, `archived`, `categoryId` | Only the IDs of matching tags | | `getTagCategories` | none | All tag categories | ```graphql theme={null} query { getTags { id name level } } ``` *** ## Time records A time record is a logged time entry for one person on a specific day, linked to projects, a task, or an absence type. Read them with `getTimeRecord`, `getTimeRecords`, and `countTimeRecords`. `getAbsenceConsumed` returns how much of an absence type a person has used in a window. | Query | Arguments | Returns | | -------------------- | ---------------------------------------------------------- | -------------------------------------------------- | | `getTimeRecord` | `id` | A single time record | | `getTimeRecords` | `startTime`, `endTime`, `time`, `absence`, `wfh`, `filter` | A list of time records | | `countTimeRecords` | `startTime`, `endTime`, `time`, `absence`, `wfh`, `filter` | The number of matching time records | | `getAbsenceConsumed` | `personId`, `absenceId`, `startTime`, `endTime` | Absence consumed in the window, in the type's unit | `getTimeRecords` and `countTimeRecords` accept top-level arguments alongside `filter`: * **`startTime`** / **`endTime`** — Unix timestamps in milliseconds bounding the period. * **`time`** — when `true`, return only worked time (entries without an absence type). * **`absence`** — when `true`, return only absence entries (entries with an absence type). * **`wfh`** — when `true`, return only entries logged as working from home. * **`filter`** — a typed filter array (see [Filtering results](#filtering-results)). ```graphql theme={null} query { getTimeRecords(startTime: 1704067200000, endTime: 1706745600000, time: true) { id duration startTime { ts iso } nonBillable person { id name } projects { id name } } } ``` `duration` is an integer in milliseconds. `startTime` and `endTime` are `BeeboleTime` objects — select their subfields `ts` (a Unix timestamp in milliseconds) and `iso` (an ISO 8601 string). The `person`, `projects`, `task`, and `absence` fields resolve the related entities inline. ```graphql theme={null} query { getAbsenceConsumed( personId: "64a1b2c3d4e5f6a7b8c9d0e1" absenceId: "64a1b2c3d4e5f6a7b8c9d0e2" startTime: 1704067200000 endTime: 1735689600000 ) } ``` *** ## Expense records An expense record is an expense logged by a person against a project and an expense type. Read them with `getExpenseRecord` and `getExpenseRecords`. | Query | Arguments | Returns | | ------------------- | -------------------------------- | ------------------------- | | `getExpenseRecord` | `id` | A single expense record | | `getExpenseRecords` | `startTime`, `endTime`, `filter` | A list of expense records | ```graphql theme={null} query { getExpenseRecords(startTime: 1704067200000, endTime: 1706745600000) { id date { ts iso } amount { value currency } comment expenseType { id name } project { id name } } } ``` `date` is a `BeeboleTime` object (`ts` in milliseconds, `iso` as an ISO 8601 string). `amount` is an object with `value` (an integer in the currency's smallest unit) and `currency`. *** ## Absence types Absence types are the kinds of leave people can log against, such as vacation or sick leave. Read them with `getAbsenceType`, `getAbsenceTypes`, and `countAbsenceTypes`. | Query | Arguments | Returns | | ------------------- | -------------------- | ------------------------------------ | | `getAbsenceType` | `id` | A single absence type | | `getAbsenceTypes` | `filter`, `archived` | A list of absence types | | `countAbsenceTypes` | `filter`, `archived` | The number of matching absence types | ```graphql theme={null} query { getAbsenceTypes { id name unit involveCosts } } ``` *** ## Filtering results The `filter` argument is **a list of typed input objects** — not a list of `field`/`value` pairs. Each entity has its own filter input type, such as `BeeboleTimeRecordFilter`, `BeebolePersonFilter`, or `BeeboleProjectFilter`. You set the named fields you want to match directly on each object. ```graphql theme={null} query { getTimeRecords(filter: [{ personId: "64a1b2c3d4e5f6a7b8c9d0e1" }]) { id duration startTime { ts iso } } } ``` Each object in the array should set a single matching field. To combine conditions, add a `following` value of `AND` or `OR` to chain one condition to the next: ```graphql theme={null} query { getProjects( filter: [ { categoryId: "64a1b2c3d4e5f6a7b8c9d0e1", following: AND } { archived: false } ] ) { id name } } ``` ### Time record filter fields `BeeboleTimeRecordFilter` accepts these fields (each optional): | Field | Type | Matches | | ---------------------------------------------- | ------------ | --------------------------------------------------------------- | | `id` / `notId` | ID | A specific time record, or all but one | | `personId` / `notPersonId` | ID | Records for (or excluding) one person | | `personIds` | `[ID]` | Records for any of several people | | `startTime` / `endTime` | Timestamp | Records starting after / ending before a millisecond timestamp | | `projectIds` / `notProjectIds` | `[ID]` | Records on (or excluding) projects, including their subprojects | | `taskIds` / `notTaskIds` | `[ID]` | Records on (or excluding) tasks | | `projectTagIds` / `projectNotTagIds` | `[ID]` | Records whose projects carry (or lack) given tags | | `personTagIds` / `personNotTagIds` | `[ID]` | Records whose person carries (or lacks) given tags | | `projectCategoryIds` / `notProjectCategoryIds` | `[ID]` | Records by project category | | `taskCategoryIds` / `notTaskCategoryIds` | `[ID]` | Records by task category | | `isAbsence` | Boolean | Whether the record is an absence | | `following` | `AND` / `OR` | Logical operator chaining this condition to the next | Other entities expose filter types tuned to their own fields — for example `BeebolePersonFilter` supports `roleId`, `tagIds`, and `name` (a case-insensitive match), while `BeeboleProjectFilter` supports `categoryId`, `managedById`, and `subProjectId`. Browse every filter type and its fields in the [Schema explorer](/help/api/schema-explorer). *** ## Counting and ID-only queries For each major entity, alongside the full `get…` query you also get a counterpart that returns just a count, and (for people, projects, tasks, and tags) one that returns only matching IDs. * **`count…`** queries (`countPersons`, `countProjects`, `countTasks`, `countTags`, `countTimeRecords`, `countAbsenceTypes`) return an integer. Use them to size a result set before fetching it. * **`filter…Ids`** queries (`filterPersonIds`, `filterProjectIds`, `filterTaskIds`, `filterTagIds`) return an array of IDs instead of full objects, which is lighter when you only need identifiers. ```graphql theme={null} query { countTimeRecords(startTime: 1704067200000, endTime: 1706745600000) } ``` The Beebole query API has no pagination — there are no `limit`, `offset`, `first`, or `cursor` arguments. A list query returns every record that matches its arguments. Narrow large result sets with `filter`, the `startTime` / `endTime` window, or `categoryId`, and use a `count…` query first to gauge the size. *** ## Related content Authenticate with an API key and send your first request. The write operations that create, update, archive, and delete records. Browse every query, type, field, and filter input interactively. # GraphQL Schema Explorer & Introspection Source: https://beebole.com/help/api/schema-explorer Explore the Beebole GraphQL schema with introspection — connect a GraphQL client to the endpoint to browse types, queries, mutations, and field docs. The Beebole GraphQL API is self-describing through introspection, so you can explore the full schema — every type, query, mutation, argument, and field — directly from a GraphQL client. Beebole does not ship a separate hosted schema browser; instead, you point any standard GraphQL tool at the API endpoint and let introspection reveal the schema. Introspection is enabled for API-key requests against the Beebole GraphQL endpoint. There is nothing to turn on in your account — any GraphQL client that supports introspection can read the schema as soon as it can authenticate. *** ## The endpoint and authentication Schema exploration uses the same endpoint and authentication as every other API call: ``` POST https://app.beebole.com/graphql ``` Authenticate by sending your API key in the `apikey` HTTP header: ```http theme={null} apikey: YOUR_API_KEY ``` For where to find your key, see [Introduction to the Beebole API](/help/api/introduction). *** ## Exploring the schema with introspection Introspection is a built-in GraphQL feature: the schema can be queried like any other data. A minimal introspection query lists every type the API exposes: ```graphql theme={null} { __schema { types { name kind } } } ``` You can also introspect a single type to see its fields, their types, and any descriptions: ```graphql theme={null} { __type(name: "Person") { name description fields { name description type { name kind } } } } ``` Most GraphQL clients run a full introspection query automatically when you connect, so you rarely write these queries by hand — the client uses the result to power autocomplete, type-aware validation, and a browsable schema view. *** ## Connecting a GraphQL client Any GraphQL client or IDE that supports introspection can explore the Beebole schema. Configure it with the endpoint URL and the `apikey` header: Point the client at `https://app.beebole.com/graphql`. In the client's HTTP headers configuration, add a header named `apikey` with your key as the value. Run the client's introspection or "refresh schema" action. The client fetches the schema and enables documentation browsing, autocomplete, and validation. This works with desktop GraphQL IDEs, GraphQL plugins for code editors, and command-line tools that fetch a schema for code generation. Because the schema comes from introspection, it always reflects the live API — there is no separate schema file to download or keep in sync. *** ## Built-in GraphiQL at the endpoint Beebole serves a GraphiQL IDE — including the schema Explorer panel — directly at the API endpoint. Open `https://app.beebole.com/graphql` in a browser to load it. GraphiQL provides a query editor, a documentation explorer for browsing types and fields, and an Explorer panel for building queries by clicking through the schema. GraphiQL sends requests to the same `POST /graphql` endpoint, so it follows the same authentication rules. Use GraphiQL's headers editor to add your `apikey` header before running introspection or queries. *** ## Related content The endpoint, API-key authentication, and how to send your first request. All available GraphQL read operations in the Beebole API. All available GraphQL write operations in the Beebole API. # Account settings for your organization Source: https://beebole.com/help/documentation/account-settings Configure your Beebole organization's defaults from Account Settings: localization, billing, cost, public holidays, work schedule, SSO, and more. Beebole's **Account Settings** is where administrators set the organization-wide defaults that cascade to everyone in the account. From this page you manage your organization's identity, regional formats, billing and cost rates, work schedule, approval workflow, and sign-in options. Account Settings is admin-only. The defaults you set here apply across the whole organization, but most can be overridden lower down — on a tag, a person, or a project. ## Open Account Settings You reach Account Settings from the user menu, not from the sidebar's top-level features. Click the button with your initials at the bottom of the left sidebar. In the menu that opens, click **Account Settings**. The other items in this menu — **Subscription**, **Person Roles**, **Work Schedules**, **Integrations**, **Master data review**, **Time Off**, **Expense Types**, **Custom Fields**, **Release Notes**, and **Delete Account** — open their own dedicated screens. ## Organization identity The header at the top of Account Settings holds your organization's name, logo, and accent color. Each edit saves automatically. ### Name Click the organization name in the header and type a new one. The change is saved automatically. ### Logo Click the logo area in the header. You can **Paste, drop, or click to add the logo of your organization**. Beebole crops the logo to fit the header, and a crop tool lets you adjust the framing before it saves. The logo appears in the sidebar and in emails Beebole sends. To remove it, open the logo again and clear it. ### Accent color Click the color swatch in the header and pick a color from the palette. The accent color updates immediately for everyone in the organization — it colors buttons, highlights, and other branded elements. To clear it, open the picker again and click the swatch that is currently selected. Each person can also choose a personal interface color from the user menu (**Pick your color**), which overrides the organization accent color for them alone. ## Settings panels Below the header, Account Settings is organized into panels. Each panel sets a default for the organization, and every edit saves automatically. The panels available are: | Panel | What it controls | | ----------------------------------- | ---------------------------------------------------------------------------------------------- | | **Localization** | Time zone, currency, number formats, time and date formats, and first day of the week | | **Show or hide by default** | Which optional elements are visible across the account by default | | **Timesheet and Planning Settings** | How your team logs time — periodicity, duration format, entry rules, categories, and reminders | | **Billing** | The default billing rate for the organization | | **Cost** | The default cost rate for the organization | | **Work schedule** | The expected working hours assigned at the organization level | | **Public holidays** | The default public holiday calendar | | **Approval workflow** | The default sequence of approval stages for submitted timesheets | | **Single Sign-On** | Google, Microsoft, and custom SSO sign-in for your members | | **Notifications** | The default notification settings for the account | | **Email templates** | The wording of emails Beebole sends on the organization's behalf | Billing rates, costs, custom fields, approval workflow, and Single Sign-On depend on your subscription and may not appear on every account. ### Localization The **Localization** panel sets the regional defaults for the whole organization: * **Language** — The interface language. Beebole is available in English, Czech, German, Spanish, French, Hungarian, Italian, Dutch, Polish, and Portuguese. * **Time zone** — The default time zone for date and time calculations. * **Currency** — The currency used for billing rates, costs, budgets, and expenses. * **First day of the week** — Whether the week starts on Monday, Sunday, or another day. This affects timesheets, calendars, and reports. * **Date format** — How dates display throughout Beebole. * **Time format** — A 12-hour or 24-hour clock. * **Decimal format** — The decimal separator used in numbers. * **Thousands separator** — The separator used to group large numbers. A person can override their own language and some regional preferences from their profile. ### Show or hide by default The **Show or hide by default** panel decides which optional elements are switched on across the account. You can show or hide **Show all projects**, **Show all secondary projects**, **Show all tasks**, **Show all time off**, **Show all expenses**, and **Show all custom fields**. If you disable one globally, you can still enable it for specific tags, people, or projects under their own **Show or hide** settings. ### Timesheet and Planning Settings The **Timesheet and Planning Settings** panel controls the timesheet experience — default periodicity, duration format, time entry rules, the categories your team tracks against, and reminders. Because it is a deep configuration area, it has its own reference page. For the full list of timesheet options, see [Timesheet and Planning Settings](/help/documentation/timesheetSettings). ## Delete your account Admin only You can schedule your Beebole organization for deletion from the **Delete Account** menu item. Beebole does not delete everything instantly — it gives you a grace period so you can change your mind. Scheduling deletion removes your entire organization — people, projects, time entries, reports, and configuration — after the grace period passes. Export any data you need first. Open the user menu with your initials, then click **Delete Account**. Click **Yes, delete my account**. Beebole schedules the account for deletion and notifies all administrators by email. During the 7-day grace period, return to **Delete Account** and click **Cancel deletion** to keep your account. After the grace period, the data is permanently deleted. ## Related content Configure how your team logs time — periodicity, duration format, rules, and reminders. Control who can view and change account settings. Set up Single Sign-On and sign-in options for your organization. Manage your Beebole plan and billing details. ## Frequently asked questions Only administrators can open and edit Account Settings. The defaults set there apply to the whole organization, so changes are restricted to people with the Admin role. No. Beebole saves each Account Settings edit automatically as you make it — there is no separate Save button on the page. Yes, in part. A person can set their own language and some regional preferences in their profile, while account-wide defaults like currency apply to everyone unless overridden lower down. Yes, during the grace period. After you schedule deletion, Beebole waits 7 days before removing your data. Open **Delete Account** and click **Cancel deletion** within that window to keep your account. Yes. You can change the currency in the **Localization** panel at any time. Beebole does not convert existing values, so review your financial data after switching. # Time-off accruals: frequency and carry-forward Source: https://beebole.com/help/documentation/accruals Configure time-off accrual policies in Beebole: choose accrual frequency, set carry-forward limits, and control when allowances are awarded. Beebole's accrual policies let you define how time off accumulates for your team on a recurring schedule. Instead of granting the full annual allowance on day one, you can configure policies where leave builds up gradually — monthly, weekly, or at whatever frequency matches your company's rules. Accrued time is reflected on each person's allowance through its editable **Accrued** field. Accrual policies are linked to absence types. Each absence type can have its own accrual policy with independent settings for frequency, quantity, and award timing. *** ## How accruals work An accrual policy defines three things: 1. **How much** time off is awarded per period (the **Quantity**). 2. **How often** the accrual is awarded (the **Frequency**). 3. **When** within each period the accrual is credited to the person's balance (the **Awarded on** setting). The policy defines how much time a person earns as each accrual period completes. Earned time is reflected on the person's allowance through its **Accrued** field — an editable adjustment to the accrued balance that appears on their profile and in time-off reports. *** ## Creating an accrual policy Go to **Settings** > **Time Off** and click the absence type you want to configure accruals for. In the **Accruals** panel, turn on the toggle to reveal the accrual settings. Enter the number of hours or days to award per accrual period. This amount is added to each person's balance at the configured frequency. Select how often the accrual is awarded. Common options include monthly, quarterly, semi-annually, or annually. Choose when the accrual is credited within each period: * **First day of the period** — The balance is credited at the start. * **Last day of the period** — The balance is credited at the end. * **Day after the period** — The balance is credited on the first day of the next period. Enter the number of hours or days to award per accrual period in **Quantity**. Every change is saved automatically — the accrual policy is now defined for this absence type. Choose **first day of the period** if you want people to use their accrued time immediately. Choose **last day of the period** or **day after the period** if you want them to earn the time before they can use it. *** ## Carry-forward rules Carry-forward controls what happens to unused time off when an allowance period ends. It is configured on the allowance, not on the accrual policy: each allowance has a **Carry forward limit** field — the maximum number of hours or days that can roll over to the next period. The default is 0, so unused time off expires at the end of the period ("use it or lose it"). Any unused balance above the limit is forfeited. Go to **Settings** > **Time Off** and click the relevant absence type. Open the **Absence allowances** panel — on **Settings** > **Account Settings** for the organization default, or on a person or tag — and click the relevant allowance. In the accrual settings, enable carry-forward and set the maximum amount that can roll over (or leave it unlimited for full carry-forward). Enter the maximum amount that can roll over in **Carry forward limit**. The change is saved automatically and applies at the end of the allowance period. Changing the **Carry forward limit** mid-period may affect balances that have already rolled over. Review individual balances after making changes to ensure they reflect your intent. *** ## Frequency settings The accrual frequency determines the cadence at which time off accrues. Choose a frequency that aligns with your organization's leave policy: | Frequency | Accrual occurs | Best for | | ---------------- | --------------- | ---------------------------------------------------------- | | **Daily** | Once per day | Leave that builds up with every calendar day | | **Weekly** | Once per week | Hourly or shift-based teams that accrue with each pay week | | **Bi-weekly** | Every two weeks | Organizations on a bi-weekly payroll cycle | | **Semi-monthly** | Twice per month | Organizations that pay on the 15th and end of month | | **Monthly** | Once per month | Most common; gradual accumulation throughout the year | The total annual accrual should match the person's overall allowance for the year. For example, if someone gets 20 days per year with monthly accruals, set the accrual amount to approximately 1.67 days per month. *** ## Award timing explained The **Awarded on** setting controls the exact moment within each accrual period when the time is credited: * **first day of the period** — Time is available immediately when the period starts. For monthly accruals, this means the 1st of each month. * **last day of the period** — Time is credited on the final day of the period. The person earns the time at the end of the accrual window. * **day after the period** — Time is credited on the first day of the following period. This ensures the person has fully completed the period before receiving their accrual. Award timing affects when a person can use accrued time off. If you choose **day after the period**, January's accrual becomes available on February 1st — not during January. *** ## Viewing accrued balances You can check any person's accrued balance from their profile: Go to **People** and click the person's name. The **Absence allowances** panel shows each allowance with its **Available** and **Accrued** amounts. Open an allowance to also see **Consumed** — the time off recorded over the period. Balances update as time-off entries are recorded on timesheets and when the allowance's **Accrued** field is adjusted. *** ## Related content Configure absence types and allowances that accrual policies are linked to. Define non-working days that affect capacity and time-off calculations. Set working hours used to calculate day-based accrual amounts. *** ## Frequently asked questions Accrual policies are configured at the absence type level and apply to everyone with an allowance for that type. To give someone a different accrual rate, create a separate absence type with its own accrual policy. The updated policy applies from the next accrual period onward. Amounts already reflected in the **Accrued** field are not recalculated. Review individual balances to ensure they are correct after the change. Yes. Accruals and carry-forward work together. The accrual policy defines how time accumulates throughout the period, and the allowance's **Carry forward limit** determines how much unused time rolls over when the period ends. No. Accrual policies are optional and configured per absence type. You can have some types with accruals (e.g., vacation) and others without (e.g., sick leave with an unlimited or fixed annual grant). End the person's allowance: open the **Absence allowances** panel on their profile and set the allowance's **Start - end** period to finish on their last day. You can also archive their profile to free the seat — see [People](/help/documentation/people). # Beebole AI: suggestions, reports, and review assistance Source: https://beebole.com/help/documentation/ai Beebole AI drafts time entries from your activity, builds reports from plain language, and flags unusual timesheets before you approve them. Beebole AI is the set of AI features built into your Beebole account: time entries suggested from your own activity, reports built from a plain-language question, and an extra pair of eyes when you review a submitted timesheet. Everything lives on the **Assistant** page — click **Assistant** in the sidebar when AI features are enabled for your account. Beebole AI runs on Beebole's own AI models, self-hosted on Beebole-operated servers in the same region as your data. Nothing is sent to a third-party AI provider. *** ## Suggested time entries Beebole drafts time entries for you and shows them in the **Suggested entries** pane next to your timesheet — open it with the ✨ button in the timesheet's top-right corner. Each suggestion carries a badge naming its source: | Badge | Source | How it works | | ----------- | -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Habit** | Your logging patterns | After a few weeks of regular use, entries are mined from your own recurring logging habits. | | **Desktop** | The [desktop app](/help/documentation/desktop-app) | Time spent in applications and websites on your computer becomes draft entries. | | **Web** | The [browser extension](/help/documentation/browser-extension) | Time on websites you choose to watch becomes draft entries. | | **Planned** | Your planned work | Work planned for you — dates and share of your time set in [Planning](/help/documentation/planning) — becomes suggestions on the scheduled days. | | **Kanban** | The Kanban board | Moving a card to its done column proposes the matching entry — when [Auto Timesheet from Planning](/help/documentation/timesheetSettings#auto-timesheet-from-planning) is enabled. | The sources work together instead of competing. When two sources describe the same work, the suggestions merge into a single card showing every contributing badge, ranked so measured activity wins over assumptions. What you already logged by hand is subtracted, suggestions shrink to fit the room a day still has, and they never land on locked days, public holidays, full-day absences, or already-submitted periods. Planned suggestions rebuild each time you open the timesheet, and habit suggestions refresh daily, so the pane always reflects your current plans. Each card shows the project or task it is about, with that entity's picture and color rather than a generic icon, so you can tell at a glance what is being proposed. Suggestions are private — only you see them until you accept. For each one you can: * **Accept** it (or **Accept all**) — the entry lands on your timesheet. * **Dismiss** it. If a dismissed suggestion was hiding an overlapping one, the runner-up reappears. * Start it — the play button on today's suggestions accepts the suggestion and keeps a [timer](/help/documentation/timesheets#using-the-timer) running on it, in one gesture. It is available in the pane, on the calendar ghosts, and in the suggestion popup. * Click **Why?** to see the evidence: the applications and sites behind a **Desktop** or **Web** suggestion, the planned dates and share of your time behind a **Planned** one, or how many recent weeks a **Habit** was seen. Activity details are only readable where they were captured — elsewhere, Beebole tells you the details are only available in the desktop app or browser that tracked the activity. Accepting, editing, and dismissing are all undoable with **⌘+Z** (Ctrl+Z on Windows and Linux). Undoing an accept also removes the entries it created — or shrinks the entry it grew. In the timesheet's [calendar view](/help/documentation/timesheets#calendar-view), suggestions also appear as ghost entries: drag a suggestion to a time slot to schedule it, or accept it as-is. Drop one of your [favorites](/help/documentation/timesheets#favorites) onto a ghost and the suggestion is retargeted to that project or task and accepted in a single move — the ghost highlights as you hover it. ### Suggestions for a task you cannot book A suggestion can name a planned task your timesheet does not accept time on — a task in a planning left out of **Record time on these plannings** in [Timesheet and Planning Settings](/help/documentation/timesheetSettings). Beebole keeps the suggestion instead of dropping it, and leaves the task on the card so you still see what the proposal is about. To accept one, pick where the time should land: the card shows **Log this time as** with the note **Choose a project to accept it.**, and the entry is created on the project chain you choose. The link to the planned task is kept behind the entry, so the time still counts toward that task — in the [Planned vs. Real](/help/documentation/reports#planned-vs-real) report and in the approval check that flags a task over its planned time. **Accept all** skips these suggestions — each one needs your choice — and leaves them in the list for you to handle one by one. ### Planned work on future days **Planned** suggestions also reach beyond today. On days still in the future they appear as read-only forecast cards — a muted, dashed preview of what you are planned to work on — so you can see the shape of your week ahead. A forecast card becomes fully actionable, with accept, edit, and dismiss, once its day arrives. Reopening a past week that is still open — draft or rejected — regenerates its planned suggestions, so staffing added after the fact still shows up when you go back to that week. Activity captured by the desktop app stays on that device, is never shared with anyone, and is deleted after a week. Only the entries you accept reach your timesheet. Accepted entries record which source they came from and go through the normal entry path — the lock date, entry restrictions, and approvals all apply. If your organization enables [auto-submit](/help/documentation/timesheetSettings#auto-submit), suggestions still pending at the deadline are converted into real entries and submitted through the normal approval flow. *** ## Report builder Describe the report you need in plain language and Beebole creates and runs it for you. * On the **Assistant** page, use **Ask for a report** — the report opens ready to run. * On the **Reports** page, type what you want to know in the report builder. The result is a regular saved report, created in a folder named **AI**: refine its filters, move it to another folder, or delete it like any other. See [Reports](/help/documentation/reports). The report builder appears only for roles allowed to manage reports. ### It speaks your organization's vocabulary You do not have to describe your account's structure — the builder already knows it. It reads the names you gave your project and task categories and each of their levels, along with your custom fields, so a request like "hours by client last month" or "hours per cost center" groups the rows on that level of your hierarchy, or on the values of that custom field. Ask for "people and their Seniority year to date" and each axis becomes a column, in the order you named them. Names you mention are matched against the projects, people, tasks, and tags you are allowed to see; when one cannot be matched, Beebole tells you what it could not find instead of running the wrong report. With a report already open, the input sits under its title and your request changes that report: add a billing column, group by month, or turn the chart into a pie, and everything you did not mention stays as it is. Ask for a new or separate report and Beebole creates one instead. The button reads **Build** for a new report and **Update** when you are changing one. ### When Beebole asks instead of guessing Some requests cannot be expressed with the options a report has. Rather than return a plausible-looking wrong answer, Beebole replies with one short question. The most common case is grouping by a tag category: tags filter a report but are not a grouping axis, so "hours by department" comes back as a question about how you want the rows grouped. ### What reaches the model Only definitions are sent: your question, the structure of the report you have open, and the names of your categories, their levels, and your custom fields. Your time entries, the amounts in them, and the names of your projects, people, and tasks are not part of the request. The model only writes the report definition — every figure is computed by Beebole's own reporting engine, with your permissions applied, exactly as for a report you build by hand. *** ## Approval review When you review a submitted timesheet, anything unusual is flagged before you decide. The review digest checks the period for: * **Time on a non-working day** * **More time than scheduled** * **Time outside working hours** * **Time on an archived project** / **Time on an archived task** * **Entry right after the lock date** * **Task over its planned time** * **Period total far from usual** Approvers are emailed when a timesheet needs their review, with a summary of the submitted time and one-click **Approve** and **Reject** links. See [Approval workflows](/help/documentation/approval). *** ## Already working for you Some AI touches need no setup at all: timesheet reminders mention how many suggested entries are waiting, and how many hours they add up to — and when auto-submit is on, they announce the deadline in advance, so you know how long you have to review them. *** ## Connect your own AI tools Beebole also works with the AI assistant you already use. Connect Claude, ChatGPT, or another assistant to your Beebole data — with exactly your permissions — and manage every connection from the **Connected apps** list. See [the Beebole MCP server](/help/integrations/mcp-server) for setup. *** ## Related content Connect Claude, ChatGPT, and other assistants to your Beebole data. Draft time entries automatically from what you work on. Turn time on the sites you choose into draft entries. Where suggested entries appear and get accepted. ## Frequently asked questions No. Beebole runs its own AI models, self-hosted on Beebole-operated servers in the same region as your data, and nothing is sent to a third-party AI provider. What the model sees is also deliberately small. For the report builder it is only definitions — your question, the structure of the report you have open, and the names of your categories and custom fields — never your time entries, their amounts, or your project, person, and task names. Connecting your own assistant through Beebole's MCP server works the other way around. That assistant reads your Beebole data with your permissions, and only when you set the connection up yourself. Only you. Suggested entries in Beebole are private to you until you accept them — teammates, managers, and admins never see your suggestions or the activity behind them. From five sources: your own recurring logging patterns, the Beebole desktop app and browser extension if you install them, work planned for you in Planning, and Kanban cards you move to a done column. Each suggestion shows its source badge and a **Why?** explanation, overlapping suggestions merge into one card, and you decide what to accept or dismiss. Describe what you want to know — on the **Assistant** page under **Ask for a report**, or directly on the **Reports** page. Beebole builds the report and runs it; the result is a normal saved report in a folder named **AI** that you can refine and reuse. Use the words your organization uses: the builder knows your category names and levels and your custom fields, so "by client" or "per cost center" groups on that level or field. With a report open, the same input changes that report instead of creating a new one. If a request cannot be expressed with the options a report has, Beebole replies with one short question rather than guessing — grouping rows by a tag category is the usual case, since tags filter a report but are not a grouping axis. Answer in the same input and the report is built. It flags time on non-working days, overtime beyond the schedule, time outside working hours, entries on archived projects or tasks, entries right after the lock date, tasks over their planned time, and period totals far from that person's usual — all before you approve or reject. # Timesheet and time off approval workflows Source: https://beebole.com/help/documentation/approval Set up multi-stage timesheet approval in Beebole: configure approval stages and quorum, approve or reject submissions, and follow up with reminders. Beebole's approval workflow lets you review and lock your team's timesheets — work time and time off alike — before they feed reports, billing, and payroll. Approvers check each submitted period and either **Approve** it or **Reject** it with a comment, stage by stage, following the workflow you configure. Approval workflows are part of Beebole's paid plans. If no workflow is configured, timesheets are approved automatically the moment they are submitted. *** ## How approval works 1. A person completes their [timesheet](/help/documentation/timesheets) and clicks **Submit** at the top of the page. The timesheet locks and enters the first approval stage. 2. The stage's approvers are notified. Depending on the stage's quorum, either any one of them or all of them must approve. 3. Once the stage is complete, the timesheet advances to the next stage. When the last stage approves, the timesheet is **Approved**. 4. If any approver rejects — a comment is always required — the timesheet becomes **Rejected** and returns to its owner, who can correct it and click **Resubmit**. The timesheet's status badge shows where a period stands: | Status | Meaning | | ------------- | -------------------------------------------------------------------- | | **Draft** | Time entries can still be edited. The period has not been submitted. | | **Submitted** | The timesheet is locked and waiting in the approval workflow. | | **Approved** | Every stage has approved. The timesheet stays locked. | | **Rejected** | An approver rejected the timesheet. The owner can edit and resubmit. | Click the status badge to open the stage breakdown: each stage shows its approver type, who has already approved and when, and who is still pending on the current stage. A stage with no eligible approver — for example, **People managers** when the person has no manager — shows **No manager found** and is skipped. *** ## Configuring the approval workflow The workflow is a list of sequential stages. It lives in the **Approval workflow** panel, which exists at three levels: your organization's **Account Settings**, every tag, and every person's profile. Like other cascading settings, the most specific level wins — a workflow set on a person overrides one set on their tags, which overrides the account default. An icon next to the panel shows where the current workflow is inherited from. Click the button with your initials at the bottom of the left sidebar. Click **Account Settings** to open your organization's settings. To define a workflow for one team or one person instead, open the same panel on a tag or on a person's profile. If no workflow exists yet, the panel explains that timesheets are automatically approved when submitted. Click **Add**. A new stage appears with **Project managers** as its type and **Any** as its quorum. Pick who approves at this stage (see the table below) and whether **All** approvers or **Any** single approver completes it. For **Specific people**, pick each approver with **Select person**; for **Tagged people**, pick one or more tags with **Select tag**. Repeat **Add** for each additional stage. Stages run in the order listed — use **Move up** and **Move down** to reorder them, or **Remove stage** to delete one. Every change in the panel is saved automatically — there is no save button. ### Approver types | Type | Who approves | | -------------------- | -------------------------------------------------------------------------------------------------------------------------- | | **Admins** | People with admin access. | | **Project managers** | The managers of the projects in the timesheet. A manager of a parent project also approves time logged on its subprojects. | | **People managers** | The managers of the person who submitted the timesheet. | | **Task managers** | The managers of the tasks in the timesheet. | | **Tagged people** | All people tagged with the selected tag. | | **Specific people** | The named people you select. | Beebole resolves approvers when the timesheet is submitted, based on its content. If a period contains entries on three projects, a **Project managers** stage includes the managers of all three. *** ## Reviewing and approving your team's timesheets Approvers can act from three places: the **Timesheet** page, the **Journal**, or directly from email. ### From the Timesheet page Managers and administrators see an approval button (**Approval**) in the cluster at the top-left corner of the timesheet grid, with a red badge counting submissions waiting for them. It opens the **Pending** pane: * The pane lists each timesheet waiting for your approval, with **Late** submissions grouped at the top. Turn on **Show all** to include every pending submission you are allowed to see, not just those waiting for you. * Click a person to open their timesheet for the submitted period. **Approve** and **Reject** buttons appear at the top of the timesheet whenever you are a pending approver — or an administrator. * Select several submissions with the checkboxes to act in bulk with **Approve** and **Reject**. The neighboring team button (**Team**) opens the **Team** pane, which lists your team members with their status for the period. People who haven't submitted yet have a **Remind** button (**Send reminder**) that emails them on the spot. Select people with the checkboxes — or **Select all** — and a bulk bar appears with **Approve**, **Remind**, and **Reject**, each followed by the number of people it applies to. Every button acts only on the people it can act on, so a mixed selection is safe: * **Approve** and **Reject** act on the selected timesheets submitted for the period on screen and waiting for you — as an administrator, on any submitted one. * **Remind** goes to the selected people who haven't submitted — their period is a draft, rejected, or empty. People whose timesheet is approved, or submitted and waiting for someone else, are never reminded. Once the reminders are sent, those people are deselected and the submitted ones stay selected for approval. The bar acts strictly on the period displayed: a person's submission from another period is never approved or rejected from here. In the **Team** pane, hold **⌘** while clicking the select-all checkbox to alternate between selecting everyone to remind and everyone to approve. ### From the Journal Click **Journal** in the left sidebar. Approvers see a banner at the top of the feed — **1 timesheet to approve**, or the total count — or **All caught up** when nothing is waiting. Expand the banner to review without leaving the page: * Each pending submission shows its total **Hours** and **Billing** amount — plus **Cost**, if your role can see costs. * Click a person's name to split their total by project, task, and absence type, or click **View records** for the day-by-day entries with their comments. * Approve or reject one row, or select several and act in bulk. Totals reflect timesheet entries you can see. Approve and reject act on the whole timesheet. ### From your email Approval notification emails include a summary of the submitted timesheet and **Approve** and **Reject** links that work straight from your inbox, without signing in. **Approve** applies the approval and opens the timesheet it acted on; **Reject** opens the same timesheet with the rejection dialog ready, since a reason is always required. Timesheet reminder emails likewise include a **Submit now** link when the period already holds entries, so your team can submit a completed period directly from the reminder. You can also decide by replying to the email — the approval email and the approval reminder both end with a note about it: * Reply with **approve** alone on the first line to approve. * Reply with **reject** followed by your reason — on the same line or the lines below — to reject. A **reject** with no reason changes nothing. Beebole reads the first word of your reply and also understands **accept**, **ok**, **yes**, **refuse**, and **decline**, in any of its languages. Before applying the decision, Beebole checks that the reply comes from the approver's own email address and that the sending mail server authenticated it. Whatever the outcome, a receipt lands in the same email thread: **Approved**, **Rejected**, or **Nothing changed** — the latter when the reason was missing, the sender couldn't be verified, or the timesheet is no longer pending your approval. A reply that doesn't start with a decision word is treated as a Journal message instead. *** ## Rejecting a timesheet Rejecting always requires an explanation. When you click **Reject**, a dialog opens with a comment box (**Enter a reason for rejection…**) — the **Reject** button stays disabled until you write one. The reason is delivered to the person and recorded in their Journal. A rejected timesheet unlocks for its owner. After correcting the entries, they click **Resubmit** and the workflow starts again from the first stage. Beebole records which entries were added, modified, or deleted since the previous submission, and uses that to spare approvers unnecessary work: a project or task manager who had already approved is only asked again if entries on their own projects or tasks changed. *** ## Time off approval Time off goes through the same workflow as work time. When someone records an absence on their timesheet and submits the period, the configured approval stages review it along with the rest of the timesheet. The absence counts against the person's allowance as soon as it is recorded — approval reviews and locks the entry but does not gate the balance. See [Time off](/help/documentation/timeoff) for absence types and allowances. *** ## Editing a submitted or approved timesheet Submitting locks a timesheet for its owner, but it doesn't have to be rejected to be corrected: | Status | Owner | Current-stage approver | Administrator | | ------------- | -------- | ----------------------------------------------------- | ------------- | | **Draft** | Can edit | — | Can edit | | **Submitted** | Locked | Can edit — the workflow restarts from the first stage | Can edit | | **Approved** | Locked | — | Can edit | | **Rejected** | Can edit | The rejecting approver can correct entries in place | Can edit | To edit on someone's behalf, open the **Pending** or **Team** pane and click the edit button (**Edit timesheet**) on the person's row. Their grid unlocks for you; the override switches off when you move to another person. Administrator edits never restart the workflow. While you are editing, the timesheet makes it impossible to forget whose entries you are touching: the grid or calendar and every row picker are washed in the same highlight tint, the edit button on the person's row fills with that color (**Stop editing this timesheet**), and an **Editing** badge appears next to the person's name at the top of the page. Click the badge or the edit button again to stop editing. The override is tied to that one person — opening another person's timesheet drops the tint and the badge along with it. The edit button is not limited to administrators — managers and approvers see it too, on the rows they are allowed to correct. Beebole decides row by row, following the table above: a timesheet that is already **Approved** stays locked for everyone but an administrator, a **Submitted** one opens only for an approver of the current stage, and the two restrictions below can take the button away entirely. Two settings in [Timesheet and Planning Settings](/help/documentation/timesheetSettings) narrow this further. **Only the owner or an admin can edit entries** removes the approver column above, so only the owner and administrators can change entries. **Only an admin can edit someone else's timesheet** leaves the owner's own editing untouched but reserves every edit on another person's timesheet for administrators. ### Force approve and reject Admin only Administrators can unblock the pipeline without reconfiguring it: they can **Approve** or **Reject** any submitted timesheet, even when they are not approvers for the current stage. An already-approved timesheet can also be rejected — by an administrator, or by someone who was an approver on its last stage — which sends it back to its owner. *** ## Reminders and notifications * **Remind approvers after** a number of **days if not yet approved** — found in the **Reminders** tab of the **Timesheet and Planning Settings** panel — automatically follows up on submissions still waiting for review. Set it account-wide in **Account Settings**, or override it on a tag or a person, just like the approval workflow itself. * The **Remind** button in the **Team** pane sends a one-off reminder to anyone who hasn't submitted. * Each person controls how they receive **Approval updates** — instantly or as a digest — in their notification settings. See [Notifications](/help/documentation/notifications). *** ## Approval history Every approval action is logged in the person's [Journal](/help/documentation/journal) feed: **Submitted for approval**, **Approved by** and **Rejected by** entries with the approver's name and the rejection reason, plus **Auto-submitted** and **Auto-approved** events, each tagged with its stage. For who approved what on the current submission, click the timesheet's status badge to open the stage breakdown. *** ## Related content Track time, submit your period, and review your team from the timesheet grid. Configure the timesheet period, auto-submit, entry restrictions, and reminders. Set up absence types and allowances, and record time off on the timesheet. Follow your team's activity feed, messages, and approval events. *** ## Frequently asked questions It depends on the approval workflow your administrator configured. Each stage names an approver type — admins, project managers, people managers, task managers, tagged people, or specific people — and Beebole resolves the actual approvers from your timesheet's content when you submit. Click the status badge on your timesheet to see each stage and who is pending. The approver must enter a reason, which Beebole sends to you and records in your Journal. Your timesheet unlocks so you can correct the entries, then click **Resubmit**. The workflow runs again from the first stage, but approvers unaffected by your changes are not asked to re-approve. Yes. The **Approval workflow** panel exists on the organization's **Account Settings**, on every tag, and on every person's profile. A workflow defined on a tag applies to everyone under that tag, and a workflow on a person overrides everything else — so each team can follow its own sign-off process. Beebole approves timesheets automatically the moment they are submitted. Add at least one approval stage in the **Approval workflow** panel to require a review before timesheets are locked. Yes. Absences recorded on the timesheet are submitted and approved together with the rest of the period, through the same approval stages. The time off counts against the person's allowance as soon as it is recorded, even before it is approved. # Assignments: control who has access to what Source: https://beebole.com/help/documentation/assignments Control which projects, time off types, and expense types each person can use in Beebole — set account-wide defaults and who has access to each item. Assignments in Beebole control which items each person can use: the projects they can log time against, the time off types and expense types they can pick, and the custom fields they see. Use them to keep timesheets short and relevant — each person only sees what applies to them. Assignments control which items are **available** to a person. What a person can **do** — view or edit data — is controlled by their role, covered in [Roles & Permissions](/help/documentation/roles-authorisations). ## How access control works Beebole resolves access from three places: | Layer | Where it lives | What it does | | --------------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------- | | Account-wide defaults | **Show or hide by default** in **Account Settings** | Decides whether each kind of item is available to everyone by default | | Item-side access | **Who has access?** on a project, time off type, expense type, or custom field | Adds people and tags to a hidden item, or excludes them from a visible one | | Person-side access | **Show or hide** on a person, tag, or project | The same relationships, edited from the other direction | The item-side and person-side panels edit the same underlying links — pick whichever direction is more convenient. All of these settings are saved automatically. ## Set the account-wide defaults Click the button with your initials at the bottom of the sidebar to open **Settings**, then click **Account Settings**. Open the **Show or hide by default** panel. Check or uncheck each option. Changes are saved automatically. | Option | What it controls | | ------------------------------- | --------------------------------------------------------------- | | **Show all projects** | Whether every project is available to everyone by default | | **Show all secondary projects** | Whether secondary projects are available by default | | **Show all time off** | Whether every time off type is available to everyone by default | | **Show all schedules** | Whether every work schedule is available to everyone by default | | **Show all expenses** | Whether every expense type is available to everyone by default | | **Show all tasks** | Whether every task is available to everyone by default | | **Show all custom fields** | Whether every custom field is available to everyone by default | When an option is disabled here, items of that kind are hidden from everyone until you grant access individually — per item under **Who has access?**, or per person or tag under **Show or hide**. For secondary projects, the account default can also be overridden per entity: on a specific project, person, or tag, choose **All secondary projects available** or **No secondary projects available** instead of following the default, or show and hide individual secondary projects one by one. Hold **⌘** and click any checkbox in the **Show or hide by default** panel to apply the same change to all options at once. ## Grant access per item with Who has access? Each project, time off type, expense type, and custom field has a **Who has access?** panel. Here is where to find it: | Item | Where to find it | | ------------- | -------------------------------------------------------------------------------------- | | Project | Click **Projects** in the sidebar, select the project, and open **Who has access?** | | Time off type | Go to **Settings** > **Time Off**, select the type, and open **Who has access?** | | Expense type | Go to **Settings** > **Expense Types**, select the type, and open **Who has access?** | | Custom field | Go to **Settings** > **Custom Fields**, select the field, and open **Who has access?** | | Work schedule | Go to **Settings** > **Work Schedules**, select the schedule, and open **Assigned to** | | Task | Click **Planning** in the sidebar, select the task, and open **Potential owners** | The panel lists who is linked to the item: * **Individually** — add specific people with the **Select person** selector. * **By tags** — add whole groups with the **Select tag** selector. Everyone tagged with that tag, or any of its descendants, gets access. The panel adapts to the account-wide default. When the default already makes the item available to everyone, the sections flip into exclusion lists — **Excluded individually** and **Excluded by tags** — so you list the people who should *not* have it. On a project, the panel also starts with a toggle that overrides the account default for that one project: **Available to everyone** or **Unavailable to everyone**. Expense types have an extra **Projects** section to limit an expense type to specific projects, and custom fields have **Projects** and **Tasks** sections for the same purpose. Time off types, expense types, and custom fields don't carry their own default toggle — their default comes from the **Show or hide by default** options above. Projects are the exception, with the per-project toggle. ## Edit access from the person or tag side with Show or hide The same links can be managed from the people side. Click **People** (or **Tags**) in the sidebar, select a person or tag, and open the **Show or hide** panel. For a person or tag it covers time off, expenses, work schedules, tasks, projects, secondary projects, and custom fields. A project's **Show or hide** panel covers secondary projects, expenses, and custom fields. Each section follows the account default, just like the item-side panel: **Show projects** lists the projects added for this person, while **Hide projects** lists exclusions when all projects are visible by default. The same pattern applies to **Show time off** / **Hide time off**, **Show expenses** / **Hide expenses**, **Show schedules** / **Hide schedules**, **Show tasks** / **Hide tasks**, and custom fields. Use this direction when onboarding one person onto several projects at once; use **Who has access?** when staffing one project with several people. ## Task assignment Tasks are independent planning items, separate from projects, and carry their own assignment attributes: * **Owner** — the single person responsible for the task, with their planned full-time percentage. * **Potential owners** — the people and tags the task is available to, managed like any **Who has access?** list with **Individually** and **By tags** sections. To set either one, click **Planning** in the sidebar, select the task, and open the **Owner** or **Potential owners** panel. See [Task Planning](/help/documentation/planning) for how owners and dates drive the Gantt and Kanban views. ## Which plannings a person can book Alongside the item-by-item assignments above, one setting decides which whole plannings a person may record time on: **Record time on these plannings**, in the **Categories** tab of the **Timesheet and Planning Settings** panel. Add a planning to the list with **Pick a planning**. See [Timesheet and planning settings](/help/documentation/timesheetSettings#plannings) for the setting itself. The list cascades like every other timesheet setting. The value on **Account Settings** applies to everyone, a value on a tag overrides it for everyone under that tag, and a value on a person overrides both for that one person — so you can let one team book time on a planning that stays out of everyone else's timesheet. Beebole enforces the list when an entry is saved, not only in the timesheet's pickers. Creating an entry on a task, or moving an existing entry to another task, checks the task's planning against the list of the person the entry belongs to — not the person doing the typing. An entry that breaks the rule is refused with *Your timesheet settings do not allow recording time on this task.* Because the check follows the timesheet's owner, a manager or admin recording time for someone else is held to that person's own list. Entries saved before you changed the list stay as they are and remain editable in every other way. ## Example: a client project for a five-person team Suppose the **Website Redesign** project for **Acme Corp** should only appear on five people's timesheets: 1. Click **Projects** in the sidebar and select the project. 2. Open **Who has access?** and switch the toggle to **Unavailable to everyone**. 3. Under **Individually**, add the five people — or tag them and add the tag under **By tags**. Only those five now see the project when they log time. Everyone else's project list stays uncluttered. ## Related content Define what each role can view and edit across Beebole. Create and organize the projects your team tracks time against. Group people with tags to grant access to whole teams at once. Configure time off types, allowances, and balances. ## Frequently asked questions Assignments decide which items are available to a person in Beebole — which projects, time off types, and expense types they can pick. Role permissions decide what they can do with data — view it or edit it, and for whom. Both apply at the same time. Open the project's **Who has access?** panel, set the toggle to **Unavailable to everyone**, then add the right people under **Individually** or a tag under **By tags**. Only the people you add see the project in Beebole. Yes. Tag the department's people, then add that tag under **By tags** in the item's **Who has access?** panel. Anyone tagged later inherits the access automatically, including through descendant tags. In Beebole's **Account Settings**, under **Show or hide by default**. Each option — projects, secondary projects, time off, expenses, work schedules, tasks, and custom fields — decides whether that kind of item is available to everyone or hidden until granted. No. Beebole saves changes in **Who has access?**, **Show or hide**, and **Show or hide by default** automatically as you add or remove people, tags, and options. # Audit trail: track every change in your account Source: https://beebole.com/help/documentation/audit-trail Beebole's audit trail records who changed what and when, surfaced in the Journal feed and as a Logs view on individual records. Beebole's audit trail records every change made to your account's data — who made it and when. It gives administrators an accountable history of edits across people, projects, tasks, time, and configuration, so you can trace any modification back to a person and a moment in time. The audit trail is part of higher-tier subscriptions and is not available on the entry-level plan. If you don't see it, check your [subscription](/help/documentation/subscription). ## What the audit trail records Each audit trail entry captures the operation that ran, who performed it, when it happened, and the values that were submitted with the change. | Detail | Description | | -------------------- | ------------------------------------------------------------------ | | **Operation** | The action that ran, such as adding, editing, or removing a record | | **Person** | Who performed the action | | **Timestamp** | When the action occurred | | **Submitted values** | The argument names and values passed with the operation | Beebole records the values submitted with a change — the new state, not a before-and-after comparison. So the audit trail tells you what was set and by whom, rather than displaying the prior value side by side. ## View audit activity in the Journal The richest view of audit activity is the **Journal** feed. Audit messages appear there alongside other account activity, newest first. Click **Journal** in the sidebar. Browse the messages in chronological order to see recent changes across the account. Use **Hide similar entries** to collapse repetitive audit messages of the same kind, so a bulk change doesn't flood the feed. ## See the change history on a record Individual records carry their own change history. When you view a project, person, task, or other record, Beebole shows who last modified it next to a **Modified by** label. Click the **Logs** link to expand the full history for that record. Each line lists an operation, who performed it, and how long ago — giving you a focused history without opening the whole Journal. Use the **Logs** view on a single record to confirm who made a specific edit, then switch to the **Journal** feed when you need the wider picture across the account. ## Related content Control who has admin access to view audit activity. Export your organization's data for reporting and record-keeping. ## Frequently asked questions No. Beebole records the values submitted with each change — the new state — along with the operation, who ran it, and when. It does not display a before-and-after comparison of the field. In two places. The **Journal** feed shows audit messages across the whole account, and the **Logs** view on an individual record shows that record's own change history. Only changes. Beebole's audit trail records actions that modify data, such as adds, edits, and removals. It does not track who viewed or accessed a record. Yes. Changes a person made stay in the audit trail after that person is archived, and their name still appears next to their actions. No. The audit trail is part of higher-tier Beebole subscriptions and is excluded from the entry-level plan. Review your [subscription](/help/documentation/subscription) if it isn't available. # Sign-in, passkeys, SSO, and API keys Source: https://beebole.com/help/documentation/authentication How signing in to Beebole works: one-time email codes, passkeys, Google and Microsoft sign-in, single sign-on (SSO), API keys, and admin sign-in as. Beebole signs everyone in without passwords. Instead, you confirm your identity with a one-time code emailed to you, a passkey, your Google or Microsoft account, or your organization's single sign-on (SSO) provider. Because there is no password to create, forget, or reset, there is nothing for an attacker to steal or reuse. Every sign-in method matches against the email address on a person's Beebole profile. To sign in, that email must already belong to an active person in your Beebole account — unless your organization enables automatic user creation for SSO. ## Sign-in methods Beebole offers four ways to sign in. A person can use whichever is most convenient on a given device. Every method also works for the [desktop app](/help/documentation/desktop-app): signing in there hands off to your browser to complete the sign-in — passkeys, Google or Microsoft, and SSO included — and returns you to the app signed in. | Method | How it works | Setup needed | | ------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------------------- | | Email code | Beebole emails a one-time 6-digit code; you enter it to sign in | None — available by default | | Passkey | Your device unlocks sign-in with a fingerprint, face, or hardware key | Enable once from your user menu | | Google or Microsoft | You sign in through your existing Google or Microsoft account | None for the person; for Google, an admin can link the domain for SSO enforcement | | SSO | You sign in through your organization's identity provider | An admin configures it on **Account Settings** | ### Email code The email code is the default method. Enter your email address on the Beebole sign-in page, and Beebole sends a one-time 6-digit code to your inbox. Enter the code to finish signing in. The code is single-use and expires after 5 minutes — request a new one if it does not arrive, and check your spam folder. ### Passkey A passkey signs you in with your device's built-in security — a fingerprint, face recognition, or a hardware security key — so you skip the email code entirely. Passkeys are phishing-resistant because the credential never leaves your device. Enable a passkey from your user menu: Click the button with your initials at the bottom of the sidebar. Click **Enable passkey** and follow your browser's prompts to register a fingerprint, face, or hardware key. **Enable passkey** appears in the user menu only on devices that support passkeys and only when your account does not already have one registered. ### Google and Microsoft sign-in You can sign in with an existing Google or Microsoft account, which is convenient for teams already using Google Workspace or Microsoft 365. Beebole matches the Google or Microsoft account's email address to your Beebole profile, so the two must use the same email address. ## Single sign-on (SSO) Single sign-on lets your whole team authenticate through your organization's identity provider using OpenID Connect. An administrator configures it on the **Single Sign-On** panel of **Account Settings**. Beebole supports three providers, each on its own tab: **Google**, **Microsoft**, and **Custom OpenID** (for providers such as Okta, Auth0, or Microsoft Entra ID). Open the **Single Sign-On** panel from **Account Settings** — the page you reach through **Settings** > **Account Settings**. SSO is configured on the organization itself, not as a separate menu item. All SSO settings save automatically as you change them. The **Custom OpenID** tab is available on higher-tier plans. If your plan does not include it, only the **Google** and **Microsoft** tabs appear. See [Subscription & Billing](/help/documentation/subscription) for what each plan includes. ### Google SSO On **Account Settings**, open the **Single Sign-On** panel and select the **Google** tab. Type your organization's email domain in the **Linked domains** field and click **Add**. People whose email matches a linked domain are sent to Google sign-in. Click **Remove** next to a domain to unlink it. Turn on **Automatically create new users on first SSO login** to create a person automatically the first time someone with a matching domain signs in. Turn on **Disable interactive sign-in. Only Google sign-in allowed.** to block the email-code method for your organization. ### Microsoft SSO On **Account Settings**, open the **Single Sign-On** panel and select the **Microsoft** tab. To require everyone to authenticate through Microsoft, turn on **Disable interactive sign-in. Only Microsoft sign-in allowed.** ### Custom OpenID The **Custom OpenID** tab connects any OpenID Connect-compatible provider. On **Account Settings**, open the **Single Sign-On** panel and select the **Custom OpenID** tab. Fill in the **Issuer URL**, **Client ID**, and **Client Secret** from your identity provider. If your provider does not support OpenID Connect discovery, turn on **Other provider** and enter the **Authorization URL** and **Token URL** by hand instead of the issuer. Copy the **Initiate URI** and **Callback URI** shown in Beebole (each has its own **Copy** button) and paste them into your identity provider's application settings. Turn on **Disable interactive sign-in. Only this SSO provider allowed.** to block all other sign-in methods. Before turning on a **Disable interactive sign-in** toggle, confirm SSO works by signing in as a test user. If your identity provider becomes unavailable while enforcement is on, your team is locked out until you turn the toggle back off. ## API keys Each person has one API key for Beebole's [GraphQL API](/help/api/introduction). Beebole creates the key automatically the first time you open it — there is no key to add, and no expiration to set. Click the button with your initials at the bottom of the sidebar. Click **Your API key**. Your key is shown, partly masked. Click **Copy** to copy the full key to your clipboard. Click **Reset** to revoke the current key and generate a new one. Treat your API key like a credential — never share it or commit it to source control. If it is exposed, click **Reset** to revoke it and issue a new one immediately. Any integration using the old key stops working until you update it. ## Sign in as another person Administrators can sign in as another person to troubleshoot a problem or confirm what that person sees. You start this from your own user menu, not from the other person's profile. Click the button with your initials at the bottom of the sidebar. Click **Sign in as…**. A search box opens listing the people in your account — pick the one you want. Click **Stop** to end the session and return to your own account. Sign-in-as activity is recorded in the [audit trail](/help/documentation/audit-trail), attributed to the administrator who started it. The option appears only for people with the Admin role. ## Related content Configure your organization, including the Single Sign-On panel. Control what each person can see and do in Beebole. Review a log of actions taken in your account, including sign-in-as. Authenticate to the Beebole GraphQL API with your API key. ## Frequently asked questions Beebole has no passwords. You sign in with a one-time 6-digit code sent to your email, or more quickly with a passkey, your Google or Microsoft account, or your organization's SSO provider. With no password to steal or reuse, there is nothing to forget or reset. Yes. As long as your organization has not turned on a **Disable interactive sign-in** toggle, you can sign in with the email code, a passkey, or a linked Google or Microsoft account — whichever is handiest on the device you are using. On **Account Settings**, open the **Single Sign-On** panel, select your provider's tab, and turn on its **Disable interactive sign-in** toggle. This blocks the email-code method so everyone must authenticate through the configured provider. Confirm they are using the exact email address on their Beebole profile, and ask them to check their spam folder for the 6-digit code. As an administrator, you can use **Sign in as…** from your user menu to see Beebole as they do. One. Beebole creates a single API key per person automatically and offers **Copy** and **Reset** actions for it. Resetting revokes the old key and issues a new one — there are no multiple keys and no expiration dates to manage. # Billing rates: turn tracked time into revenue Source: https://beebole.com/help/documentation/billing Set billing rates in Beebole at the organization, tag, project, or person level and turn tracked hours into billable amounts in reports. Beebole's billing rates turn tracked time into revenue figures. You define what an hour, a day, or a piece of work is worth, and Beebole calculates billable amounts from the time your team logs — ready to analyze in reports and to measure against budgets. Billing rates are available on paid Beebole plans. Who can view and edit them is controlled by the **Billing rates** permission — see [Roles & permissions](/help/documentation/roles-authorisations).