Notification Preferences
The notification preference object
| Attribute | Type | Description |
|---|---|---|
channels |
array | Array of per-type, per-channel preferences. Contains one row for every supported notification type and channel pair, always in the same fixed order, with a type’s in_app row before its email row. |
channels[].notification_type |
string | Notification preference type. |
channels[].channel |
string | Delivery channel: in_app (the in-product feed and bell) or email. |
channels[].enabled |
boolean | Whether the channel is enabled for this notification type. |
channels[].feed_type |
string | The notification_type of the corresponding notification in GET /v2/notifications, or null when there is no corresponding notification type. null for occasional_updates and project_deleted. Both submitted-timesheet preferences share the timesheet_submitted feed type. |
channels[].category |
string | The category of the corresponding notification in GET /v2/notifications, or null when feed_type is null. |
weekly_report |
object | Weekly tracked-time email preference. |
weekly_report.enabled |
boolean | Whether the weekly tracked-time email is enabled. |
approval_confirmation |
object | Timesheet approval email preference. |
approval_confirmation.enabled |
boolean | Whether an email is sent when a timesheet is approved. |
reminders |
object | Daily reminder preferences. |
reminders.enabled |
boolean | Whether daily reminders are enabled. |
reminders.time_of_day |
integer | Time of day for reminders, as seconds after local midnight in the user’s timezone. Can range from 0 to 86399 (e.g. 59400 is 16:30). |
reminders.days |
array | Weekdays on which reminders are sent, as strings from monday through sunday. |
reminders.via_desktop |
boolean | Whether desktop and web-push reminders are enabled. This does not register a push subscription. |
reminders.via_slack |
boolean | Whether Slack reminders are enabled. The value is stored whether or not Slack is connected. |
reminders.team_wide |
boolean | Whether the user receives team-wide reminders. |
Reminders sent by email and in the in-product feed are expressed through the channels array as the time_tracking_reminder type with the email and in_app channels. web_push_subscription is not part of this object.
Required permissions
Any authenticated user can read and update their own notification preferences, and no administrator role is required. There is no :id parameter: this endpoint always operates on the authenticated caller’s preferences. An inactive account returns a 403 Forbidden status code.
Retrieve notification preferences
Beta endpoint. Fully available, but the response shape may still change. Until it is frozen, every response includes
Harvest-Api-Stability: beta.
Returns the notification preferences for the authenticated user.
GET /v2/notification_preferences
This endpoint takes no parameters.
Example Request:
curl "https://api.harvestapp.com/v2/notification_preferences" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Harvest-Account-Id: $ACCOUNT_ID" \
-H "User-Agent: MyApp ([email protected])"
Example Response:
{
"channels": [
{
"notification_type": "timesheet_submitted_person",
"channel": "in_app",
"enabled": true,
"feed_type": "timesheet_submitted",
"category": "timesheets"
},
{
"notification_type": "timesheet_submitted_person",
"channel": "email",
"enabled": false,
"feed_type": "timesheet_submitted",
"category": "timesheets"
},
{
"notification_type": "timesheet_submitted_project",
"channel": "in_app",
"enabled": true,
"feed_type": "timesheet_submitted",
"category": "timesheets"
},
{
"notification_type": "timesheet_submitted_project",
"channel": "email",
"enabled": false,
"feed_type": "timesheet_submitted",
"category": "timesheets"
},
{
"notification_type": "timesheet_approved",
"channel": "in_app",
"enabled": true,
"feed_type": "timesheet_approved",
"category": "timesheets"
},
"{18 more channel preference objects}",
{
"notification_type": "occasional_updates",
"channel": "email",
"enabled": true,
"feed_type": null,
"category": null
}
],
"weekly_report": {
"enabled": true
},
"approval_confirmation": {
"enabled": true
},
"reminders": {
"enabled": true,
"time_of_day": 59400,
"days": [
"monday",
"tuesday",
"wednesday",
"thursday",
"friday"
],
"via_desktop": true,
"via_slack": false,
"team_wide": false
}
}Update notification preferences
Beta endpoint. Fully available, but the response shape may still change. Until it is frozen, every response includes
Harvest-Api-Stability: beta.
Updates the authenticated user’s notification preferences. Every key is optional, and omitted keys keep their current value. Returns the preferences as they are after the update.
PATCH /v2/notification_preferences
| Parameter | Type | Description |
|---|---|---|
channels |
array of objects | Preference entries in the form {notification_type: String, channel: "in_app" or "email", enabled: Boolean}. Supply at most one entry per type and channel pair. An empty array changes nothing. |
channels[].notification_type |
string | Notification preference type. |
channels[].channel |
string | Delivery channel: in_app or email. |
channels[].enabled |
boolean | Whether to enable the channel for this notification type. |
weekly_report.enabled |
boolean | Whether to enable the weekly tracked-time email. |
approval_confirmation.enabled |
boolean | Whether to enable the email sent when a timesheet is approved. |
reminders.enabled |
boolean | Whether to enable daily reminders. |
reminders.time_of_day |
integer or numeric string | Time of day for reminders, as seconds after local midnight in the user’s timezone. Can range from 0 to 86399. |
reminders.days |
array of strings | Weekdays on which to send reminders: monday through sunday. An empty array is invalid. |
reminders.via_desktop |
boolean | Whether to enable desktop and web-push reminders. Does not register a push subscription. |
reminders.via_slack |
boolean | Whether to enable Slack reminders. Stored whether or not Slack is connected. |
reminders.team_wide |
boolean | Whether to receive team-wide reminders. |
Boolean values may be sent as true, false, "true", "false", "1", "0", 1, or 0. Null values are invalid. Unknown top-level keys are ignored.
Example Request:
curl -X PATCH "https://api.harvestapp.com/v2/notification_preferences" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Harvest-Account-Id: $ACCOUNT_ID" \
-H "Content-Type: application/json" \
-H "User-Agent: MyApp ([email protected])" \
-d '{"channels": [{"notification_type": "timesheet_submitted_person", "channel": "email", "enabled": true}], "reminders": {"time_of_day": 59400}}'
Example Response:
{
"channels": [
{
"notification_type": "timesheet_submitted_person",
"channel": "in_app",
"enabled": true,
"feed_type": "timesheet_submitted",
"category": "timesheets"
},
{
"notification_type": "timesheet_submitted_person",
"channel": "email",
"enabled": true,
"feed_type": "timesheet_submitted",
"category": "timesheets"
},
{
"notification_type": "timesheet_submitted_project",
"channel": "in_app",
"enabled": true,
"feed_type": "timesheet_submitted",
"category": "timesheets"
},
{
"notification_type": "timesheet_submitted_project",
"channel": "email",
"enabled": false,
"feed_type": "timesheet_submitted",
"category": "timesheets"
},
{
"notification_type": "timesheet_approved",
"channel": "in_app",
"enabled": true,
"feed_type": "timesheet_approved",
"category": "timesheets"
},
"{18 more channel preference objects}",
{
"notification_type": "occasional_updates",
"channel": "email",
"enabled": true,
"feed_type": null,
"category": null
}
],
"weekly_report": {
"enabled": true
},
"approval_confirmation": {
"enabled": true
},
"reminders": {
"enabled": true,
"time_of_day": 59400,
"days": [
"monday",
"tuesday",
"wednesday",
"thursday",
"friday"
],
"via_desktop": true,
"via_slack": false,
"team_wide": false
}
}Update notification preferences with PUT
Beta endpoint. Fully available, but the response shape may still change. Until it is frozen, every response includes
Harvest-Api-Stability: beta.
PUT /v2/notification_preferences accepts the same request body and behaves identically to PATCH: supplied keys are merged, and omitted keys keep their current value.
PUT /v2/notification_preferences
Example Request:
curl -X PUT "https://api.harvestapp.com/v2/notification_preferences" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Harvest-Account-Id: $ACCOUNT_ID" \
-H "Content-Type: application/json" \
-H "User-Agent: MyApp ([email protected])" \
-d '{"weekly_report": {"enabled": false}}'
The response is the same preference object returned by PATCH /v2/notification_preferences.
Errors
| Status | Description |
|---|---|
400 |
The User-Agent header is missing: {"message":"User-Agent header required"}. |
401 |
The credentials are missing or invalid. |
403 |
The account is inactive: {"message":"Account inactive"}. |
409 |
Another request updated the preferences at the same time and the change was rolled back: {"message":"Notification preferences were updated concurrently; retry the request"}. |
415 |
A request body was not sent as application/json: {"message":"Content-Type header required to be application/json, got …"}. When the header is absent, the message ends with got nil. |
422 |
The request is malformed, or a well-formed value cannot be stored by the account. The response body contains a message that explains the rejection. |
A 422 is returned for an unknown notification type (Unknown notification type "…"), an unsupported channel for a type (Unsupported notification channel "…" for "…"), a duplicate type and channel pair (Duplicate channel preference for "…"/"…"), a non-boolean value (… must be a boolean), an invalid weekday, an empty reminders.days array, or an out-of-range reminders.time_of_day. Multiple validation messages are joined with , .
A reminders.team_wide value is refused with a 422 when the account has no team-wide timesheet reminder: the message is This account has no team-wide timesheet reminder to opt out of when opting out, or This account has no team-wide timesheet reminder to opt into when opting in. Setting reminders.team_wide to the value already held succeeds without change.