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 instead. For authentication setup, see the API introduction.
id (not _id), and the organization-level query is spelled currentOrganisation (British spelling, as in the schema).
Organization
Read account-level data withcurrentOrganisation and getAuditTrails.
getAuditTrails returns the changes recorded in the account’s audit trail. It works only for administrators, on plans that include the audit trail (not the Free plan). With no startTime, it covers the last seven days; a startTime returns the seven days that follow it. fromPersonId keeps the changes one person made, and affectedPersonId keeps the changes made to one person’s time entries, whoever made them.
People
Read team members withcurrentPerson, getPerson, getPersons, countPersons, and filterPersonIds. getPotentialProjectOwners lists the people who have access to a project.
getPersons returns only active people. Pass archived: true to include archived ones.
List the people who have access to a project
BeebolePersonFilter has no project field, so read a project’s members with getPotentialProjectOwners. It resolves the project’s Who has access? panel: when the project is restricted, it returns the people added individually plus the people carrying one of the tags added; when the project is available to everyone, it returns every active person except the ones excluded individually or by tag.
Projects
Read projects withgetProject, getProjects, countProjects, and filterProjectIds. getProjectCategories lists the project categories projects are organized into, such as Client or Internal.
level field is the project’s depth in the hierarchy, where 0 is a top-level project. Restrict results to one category with categoryId:
Tasks
Read tasks withgetTask, getTasks, countTasks, and filterTaskIds. getTaskCategories lists the task categories, which Beebole calls plannings.
On
getTasks and countTasks, startTime and endTime (millisecond timestamps) keep the tasks whose dates overlap the window: a task is returned when it ends at or after startTime and starts at or before endTime, so a task that runs across either edge of the window is included.
Tags
Read tags withgetTag, getTags, countTags, and filterTagIds. getTagCategories lists the categories tags are grouped into.
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 withgetTimeRecord, getTimeRecords, and countTimeRecords. getAbsenceQuotaConsumed returns how much of each of a person’s time off allowances they have used.
getTimeRecords and countTimeRecords accept top-level arguments alongside filter:
startTime/endTime: Unix timestamps in milliseconds bounding the period. Both apply to the record’s start, since a record belongs to the day it starts on: a record is returned when it starts betweenstartTimeandendTime, both included. To cover a whole month, end on its last millisecond (1706745599999for January 31, 2024).time: whentrue, return only worked time (entries without an absence type).absence: whentrue, return only absence entries (entries with an absence type).wfh: whentrue, return only entries logged as working from home.filter: a typed filter array (see Filtering results).
duration is a number of 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.
getAbsenceQuotaConsumed takes the IDs of the person’s allowances, which you read from the person’s absenceQuotas attribute (it includes allowances inherited from tags and the organization):
0.
Find the most recent entry on a project
getTimeRecords has no sort or limit argument and returns its records in no particular order. To find the last time someone logged time on a project, filter on the project and the person, keep the window short, and take the record with the highest startTime.ts:
projectIds also matches the entries on the project’s subprojects, and leaving out the personId condition finds the last entry anyone logged on the project.
Timesheets and approvals
A timesheet is one person’s time records over one timesheet period, and its approval status — draft, submitted, approved, or rejected — belongs to the period, not to the individual records. Time records carry no status field, andgetTimeRecords ignores the status input that the schema lists on its filter type. Read approval data with these queries instead:
The
status field on approval states and events uses one-letter codes: d (draft), s (submitted), a (approved), and r (rejected). An approval event can also carry f, which marks a period that auto-submit could not process: the period stays a draft.
List timesheets awaiting approval
Check whether a period is still a draft
status: "d". To find every draft in a team, call getTeamApprovalStates with the team’s personIds and keep the entries whose status is d. The submitId of a submitted period is what approveTimesheet and rejectTimesheet expect — see Mutations.
Expense records
An expense record is an expense logged by a person against a project and an expense type. Read them withgetExpenseRecord and getExpenseRecords.
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 (an ISO 4217 code).
Billing and cost rates
A billing or cost rate is a dated amount set on the organization, a tag, a person, or a project, as described in Billing and Costs. Read rates through thebillings and costs fields of currentOrganisation, tags, people, and projects. Each returns value, the list of rates that applies to the record (its own, or when it has none, the ones it inherits from a tag, a parent, or the organization), and path, the chain from the record you asked for to the one the rates are set on.
path with a single entry means the rates are the record’s own; otherwise its last entry is the tag, parent, or organization that holds them. Each rate’s startTime is its From date, method is hourly, daily, fixed, or nonBillable, and amount.value is an integer in the currency’s smallest unit. When splitType is set (persons, projects, or tags), personSplit, projectSplit, or tagSplit lists the separate amounts. The rate id is what the rate mutations, such as editBillingAmount, expect.
Time records carry no amount, only their nonBillable flag. Beebole computes billing and cost in reports, from the rate that covers each entry’s date.
Absence types
Absence types are the kinds of leave people can log against, such as vacation or sick leave. Read them withgetAbsenceType, getAbsenceTypes, and countAbsenceTypes.
unit is day or hour.
Custom fields
Custom field definitions and the values stored on records are separate entities. Read definitions withgetCustomField, getCustomFields, and countCustomFields; read values with getCustomFieldValue, getCustomFieldValues, and countCustomFieldValues.
A value’s
entityType is one of Person, Project, Task, Tag, TimeRecord, or AbsenceType, and entityId is the record it sits on. The definition’s fieldType is text, number, date, datetime, url, boolean, or uniqueId, and the value itself lives in the matching field: textValue (for text and uniqueId), numberValue, dateValue, dateTimeValue, urlValue, or booleanValue. The person, project, task, tag, timeRecord, and absenceType fields resolve the owning record inline — only the one matching entityType is set.
Look up records by an external identifier
Beebole has no built-in external ID attribute: an identifier from another system lives in a text or Unique ID custom field (a migrated legacy account gets a text field named External ID). To find the record that carries a given identifier, filter the field’s values oncustomFieldId and textValue. The textValue filter matches the whole stored value, ignoring upper and lower case and any spaces around it:
entityId is the Beebole record, and person resolves it inline when entityType is Person (use project, task, and the other entity fields for other types). On a Unique ID field, at most one record matches. To read the values on one known record instead, filter by entityType and entityId. Writing values is covered under Custom fields on the Mutations page.
Filtering results
Thefilter 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.
OnlyOnePropertyPerFilterAllowed. To combine conditions, add a following value of AND or OR to chain one condition to the next, and leave following off the last object (an object without it anywhere else fails with IncorrectFilter). AND binds before OR, so A AND B OR C reads as (A AND B) OR C:
Time record filter fields
BeeboleTimeRecordFilter accepts these fields (each optional):
The schema also lists
isAbsence, status, and billable on this input, but getTimeRecords and countTimeRecords don’t filter on them: status and billable are ignored, and isAbsence matches no record. Separate worked time from absences with the top-level time and absence arguments, and read approval status with the queries under Timesheets and approvals.
Other filter types
Every other entity exposes a filter input tuned to its own fields. All fields are optional,name is a case-insensitive regular expression (so Acme also matches Acme Corp), and following chains conditions the same way.
For example, the projects that have a given person among their managers:
Counting and ID-only queries
For most entities (expense records are the exception), alongside the fullget… query you also get a counterpart that returns just a count, and (for people, projects, tasks, and tags) one that returns only matching IDs.
- The
count…queries (countPersons,countProjects,countTasks,countTags,countTimeRecords,countAbsenceTypes,countCustomFields,countCustomFieldValues) return an integer. Use them to size a result set before fetching it. - The
filter…Idsqueries (filterPersonIds,filterProjectIds,filterTaskIds,filterTagIds) return an array of IDs instead of full objects, which is lighter when you only need identifiers.
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
API Introduction
Authenticate with an API key and send your first request.
Mutations
The write operations that create, update, archive, and delete records.
Schema explorer
Browse every query, type, field, and filter input interactively.