Account and settings
Read the key's user, change their settings, check the AI allowance and export their data.
Settings
/settings/profile, /settings/preferences, /settings/notifications, /settings/review, /settings/tasks and /settings/dashboard read with GET and change with PATCH (PUT for the dashboard), the same as the Settings pages. A PATCH changes only the fields you send. Each writes through the code the page saves with, so the limits and errors match the app's.
- Changing
timeZonemoves every task's due date so it keeps its date and time on the clock. A zone the server doesn't know returns 400, and a change made elsewhere during the request returns 409. PATCH /settings/notifications/{kind}changes one kind's channels and timing. A timing field the kind doesn't use, or a value off its list, returns 400 withdetails.fieldand the allowed values indetails.reason.subjectOverridesonPATCH /settings/reviewreplaces the whole list, and everysubjectIdmust be the user's.PUT /settings/dashboardtakes the cards to show, in order. Send theupdatedAtyou read asbaseUpdatedAtto get 409 instead of overwriting a layout saved elsewhere.
What the API can't change
The API can't change the password, the email address, two-factor, passkeys, sign-in methods, sessions, API keys, connected apps, the plan or payment, and can't delete the account. The theme and the AI model choice are kept in the browser.
Get the key's user
Returns the user who owns the API key. Any valid key may call it.
Required scopes: None beyond a valid key.
AuthorizationBearer <token>OK
application/json- response
data*curl -X GET 'https://workroad.app/api/v1/me' \ -H 'Authorization: Bearer $WORKROAD_API_KEY'{ "data": { "id": "0b7d4c3e-1f2a-4b5c-9d6e-7f8a9b0c1d2e", "name": "Ada Lovelace", "username": "ada", "subscription": "PAID", "premium": true, "admin": false, "timeZone": "Europe/London", "createdAt": "2026-01-15T09:30:00.000Z" }}Get the profile
The user's name, username, bio and what their public profile shows.
Required scopes: settings:view
AuthorizationBearer <token>OK
application/json- response
data*curl -X GET 'https://workroad.app/api/v1/settings/profile' \ -H 'Authorization: Bearer $WORKROAD_API_KEY'{ "data": { "name": "Ada Lovelace", "username": "ada", "description": "Maths and computing, mostly.", "status": "Revising for finals", "location": "London", "showLastLogin": true, "showCreated": true, "showTimezone": false, "showMembership": true }}Update the profile
Changes the name, username, bio, status, location or what the public profile shows. The sign-in email address can't be changed here.
Required scopes: settings:update
AuthorizationBearer <token>application/json- body
Send only the fields to change. An omitted field stays as it is.
name?string2 to 100 characters.
2 <= length <= 100username?string3 to 30 letters, digits, _ or ., stored in lower case. Fails with 409 when another account has it.
^[a-zA-Z0-9_.]+$3 <= length <= 30description?stringUp to 512 characters. Send an empty string to clear it.
0 <= length <= 512status?stringUp to 256 characters. Send an empty string to clear it.
0 <= length <= 256location?stringUp to 256 characters. Send an empty string to clear it.
0 <= length <= 256showLastLogin?booleanshowCreated?booleanshowTimezone?booleanshowMembership?booleanOK
application/json- response
data*curl -X PATCH 'https://workroad.app/api/v1/settings/profile' \ -H 'Authorization: Bearer $WORKROAD_API_KEY' \ -H 'Content-Type: application/json' \ -d '{}'{ "data": { "name": "Ada Lovelace", "username": "ada", "description": "Maths and computing, mostly.", "status": "Revising for finals", "location": "London", "showLastLogin": true, "showCreated": true, "showTimezone": false, "showMembership": true }}Get language, time zone and privacy
The app language, the account's time zone and leaderboard visibility.
Required scopes: settings:view
AuthorizationBearer <token>OK
application/json- response
data*curl -X GET 'https://workroad.app/api/v1/settings/preferences' \ -H 'Authorization: Bearer $WORKROAD_API_KEY'{ "data": { "locale": "en", "timeZone": "Europe/London", "showOnLeaderboards": true }}Update language, time zone or privacy
Changing the time zone moves every task's due date so it keeps its date and time on the clock, the same as changing it in Settings → Account.
Required scopes: settings:update
AuthorizationBearer <token>application/json- body
Send only the fields to change. An omitted field stays as it is.
locale?stringOne of the five app languages.
"en""de""es""fr""it"timeZone?stringAn IANA zone name. Aliases such as Asia/Calcutta are stored by their canonical name. Every task keeps its due date and time on the clock, so a task due at 09:00 is still due at 09:00 in the new zone.
1 <= length <= 64showOnLeaderboards?booleanOK
application/json- response
data*curl -X PATCH 'https://workroad.app/api/v1/settings/preferences' \ -H 'Authorization: Bearer $WORKROAD_API_KEY' \ -H 'Content-Type: application/json' \ -d '{}'{ "data": { "locale": "en", "timeZone": "Europe/London", "showOnLeaderboards": true }}Get notification settings
Quiet hours, the email digest, push content, calendar alarms, and each notification kind's channels and timing.
Required scopes: settings:view
AuthorizationBearer <token>OK
application/json- response
data*curl -X GET 'https://workroad.app/api/v1/settings/notifications' \ -H 'Authorization: Bearer $WORKROAD_API_KEY'{ "data": { "quietHoursEnabled": true, "quietStart": "22:00", "quietEnd": "07:00", "pushShowContent": false, "calendarAlarms": false, "digestFrequency": "WEEKLY", "digestTime": "07:30", "digestWeekday": 1, "emailSuppressedAt": null, "emailSuppressedReason": null, "emailVerified": true, "kinds": [ { "kind": "TASK_DUE", "inApp": true, "email": false, "push": true, "leadMinutes": 60, "leadDays": [ 1 ], "localTime": "18:00" }, { "kind": "EXAM_SOON", "inApp": true, "email": true, "push": true, "leadMinutes": null, "leadDays": [ 3, 1 ], "localTime": "18:00" } ] }}Update notification settings
Changes quiet hours, the digest, push content or calendar alarms. Change one notification kind with updateNotificationKind.
Required scopes: settings:update
AuthorizationBearer <token>application/json- body
Send only the fields to change. An omitted field stays as it is.
quietHoursEnabled?booleanquietStart?stringHH:MM, such as 22:00.
quietEnd?stringHH:MM, such as 07:00.
pushShowContent?booleancalendarAlarms?booleandigestFrequency?string"OFF""DAILY""WEEKLY"digestTime?stringHH:MM, such as 07:30.
digestWeekday?integer0 for Sunday to 6 for Saturday.
0 <= value <= 6OK
application/json- response
data*curl -X PATCH 'https://workroad.app/api/v1/settings/notifications' \ -H 'Authorization: Bearer $WORKROAD_API_KEY' \ -H 'Content-Type: application/json' \ -d '{}'{ "data": { "quietHoursEnabled": true, "quietStart": "22:00", "quietEnd": "07:00", "pushShowContent": false, "calendarAlarms": false, "digestFrequency": "WEEKLY", "digestTime": "07:30", "digestWeekday": 1, "emailSuppressedAt": null, "emailSuppressedReason": null, "emailVerified": true, "kinds": [ { "kind": "TASK_DUE", "inApp": true, "email": false, "push": true, "leadMinutes": 60, "leadDays": [ 1 ], "localTime": "18:00" }, { "kind": "EXAM_SOON", "inApp": true, "email": true, "push": true, "leadMinutes": null, "leadDays": [ 3, 1 ], "localTime": "18:00" } ] }}Update one notification kind
Turns a kind's bell, email or push on or off, or changes when it is sent. Returns the kind as it now resolves.
Required scopes: settings:update
AuthorizationBearer <token>kind*stringTASK_DUE before a task is due, EXAM_SOON before an exam, STREAK_AT_RISK when today's goal isn't met, LESSON_STARTING before a lesson, SCHEDULE_SHARE_CHANGED when someone shares a timetable or stops, GENERATION_FINISHED and RECORDING_READY when AI work finishes, DIGEST for the email digest.
"TASK_DUE""EXAM_SOON""STREAK_AT_RISK""LESSON_STARTING""SCHEDULE_SHARE_CHANGED""GENERATION_FINISHED""RECORDING_READY""DIGEST"application/json- body
Send only the fields to change. An omitted field stays as it is. A timing field the kind doesn't use, or a value off its list, fails with 400.
inApp?booleanemail?booleanpush?booleanleadMinutes?|TASK_DUE: 15, 60, 180, 1440. LESSON_STARTING: 5, 10, 15, 30. Null restores the default.
leadDays?|TASK_DUE (all-day tasks): 0, 1, 2. EXAM_SOON: 1, 2, 3, 5, 7, 14; one value replaces the default of 3 days and 1 day before. Null restores the default.
localTime?|HH:MM for TASK_DUE (all-day tasks), EXAM_SOON and STREAK_AT_RISK. Null restores the default.
OK
application/json- response
data*curl -X PATCH 'https://workroad.app/api/v1/settings/notifications/TASK_DUE' \ -H 'Authorization: Bearer $WORKROAD_API_KEY' \ -H 'Content-Type: application/json' \ -d '{}'{ "data": { "kind": "TASK_DUE", "inApp": true, "email": false, "push": true, "leadMinutes": 60, "leadDays": [ 1 ], "localTime": "18:00" }}Resume notification email
Turns notification email back on after it was suppressed because a message bounced or was reported as spam. Fix the address first, or the next email suppresses it again.
Required scopes: settings:update
AuthorizationBearer <token>Idempotency-Key?stringA client-generated key (1 to 255 visible ASCII characters) that makes this POST safe to retry. A retry with the same key and body replays the original response; the same key with a different body returns 409 idempotency_conflict. Stored for 24 hours.
^[\x21-\x7E]{1,255}$1 <= length <= 255OK
application/json- response
data*curl -X POST 'https://workroad.app/api/v1/settings/notifications/resume-email' \ -H 'Authorization: Bearer $WORKROAD_API_KEY'{ "data": { "quietHoursEnabled": true, "quietStart": "22:00", "quietEnd": "07:00", "pushShowContent": false, "calendarAlarms": false, "digestFrequency": "WEEKLY", "digestTime": "07:30", "digestWeekday": 1, "emailSuppressedAt": null, "emailSuppressedReason": null, "emailVerified": true, "kinds": [ { "kind": "TASK_DUE", "inApp": true, "email": false, "push": true, "leadMinutes": 60, "leadDays": [ 1 ], "localTime": "18:00" }, { "kind": "EXAM_SOON", "inApp": true, "email": true, "push": true, "leadMinutes": null, "leadDays": [ 3, 1 ], "localTime": "18:00" } ] }}Get review settings
Daily new-card and review limits, FSRS desired retention, the daily goal, leech handling and per-subject overrides.
Required scopes: settings:view
AuthorizationBearer <token>OK
application/json- response
data*curl -X GET 'https://workroad.app/api/v1/settings/review' \ -H 'Authorization: Bearer $WORKROAD_API_KEY'{ "data": { "newPerDay": 20, "reviewsPerDay": null, "retention": 0.9, "goalKind": "cards", "goalTarget": 30, "leechThreshold": 8, "suspendLeeches": true, "subjectOverrides": [ { "subjectId": "5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f", "newPerDay": 10, "reviewsPerDay": null, "retention": 0.95 } ], "optimizedAt": null }}Update review settings
Changes the limits, retention, daily goal or leech handling the review queue and practice sessions use.
Required scopes: settings:update
AuthorizationBearer <token>application/json- body
Send only the fields to change. An omitted field stays as it is. A change applies from the next card; today's goal stays as it was set this morning.
newPerDay?integer0 to 2000.
0 <= value <= 2000reviewsPerDay?|0 to 10000, or null for no limit.
retention?number0.7 to 0.99. Higher means more reviews and fewer forgotten cards.
0.7 <= value <= 0.99goalKind?string"cards""minutes"goalTarget?integer1 to 2000.
1 <= value <= 2000leechThreshold?integer2 to 100.
2 <= value <= 100suspendLeeches?booleansubjectOverrides?array<>Replaces every override when sent. Send an empty list to remove them all.
items <= 200OK
application/json- response
data*curl -X PATCH 'https://workroad.app/api/v1/settings/review' \ -H 'Authorization: Bearer $WORKROAD_API_KEY' \ -H 'Content-Type: application/json' \ -d '{}'{ "data": { "newPerDay": 20, "reviewsPerDay": null, "retention": 0.9, "goalKind": "cards", "goalTarget": 30, "leechThreshold": 8, "suspendLeeches": true, "subjectOverrides": [ { "subjectId": "5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f", "newPerDay": 10, "reviewsPerDay": null, "retention": 0.95 } ], "optimizedAt": null }}Get task preferences
The task types the app offers and the colours of overdue and due tasks.
Required scopes: settings:view
AuthorizationBearer <token>OK
application/json- response
data*curl -X GET 'https://workroad.app/api/v1/settings/tasks' \ -H 'Authorization: Bearer $WORKROAD_API_KEY'{ "data": { "types": [ "Homework", "Test", "Study", "Project" ], "overdueColor": "#ef4444", "dueTodayColor": "#f97316", "nearlyDueColor": "#eab308" }}Update task preferences
Replaces the task types or changes the due-date colours.
Required scopes: settings:update
AuthorizationBearer <token>application/json- body
Send only the fields to change. An omitted field stays as it is.
types?array<>Replaces the whole list, in order. Tasks keep their type when it is removed here.
1 <= items <= 50overdueColor?stringOverdue tasks, as #RRGGBB.
^#[0-9A-F]{6}$dueTodayColor?stringTasks due today, as #RRGGBB.
^#[0-9A-F]{6}$nearlyDueColor?stringTasks due soon, as #RRGGBB.
^#[0-9A-F]{6}$OK
application/json- response
data*curl -X PATCH 'https://workroad.app/api/v1/settings/tasks' \ -H 'Authorization: Bearer $WORKROAD_API_KEY' \ -H 'Content-Type: application/json' \ -d '{}'{ "data": { "types": [ "Homework", "Test", "Study", "Project" ], "overdueColor": "#ef4444", "dueTodayColor": "#f97316", "nearlyDueColor": "#eab308" }}Get the dashboard layout
The dashboard cards shown, in order, and their sizes.
Required scopes: settings:view
AuthorizationBearer <token>OK
application/json- response
data*curl -X GET 'https://workroad.app/api/v1/settings/dashboard' \ -H 'Authorization: Bearer $WORKROAD_API_KEY'{ "data": { "widgets": [ { "id": "reviews", "size": "single" }, { "id": "schedule", "size": "wide" }, { "id": "tasks", "size": "single" } ], "updatedAt": "2026-02-01T12:00:00.000Z" }}Replace the dashboard layout
Sets which cards the dashboard shows, in order, and their sizes. A card listed twice is shown once.
Required scopes: settings:update
AuthorizationBearer <token>application/json- body
widgets*array<>Every card to show, in order. Leave a card out to hide it.
items <= 9baseUpdatedAt?|The updatedAt you read. When sent, a layout saved elsewhere since then fails with 409; null means no layout was saved yet. Leave it out to replace whatever is saved.
OK
application/json- response
data*curl -X PUT 'https://workroad.app/api/v1/settings/dashboard' \ -H 'Authorization: Bearer $WORKROAD_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "widgets": [ { "id": "reviews", "size": "single" } ]}'{ "data": { "widgets": [ { "id": "reviews", "size": "single" }, { "id": "schedule", "size": "wide" }, { "id": "tasks", "size": "single" } ], "updatedAt": "2026-02-01T12:00:00.000Z" }}Get the AI allowance
This month's AI use and the credit balance for the key's user, the same numbers the app shows. The free plan allows 3 AI uses per UTC calendar month. The paid plan has no count limit but allows 100 recordings a month under fair use. Once those are spent, each AI use spends one credit while any are left. Any valid key may call it.
Required scopes: None beyond a valid key.
AuthorizationBearer <token>OK
application/json- response
data*curl -X GET 'https://workroad.app/api/v1/ai-allowance' \ -H 'Authorization: Bearer $WORKROAD_API_KEY'{ "data": { "plan": "free", "used": 1, "creditUses": 0, "limit": 3, "recordings": { "used": 1, "limit": null }, "resetsAt": "2026-02-01T00:00:00.000Z", "credits": { "balance": 7 } }}Get the account export
The latest account export: everything the user's account holds in one ZIP (notes as Markdown with their images and files, recordings, study sets with their review history, tasks, schedules, tests, Copilot chats and settings as JSON). Start one with POST /export. data is null when none was started in the last 7 days.
Required scopes: account:export
AuthorizationBearer <token>OK
application/json- response
data*|nullcurl -X GET 'https://workroad.app/api/v1/export' \ -H 'Authorization: Bearer $WORKROAD_API_KEY'{ "data": { "id": "4f1c2b3a-5d6e-4f70-8a9b-0c1d2e3f4a5b", "status": "ready", "createdAt": "2026-10-28T09:15:00.000Z", "downloadUrl": "https://storage.example.com/export/…/4f1c2b3a.zip?X-Amz-Signature=…", "expiresAt": "2026-11-04T09:21:42.000Z", "sizeBytes": 48213504 }}Start an account export
Starts building the account export, or returns the one already being built. It takes a few minutes for a large account; poll GET /export until status is ready. The user also gets an email with a link when it is ready.
Required scopes: account:export
AuthorizationBearer <token>Idempotency-Key?stringA client-generated key (1 to 255 visible ASCII characters) that makes this POST safe to retry. A retry with the same key and body replays the original response; the same key with a different body returns 409 idempotency_conflict. Stored for 24 hours.
^[\x21-\x7E]{1,255}$1 <= length <= 255Accepted
application/json- response
data*curl -X POST 'https://workroad.app/api/v1/export' \ -H 'Authorization: Bearer $WORKROAD_API_KEY'{ "data": { "id": "4f1c2b3a-5d6e-4f70-8a9b-0c1d2e3f4a5b", "status": "pending", "createdAt": "2026-10-28T09:15:00.000Z", "downloadUrl": null, "expiresAt": null, "sizeBytes": null }}