Submit Admin Timesheet
This endpoint submits a timesheet on behalf of a user. By default the timecards are approved immediately. Set requireReview to true to send them for review instead.
Endpoint
Section titled “Endpoint”POST /v1/timesheets/timesheets/submit-adminAuthentication
Section titled “Authentication”Include your API key in the x-api-key header. See Authentication.
Required Scope
Section titled “Required Scope”timesheets:submit-adminRequest Body
Section titled “Request Body”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.
| Field | Type | Required | Description |
|---|---|---|---|
userId | string | Yes | User whose timesheet is submitted. Accepts an external user ID (user-a) or an internal one (tenant-123#user-a). |
requireReview | boolean | No | Defaults to false. When false, every timecard is stored as approved immediately and no emails are sent. When true, every timecard is stored as pending and review emails are sent. |
timecards | object[] | Yes | Timecards in the timesheet. Send at least one. |
Each timecard has the same shape as in Submit Self Timesheet: projectId, taskId, and data with all seven days from Saturday to Friday. Each day has date (YYYY-MM-DD), hours, and an optional description.
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.
Example Request Body
Section titled “Example Request Body”{ "userId": "user-a", "requireReview": false, "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" } } } ]}Example Request
Section titled “Example Request”curl -X POST "https://api.crewsum.com/v1/timesheets/timesheets/submit-admin" \ -H "x-api-key: $CREWSUM_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "userId": "user-a", "requireReview": false, "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" } } } ] }'const response = await fetch( "https://api.crewsum.com/v1/timesheets/timesheets/submit-admin", { method: "POST", headers: { "x-api-key": process.env.CREWSUM_API_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ userId: "user-a", requireReview: false, 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" }, }, }, ], }), },);const result = await response.json();POST /v1/timesheets/timesheets/submit-admin HTTP/1.1Host: api.crewsum.comx-api-key: <your-api-key>Content-Type: application/json
{ "userId": "user-a", "requireReview": false, "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" } } } ]}Response
Section titled “Response”On success, the API returns HTTP 200 OK. The response does not include the timesheet ID. It is <tenantId>#<userId>#<periodStart>_<periodEnd>, here tenant-123#user-a#20221231_20230106.
Example Response: requireReview is false or omitted
Section titled “Example Response: requireReview is false or omitted”{ "message": "Successfully submitted timesheet"}Example Response: requireReview is true
Section titled “Example Response: requireReview is true”data has one item for each email the API tried to send. status is fulfilled or rejected. A rejected item also has a rejectionReason.
{ "message": "Successfully submitted timesheet and sent mail messages", "data": [ { "to": "manager@example.com", "status": "fulfilled" } ]}Behavior
Section titled “Behavior”requireReviewisfalseor omitted: every timecard is stored asapproved. Nobody reviews the time and no emails are sent.requireReviewistrue: every timecard is stored aspending, and the API sends the same emails as Submit Self Timesheet.- Existing timesheet: if a timesheet with the same ID exists, the API overwrites it, even if it was already submitted or approved. There is no “already submitted” check. 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 to
approvedorpendingaccording torequireReview,submittedAtis set to the current time, andresubmissionAllowedis set tofalse. - The API returns
400if the hours on any one date add up to more than 24 across all timecards. - The timesheet is saved before the emails are sent. If the email step fails, the API returns
500, but the submission is kept.
Error Responses
Section titled “Error Responses”| Status | Meaning |
|---|---|
400 | userId is missing. Message: UserID Missing. |
400 | One date has more than 24 hours in total. Message: Error submitting timesheet. Daily hours cannot exceed 24 hours on <date>. |
500 | timecards is empty, or a date in the first timecard is not valid. Message: Error parsing timesheet. |
500 | The user could not be found or loaded. Message: Error fetching startup information for the submission. |
500 | Crewsum could not save the timesheet. The message starts with Error saving timesheet. or Error submitting timesheet. and names the step that failed. |
500 | requireReview is true, the timesheet was saved, but the emails could not be prepared. Message: Error sending submission notifications. |
500 | The 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.
Related Webhooks
Section titled “Related Webhooks”This endpoint does not trigger a webhook. It does not send timesheet.submitted.