Skip to content

Submit Self Timesheet

This endpoint submits a timesheet for a user, sets every timecard to pending, and emails the people who review or follow that time.

POST /v1/timesheets/timesheets/submit-self

Include your API key in the x-api-key header. See Authentication.

timesheets:submit-self

The request body is not validated against a schema. Send every field marked Required in the shape shown. A missing or malformed field usually causes a 500, not a 400.

FieldTypeRequiredDescription
userIdstringYesUser whose timesheet is submitted. Accepts an external user ID (user-a) or an internal one (tenant-123#user-a). It can be any user in your account.
timecardsobject[]YesTimecards in the timesheet. Send at least one.

Each timecard:

FieldTypeRequiredDescription
projectIdstringYesProject ID.
taskIdstringYesTask ID. Each projectId and taskId pair identifies one timecard.
dataobjectYesOne entry for each day: Saturday, Sunday, Monday, Tuesday, Wednesday, Thursday, and Friday. All seven keys are required.

Each day in data:

FieldTypeRequiredDescription
datestringYesDate in YYYY-MM-DD format.
hoursnumberYesHours worked on that day.
descriptionstring | nullNoDescription of the work.

The API takes the timesheet period from the earliest and latest date in the first timecard, and builds the timesheet ID from that period. Use the same seven dates in every timecard. The API does not check that each date falls on the weekday of its key.

{
"userId": "user-a",
"timecards": [
{
"projectId": "project-mno123pqr4567890",
"taskId": "task-pqr123stu4567890",
"data": {
"Saturday": { "date": "2022-12-31", "hours": 0, "description": null },
"Sunday": { "date": "2023-01-01", "hours": 0, "description": null },
"Monday": { "date": "2023-01-02", "hours": 8, "description": "Worked on implementation" },
"Tuesday": { "date": "2023-01-03", "hours": 8, "description": "Worked on implementation" },
"Wednesday": { "date": "2023-01-04", "hours": 8, "description": "Worked on implementation" },
"Thursday": { "date": "2023-01-05", "hours": 8, "description": "Worked on implementation" },
"Friday": { "date": "2023-01-06", "hours": 8, "description": "Worked on implementation" }
}
}
]
}
Terminal window
curl -X POST "https://api.crewsum.com/v1/timesheets/timesheets/submit-self" \
-H "x-api-key: $CREWSUM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"userId": "user-a",
"timecards": [
{
"projectId": "project-mno123pqr4567890",
"taskId": "task-pqr123stu4567890",
"data": {
"Saturday": { "date": "2022-12-31", "hours": 0, "description": null },
"Sunday": { "date": "2023-01-01", "hours": 0, "description": null },
"Monday": { "date": "2023-01-02", "hours": 8, "description": "Worked on implementation" },
"Tuesday": { "date": "2023-01-03", "hours": 8, "description": "Worked on implementation" },
"Wednesday": { "date": "2023-01-04", "hours": 8, "description": "Worked on implementation" },
"Thursday": { "date": "2023-01-05", "hours": 8, "description": "Worked on implementation" },
"Friday": { "date": "2023-01-06", "hours": 8, "description": "Worked on implementation" }
}
}
]
}'

On success, the API returns HTTP 200 OK.

data has one item for each email the API tried to send. status is fulfilled or rejected. A rejected item also has a rejectionReason. The response does not include the timesheet ID. It is <tenantId>#<userId>#<periodStart>_<periodEnd>, here tenant-123#user-a#20221231_20230106.

{
"message": "Successfully submitted timesheet",
"data": [
{
"to": "manager@example.com",
"status": "fulfilled"
}
]
}
  • New timesheet: the API creates the timesheet, sets its submittedAt, and stores every timecard as pending.
  • Existing timesheet: if a timesheet with the same ID exists, the API merges the request into it. Timecards that are new in the request are added, timecards left out of the request are removed, and changed timecards are updated. Then every timecard is set back to pending, including timecards that were already approved. submittedAt is set to the current time and resubmissionAllowed is set to false.
  • A submitted timesheet can be submitted again only if resubmission was allowed in Crewsum or at least one timecard is rejected. Otherwise the API returns 400. A saved draft can always be submitted.
  • The API returns 400 if the hours on any one date add up to more than 24 across all timecards.
  • The API emails:
    • project contacts whose notification level for the project is copy only or copy with link (copy-with-link contacts get a link to approve the time),
    • the user,
    • the user’s supplier contacts,
    • your internal copy address, if it is turned on.
  • The response lists the email addresses that were sent to.
  • After the timesheet is submitted, Crewsum sends the timesheet.submitted webhook event. The payload is { "timesheet": <timesheet> }, in the same shape as Get Timesheet.
  • The timesheet is saved before the emails are sent. If the email step fails, the API returns 500, but the submission is kept and no webhook is sent.
StatusMeaning
400userId is missing. Message: UserID Missing.
400One date has more than 24 hours in total. Message: Error submitting timesheet. Daily hours cannot exceed 24 hours on <date>.
400The timesheet was already submitted and cannot be submitted again. Message: Error submitting timesheet. Timesheet already submitted!
500timecards is empty, or a date in the first timecard is not valid. Message: Error parsing timesheet.
500The user could not be found or loaded. Message: Error fetching startup information for the submission.
500Crewsum could not save the timesheet. The message starts with Error saving timesheet. or Error submitting timesheet. and names the step that failed.
500The timesheet was saved, but the emails could not be prepared. Message: Error sending submission notifications.
500The body is missing or has the wrong shape, for example no timecards array. The response has a generic error body.

Authentication and scope failures are described in Errors.