BigTime's time entry api provides access to user's timesheet data -- allowing you to create programs and web services that query, create or edit a user's time entry data without relying on the BigTime web pages.
As long as time entries have not yet been submitted by the user, they can be deleted or adjusted. Once an entry has been submitted, however, time entries can no longer be edited by a non-admin user. While you can "view" those entries, they are locked by the system (unless the logged in user has special administrative rights). So, as you are reviewing a user's information, it's important to remember that only unsubmitted time entries can be edited.
Also, keep in mind that time entries can change their ID value as they move through the system (typically, when they cross the submitted/unsubmitted threshold). So, an entry with an "SID" value of -123, may have a new SID value of 456 once a user clicks the SUBMIT button. While that issue won't typically create problems for you as you use the api, it could lead to errors if multiple API-driven applications are manipulating the same set of time entries (or if the user submits their time in between your query and your update). The best way to avoid this type of error is to requery a time entry before attempting to update it.
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} HTTP GET: /time/Sheet/{staffsid}?StartDt={YYYY-MM-DD}&EndDt={YYYY-MM-DD}&View={Detailed} HTTP RESPONSE: (array of TimeEntry objects -- see below for details)
Parameter | Type | Description |
---|---|---|
StaffSid | Numeric | Specifies an alternate staffsid for whom you would like to pull time entry details. Note that the logged in user will need to have admin rights in order to pull data for another user (if they don't -- then the url will return their own personal data and disregard this parameter). |
StartDt | Date | Starting date for the timesheet data. This MUST be specified or the request will return an error. |
EndDt | Date | Ending date for the timesheet data. This MUST be specified or the request will return an error. |
View | Basic; Detailed | Take a look at the time entry object details below. For each field, the "views" within which that field appears are flagged. The Detailed view requires more server resources and, as a result, will return more slowly. |
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} HTTP GET: /time/ByProject/{Id}?StartDt={YYYY-MM-DD}&EndDt={YYYY-MM-DD}&View={Detailed}&[IsApproved=TRUE/FALSE] HTTP RESPONSE: (array of TimeEntry objects -- see below for details)
Parameter | Type | Description |
---|---|---|
Id | Numeric | Specifies a projectSid for which you would like to pull time entry details. Note that the logged in user will need to have rights to the project in order to pull data for it. Otherwise, a 405 error will be returned. |
StartDt | Date | Starting date for the timesheet data. This MUST be specified or the request will return an error. |
EndDt | Date | Ending date for the timesheet data. This MUST be specified or the request will return an error. |
View | Basic; Detailed | Take a look at the time entry object details below. For each field, the "views" within which that field appears are flagged. The Detailed view requires more server resources and, as a result, will return more slowly. |
IsApproved | TRUE/FALSE | This is an optional parameter (defaults to FALSE). If it's set to TRUE, then only approved time will be returned. |
You can use a simple GET request to pull the details associated with a single time entry item. Note that the DETAIL view fields will be returned by default.
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} HTTP GET: /time/{sid} HTTP RESPONSE: (TimeEntry object -- see below for details)
Note that only the fields listed in the AddUpdate view (below) should be included in your post to this url. Any other data will be ignored. BigTime doesn't care about the order in which field data is posted, and none of the fields are case sensitive. Note that any validation errors will return a 400 error, and an invalid permission will return a 405 (eg - attempting to create time against a project you don't have rights to log time against).
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} HTTP Post: /time [?MarkSubmitted=true/false] POST CONTENT: {"staffsid": 123, "projectsid": 123, "dt": "2013-01-01", "hours_in"": 1.25, "notes": "These are my time entry notes..."} You can include ANY of the AddUpdate fields below, but you MUST include the required (*) fields. HTTP RESPONSE: (updated TimeEntry object -- see below for details)
Note that only the fields listed in the AddUpdate and Update views (below) should be included in your post to this url. Any other data will be ignored. Note again, validation errors will return a 400 error, and an invalid permission will return a 405 (eg - attempting to create time against a project you don't have rights to log time against).
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} HTTP Post: /time/{sid} [?MarkSubmitted=true/false] POST CONTENT: {hours_in: 2.25, notes: "this is an updated note..."} ** You can include ANY of the AddUpdate/Update fields below. However - you cannot "zero-out" or remove any of the required (*) fields. HTTP RESPONSE: (updated TimeEntry object -- see below for details)
As long as the user hasn't submitted an entry, BigTime will allow you to delete it through the api.
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} HTTP DELETE: /time/{sid} HTTP RESPONSE: 200 is a successful delete.
For each of the operations listed above, you will post or "get" one or more time entry objects. The field list below is a complete list of fields available through the api (including the views within which those fields are found). Take special note of the AddUpdate and Update views. Required fields are flagged with an "*" character.
Field | Type | Description | View(s) |
---|---|---|---|
SID | Integer | Unique SID code for this time record. Note that this value is a negative number for any unsubmitted time and a positive number once the entry has been submitted. | Basic, Detailed, Update*, Timer |
IsNew | True/False | If this value is TRUE, the time entry we are examining has not yet been submitted by the user. Note that updates to time entries can pass a MarkSubmitted querystring parameter of "true" which will automatically submit an entry that is unsubmitted (once it is saved successfully). | Basic, Detailed |
Dt | Date | Time entry INPUT date (eg - the date against which the time is logged, not the date the user physically entered it). | Basic, Detailed, AddUpdate* |
ProjectSID | Integer | The unique "system id" for the project against which this time is logged. Note that this value is required. | Basic, Detailed, AddUpdate*, Timer |
ProjectNm | String | The "display" name for the project against which the time is logged. | Detailed, Timer |
ClientID | Integer | Each project in the system can be attached to a client record, and this convenience property displays the clientID for the project to which a time entry is attached. | Basic, Detailed, Timer |
ClientNm | String | Name of the client to which the entry is attached. | Detailed, Timer |
ProjectLinkValue ProjectLinkType |
String Enum |
If you know a project's full displayName or it's Id, but not the project's SystemId, then you can still create a time entry. Just use these 2 alternate properties instead of the ProjectSid field and BigTime will automatically LOOKUP the correct ProjectSid value for you. ProjectLinkType Values: FindBySystemId(default) = 0; FindByProjectDName=2; FindByProjectCode=3; |
AddUpdate* |
StaffSID | Integer | Unique SystemId for the staffer whose timesheet the entry appears on. | Basic, Detailed, AddUpdate*, Timer |
StaffLinkValue StaffLinkType |
String Enum |
If you know a staff member's name or their email, but not the staff member's StaffSid, then you can still create a time entry. Just use these 2 alternate properties instead of the StaffSid field and BigTime will automatically LOOKUP the correct StaffSid value for you. StaffLinkType Values: FindByStaffSid(default) = 0; FindByFullName(First+Last)=1; FindByLastName=2; FindByEMail=3; |
AddUpdate* |
FName | String | FIRST(Given) name of the staff member to whom this entry is attached. | Detailed |
SName | String | Last(Family) name of the staff member to whom this entry is attached. | Detailed |
SourceNm | String | First+Last(Family) name(s) of the staff member to whom this entry is attached. | Detailed |
BudgCatID | Integer | LaborCode value for this time entry (note that the description of this field is governed by a firm's vocabulary). | Basic, Detailed, AddUpdate, Timer |
BudgCatNm | String | LaborCode value for this time entry (note that the description of this field is governed by a firm's vocabulary). | Detailed, Timer |
BudgCatLinkValue BudgCatLinkType |
String Enum |
If you know a labor code's name or the code, but not the systemId, then you can use these 2 alternate properties instead of the BudgCatID field and BigTime will automatically LOOKUP the correct BudgCatID value for you. BudgCatLinkType Values: FindBySystemId(default) = 0; FindByName=1; FindByCode=2; |
AddUpdate* |
TaskSID | Integer | The SystemId for the Task to which the time entry is attached (note that the description of this field is governed by a firm's vocabulary. May also be called an engagement or a phase). | Basic, Detailed, AddUpdate, Timer |
TaskNm | String | The name of the Task to which the time entry is attached (note that the description of this field is governed by a firm's vocabulary. May also be called an engagement or a phase). Specifying Task Name for Add/Update Requests If you know the NAME of the task you would like to link an entry to, but not the BigTime TaskSID code, then you can specify a TaskNm value in your addUpdate request and BigTime will attempt to match your task to the string value you specify. Note that task names should, in that case, be unique (the system will not do a partial/wildcard match). If Both TaskSid and TaskNm are specified, then the system will use the TaskSid value. |
Detailed, Addupdate,Timer |
QBClass | Integer | The SystemId for the QuickBooks Class to which the time entry is attached. | Basic, Detailed, AddUpdate, Timer |
QBClassNm | String | The QuickBooks Class name to which the time entry is attached. | Detailed, Timer |
PayrollItem | Integer | The SystemId for the payroll item to which the time entry is attached. | Basic, Detailed, AddUpdate, Timer |
PayrollItemNm | String | The payroll item name to which the time entry is attached. | Detailed, Timer |
Hours_IN | Numeric | Every time entry must have a value for the actual TIME logged. For the API, that time value is expressed in terms of hourly fractions (eg - 1:10 is express as 1.1; 1:30 is expressed as 1.5). | Basic, Detailed, AddUpdate*, Timer |
Notes | String | User entered notes for the time entry (up to 1000 characters). | Basic, Detailed, AddUpdate, Timer |
RevisionNotes | String | If this time entry has been rejected/revised by a manager -- this this field contains that manager's notes. | Detailed, AddUpdate |
AuditLogNote | String | If a time entry is revised AND the timesheet audit-log is active, then the value passed to this field will be logged in the entry's audit-log as the "explanation" of the change. For typical audit log setup, user notes are required for (a) time logged outside of the current calendar date and (b) time that is entered and then subsequently revised (often, the latter requirement is relaxed if an entry is revised on the same calendar date .. so users can correct mistakes without comment as long as they are corrected on the correct calendar date). Note that this field is ignored if no audit log note is required. | AddUpdate |
NoCharge | True/False | TRUE if this entry should be considered NoCharge for billing purposes. | Basic, Detailed, AddUpdate, Timer |
IsApproved | True/False | TRUE if this entry has been approved by the user's manager(s). Note that many firms don't have a formal review process for time entries -- so this value will always be "false" for those firms. | Detailed |
CanApproved | True/False | TRUE if the currently logged in user has rights to approve this specific entry. | Detailed |
ApprovalStatus | Number | Number indicating the status of the time entry (unsubmitted=0, readyToReview=1, approved=2, rejected=4). | Detailed |
ApprovalStatusNm | String | If the firm uses a formal manager approval process for time, then you can use this field to track the current status of the entry. This is the text version of ApprovalStatus -- so it will display "Unsubmitted", "Under Review", "Approved" or "Rejected". | Detailed |
ApprovalInfo | Object | For firms that use multi-level approvals, status can be more complicated. For these firms, the detail object contains a full outline of approval status for each "stage" of approval. | Detailed, AddUpdate |
InvoiceSID | Integer | If the firm is using BigTime to invoice, then this field indicates the invoice against which the time entry was logged. | Detailed, AddUpdate |
IsInvoiced | Boolean | If the firm is using BigTime to invoice, then this field will be TRUE if the time entry has already been invoiced. | Detailed |
HoursBillable | Numeric | Once time is submitted, it can have BOTH an input hours column AND a billed hours column (eg - a firm may only bill part of the hours input by the staffer). See notes on Hours_IN for details on the data format for this column. | Detailed, AddUpdate* |
BillRate | Numeric | Billing rate (applied to an entry on (a) submission or (b) creation of an invoice). | Detailed, AddUpdate |
ChargeBillable | Numeric | Typically HoursBillable*BillRate (NoCharge hours always have a chargable amount of $0). | Detailed, AddUpdate* |
BillingRateLocked | Boolean | TRUE if the system has marked this billing rate as "locked" (so it won't be updated when a project or staffer rate is updated). | Detailed, AddUpdate |
CostRate | Numeric | The labor "cost" rate -- applied by the system when a user SUBMITS an entry. | Detailed, AddUpdate |
DtCreated | Numeric | Read-only value indicating the UTC date/time on which the time entry was initially entered into the system. Returned as a UNIX timestamp value (number of seconds since 1/1/70). | Basic, Detailed |
DtModified | Numeric | Read-only value indicating the UTC date/time on which the time entry was last modified. Returned as a UNIX timestamp value (number of seconds since 1/1/70). | Basic, Detailed |
Pass a date and the system will return the weekly start date that CONTAINS that date (based on the user's firm settings).
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} HTTP GET: /time/periodContainingDate/{yyyyMMdd} HTTP RESPONSE: date in yyyy-MM-dd format.
You can use a simple GET request to pull the custom field details associated with a single time entry item.
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} HTTP GET: /time/customFields/{sid}&{getValues} HTTP RESPONSE: (A JSON list of all custom fields related to the time entry. Also if a flagged to return values it will return a second list with the value and mapping.)
Field | Type | Description |
---|---|---|
EntityType | Integer | The entity type enum, should be 4 for time |
EntityType_Nm | String | Label for entity type, this should say time. |
UDFSid | Integer | The unique identifier for a custom field. |
UDFNm | String | The name of the the custom field. |
UDFLabel | String | The label of the custom field visible in the UI. |
UDFLen | Integer | The max length of the user defined field. This defaults as 0 if there is no max length or if max length does not apply. |
UDFDefault | String | The default value of the user defined field. |
UDFLookup | String | A string of values that represent the picklist for the UDF. Each value is seperated by a new line. |
IsSystem | true/false | This is true if this is a bigtime created value, false if the UDF was created by the user. |
IsRequired | true/false | Marks if the custom field is required to be filled in when a user creates a timesheet entry. |
SortOrd | Integer | The order in which the custom fields are displayed in a timesheet, lowest to greatest being the last custom field. |
ImportId | Integer | This identifies if the field was created by an import. |
UDFHtmlInput | String | This is not used. |
Types | List | This is not used. |
ErrorList | List | This will the display the most recent error if any while saving the udf. |
Field | Type | Description |
---|---|---|
StrVal | String | The value of the user defined field. |
EntityId | Integer | This should match the unique identifer of the time entry queried. |
EntityType | Integer | The number representation of the ENum, this should match back to the type in UDFList. |
Label | String | The label the user sees this field as in the UI. |
UDFNm | String | The name of the user defined field stored in bigtime. |
UDFSid | Integer | The unique identifier of the user defined field, this can be found in UDFList to match. |
ValueType | String | This is the type of the user defined field. |
ErrorList | List | This is a list of any errors caused during the save. |
You can use a simple POST request to push a custom field detail associated with a single time entry item. The JSON should contain a JSON array of custom fields you would like to post, this array should have three properties inside of it, EntityID, UDFSid, and StrVal. The EntityID should be the time id, UDFSid should be the custom fields id you would like to post to, and StrVal should be the string value you would like the custom field to be set to. NOTE: The time id should be negative if unsubmitted and postive if querying for submitted time. However, the EntityID field should ALWAYS be a positive number.
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} HTTP POST: /time/customFieldsPost/{sid} HTTP RESPONSE: Results: true if successfully saved.
Pass a date range and the system will return a single object for each date during that period that the user has time entry data.
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} HTTP GET: /time/TotalByDay/{staffsid}?StartDt={YYYY-MM-DD}&EndDt={YYYY-MM-DD} HTTP RESPONSE: array of DailyTotal objects (see below).
Field | Type | Description |
---|---|---|
staffSid | Integer | |
dt | Integer | Date |
totalHours | Numeric | TOTAL hours for the date |
timerCount | Integer | If a user has more than one open timer, this value indicates the COUNT of timers. Remember that only one timer can be "active" at a time, but a user can maintain several draft entries that they can switch between throughout the day. |
billableHours | Numeric | BILLABLE hours for the date (total=billable+nonBillable) |
nonBillableHours | Numeric | NON-BILLABLE hours for the date (total=billable+nonBillable) |
totalUnsubmittedTime | Numeric | If there is any UNSUBMITTED time for the period, this value will be >0. |
If your firm uses the system's audit logging to track user comments on changes that are made which don't conform to your audit standards, then you can use this end-point to update log entries with user comments.
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} HTTP POST: /time/AuditLogDetail HTTP RESPONSE: empty HTTP 200 means that the audit log comment was updated correctly.
In addition to creating and saving time entries, the BigTime API can be used to track "work in progress" with timers.
When a user needs to track activity that starts/stops throughout the day, they use BigTime's timer functionality to track that work. Then, once that work is completed (or once the day ends), those timers can be converted into time entries. The timer end-points in the api allow you to manage those activities.
Timer don't have dates. A timer is a work in progress. It's a stopwatch that hasn't been turned into an actual time entry yet. So, timer objects don't have a "date" Once those objects are converted into time entries -- then they will have dates assigned.
Timer information isn't included in your summary of timesheet data. Since timers are works in progress, their numbers aren't included in your daily summaries. For a complete picture of time entry "totals" for today's date, you should call /timer/total to get a total number of elapsed hours to add to the current date's total.
Call this simple end-point and the system will return all of the user's active timers.
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} HTTP GET: /timer/sheet/{staffsid} HTTP RESPONSE: List of Timer objects (see detail below).
Timer objects are very similar to TimeEntry objects. In fact, the list of fields for a TimeEntry contains the "Timer" tag (above) for the set of fields that are returned for a timer object.
In addition to the basic time-entry related fields, every Timer object returns the following information.
Field | Type | Description |
---|---|---|
ElapsedTime | Numeric |
Total hours (including any RUNNING time). For timers, this number is a better indicator of total hours than Hours_IN. However, you can use the difference between ElapsedTime and Hours_IN to perform some fun calculations (see note below).
You only accumulate hours in the Hours_IN field when you pause/stop a timer. So, brand new timers contain a 0 in Hours_IN. Timers that have been started/stopped at least once will contain the total "accumulated" hours in Hours_IN. Have a user that wants to cancel their timer? Stop the timer (eg - call /timer/stop) and then save that stopped timer with the old value from Hours_IN. Want to know how much time has elapsed since you LAST CLICKED start? Look at the difference betwen Hours_IN and ElapsedTime. |
IsActive | True/False | A user can have only one active timer at a time. They may have dozens of timers "in process", but only one of them can be actively accruing time at any given moment. Starting one will stop the rest. The timer which is currently active (if any) will return TRUE for this field. |
StartTime | Numeric | if this is an active timer, then the StartTime is the UTC date/time at which the timer was started (expressed as a number: secondsFromEpoch). |
Call this simple end-point and the system will return the total hours in the user's timer list as well as the id/start time/elapsed time of the current "active" timer.
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} HTTP GET: /timer/total/{staffsid} HTTP RESPONSE: {totalHours:2.256645, activeTimerSid:1, activeTimerStartDt:1234876595689, activeTimerProjectNm: "ABC Company", activeTimerHours:1.006645}.
You can use a simple GET request to pull the details associated with a single timer. Note that the TIMER view (see field list above) will be returned by default.
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} HTTP GET: /timer/item/{sid} HTTP RESPONSE: (Timer object -- see above for details)
As mentioned, only one timer can be actively accumulating time at any given moment. You don't need to know which one -- just call this end point to get a copy of the user's currently running timer. If no timer is found, the system will return an empty 200 response. Note that you can also pass an alternative staffSid to get the active timer for a specific staffer. By default, the current user's active timer is returned.
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} HTTP GET: /timer/activeItem/{staffSid} HTTP RESPONSE: (Timer object -- see above for details)
Note that only the fields listed in the AddUpdate view (above) should be included in your post to this url. This works just like a time entry -- so any other data will be ignored. BigTime doesn't care about the order in which field data is posted, and none of the fields are case sensitive. Note that any validation errors will return a 400 error, and an invalid permission will return a 405 (eg - attempting to create time against a project you don't have rights to log time against).
Use the /Add endpoint to create a new (blank) timer that is automatically started.
You can save a new timer that was "already started" by passing a value for StartTime when you create a new timer. This will (a) automatically stop any running timer(s) and (b) create a new timer with a "starttime" of the value passed in your json.
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} HTTP Post: /timer/item POST CONTENT: {staffsid: 123, projectsid: 123, hours_in: 1.25, notes: "These are my timer notes..."} You can include ANY of the AddUpdate fields below, but you MUST include the required (*) fields. HTTP RESPONSE: (new Timer object -- see above for details)
Creates a new timer (linked to the user but nothing else) and starts it automatically.
BigTime will pause any running timers and start the new one you create automatically.
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} HTTP GET: /timer/add HTTP RESPONSE: (new Timer object -- see above for details)
Only the fields listed in the AddUpdate views (above) should be included in your post to this url.
There are several ways to start a new timer, but the best approach it to call the /start/ endpoint. If you setting the IsActive value to TRUE has the same effect. If you want to start a timer "as of" a specific date, then pass the UTC timestamp to StartTime.
You should also note that setting IsActive to FALSE has the same effect as cancelling a timer. So, if you're passing IsActive=FALSE to an active timer, then you should also pass a "final" number in Hours_IN for that timer's total hours. If, however, you are using the server to track and control timers, it's best to use the start/stop endpoints to activate and de-activate timers.
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} HTTP Post: /timer/Item{sid} POST CONTENT: {projectSid: 1234, notes: "this is an updated note..."} ** You can include ANY of the AddUpdate/Update fields below. However - you cannot "zero-out" or remove any of the required (*) fields. HTTP RESPONSE: (updated Timer object -- see above for details)
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} HTTP DELETE: /timer/Item/{sid} HTTP RESPONSE: 200 is a successful delete.
Starting a timer will automatically pause any other running timers, so you should not need to call pause explicitly if you are just switching the "active" status from one timer to another.
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} STARTING: /timer/start/{sid} PAUSING: /timer/pause/{sid} HTTP RESPONSE: (updated Timer object -- see above for details)
As a final step in a timer's life-cycle, a user will convert it into a time entry. Calling this function will (a) create that entry and (b) delete the timer. The call will return a server error if it fails, and the timer will not be removed.
If the user has made updates to the timer they want to convert, you can post those updates as well.
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} POST: /timer/SaveAsTime/{sid} HTTP RESPONSE: (new TIME ENTRY OBJECT -- see above for details)
Instead of closing out timers one at a time, your user may want to close out all of thier open timers at once. BigTime makes this endpoint abailable to accomplish exactly that task.
HEADERS: X-Auth-Token:{YourAPIToken}, X-Auth-Realm:{YourFirmId} POST: /timer/CloseAllTimers/{staffsid} HTTP RESPONSE: A list of time entry Id's added: {-1234,-123456,-3333,...}