Study sets
Create study sets with their terms, edit terms one at a time or in batches, and archive sets.
Sets and terms
A study set holds a fixed maximum of terms, and each term carries its own flashcard review history. POST /sets takes the set's terms in the same call. POST /sets/{setId}/terms appends a batch of terms and leaves the existing ones untouched. PATCH /sets/{setId}/terms/{termId} changes only the fields you send, and PATCH /sets/{setId}/terms does the same for up to 500 terms at once, all or none: a termId that isn't the set's returns 400 with details.reason unknown-term. POST /sets/{setId}/terms/delete deletes up to 500 terms and their review history, and refuses with 409 last-term to empty the set. DELETE /sets/{setId}/terms/{termId} erases the term's review history for good, so it needs sets:delete; a set's last term can't be deleted this way, delete the set instead.
A term is at most 2000 characters and a definition at most 4000. A term has up to 10 hints of at most 500 characters each, and a hint may be empty. A subjectId the key's user doesn't own returns the same 400 invalid_request on set endpoints.
GET /sets/{setId}/terms pages a set's terms like any other list. Its page size defaults to 100 and goes up to 500. GET /sets/{setId} returns only the first terms of a large set and sets termsTruncated when there are more, so use the list to read the rest.
List study sets
Newest first. Terms are omitted; fetch a single set for its terms. Archived sets are listed too unless archived says otherwise.
Required scopes: sets:view
AuthorizationBearer <token>limit?integerPage size, 1-100.
1 <= value <= 10025cursor?|Opaque. Pass nextCursor from the previous page unchanged.
archived?stringinclude (the default) lists every set, exclude only the active ones and only only the archived ones.
"include""include""exclude""only"OK
application/json- response
data*array<>nextCursor*|Opaque. Pass as ?cursor= unchanged to fetch the next page. null on the last page.
curl -X GET 'https://workroad.app/api/v1/sets' \ -H 'Authorization: Bearer $WORKROAD_API_KEY'{ "data": [ { "id": "7e6d5c4b-3a2f-4e1d-8c0b-9a8f7e6d5c4b", "name": "Spanish verbs", "description": "Irregular preterite forms.", "subjectId": "5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f", "archived": false, "favorite": false, "termCount": 1, "createdAt": "2026-01-15T09:30:00.000Z", "lastUsed": "2026-02-01T12:00:00.000Z", "updatedAt": "2026-02-01T12:00:00.000Z" } ], "nextCursor": null}Get a study set with its terms
Returns the set and up to 500 of its terms. termsTruncated is true when the set has more.
Required scopes: sets:view
AuthorizationBearer <token>setId*stringStudy set id, scoped to the key's user.
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$uuidOK
application/json- response
data*curl -X GET 'https://workroad.app/api/v1/sets/497f6eca-6276-4993-bfeb-53cbbbba6f08' \ -H 'Authorization: Bearer $WORKROAD_API_KEY'{ "data": { "id": "7e6d5c4b-3a2f-4e1d-8c0b-9a8f7e6d5c4b", "name": "Spanish verbs", "description": "Irregular preterite forms.", "subjectId": "5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f", "archived": false, "favorite": false, "termCount": 1, "createdAt": "2026-01-15T09:30:00.000Z", "lastUsed": "2026-02-01T12:00:00.000Z", "updatedAt": "2026-02-01T12:00:00.000Z", "terms": [ { "id": "2a1b0c9d-8e7f-4a6b-9c5d-4e3f2a1b0c9d", "term": "tener (yo)", "definition": "tuve", "hints": [ "irregular stem tuv-" ] } ], "termsTruncated": false }}List a set's terms
Ordered by id, 100 per page by default and up to 500. Use this to read terms past the 500 that GET /sets/{setId} returns.
Required scopes: sets:view
AuthorizationBearer <token>setId*stringStudy set id, scoped to the key's user.
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$uuidlimit?integerPage size, 1-500.
1 <= value <= 500100cursor?|Opaque. Pass nextCursor from the previous page unchanged.
OK
application/json- response
data*array<>nextCursor*|Opaque. Pass as ?cursor= unchanged to fetch the next page. null on the last page.
curl -X GET 'https://workroad.app/api/v1/sets/497f6eca-6276-4993-bfeb-53cbbbba6f08/terms' \ -H 'Authorization: Bearer $WORKROAD_API_KEY'{ "data": [ { "id": "2a1b0c9d-8e7f-4a6b-9c5d-4e3f2a1b0c9d", "term": "tener (yo)", "definition": "tuve", "hints": [ "irregular stem tuv-" ] } ], "nextCursor": null}Create a study set
Creates a set with 1-2000 terms in one call and returns it as GET /sets/{setId} would. File it under the course with subjectId or subjectName.
Use this to save content you wrote yourself. It uses none of the user's WorkRoad AI uses.
Required scopes: sets:create
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 <= 255application/json- body
name*string1 <= length <= 120description?stringlength <= 2000subjectId?stringSubject id owned by the user.
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$uuidsubjectName?stringThe subject (the course or module, such as Software Engineering) by name instead of by id. It must match one of the user's active subjects, ignoring case and accents. A name that matches none is refused with 400 unknown-subject, whose details.subjects lists the user's subjects, unless createSubjectIfMissing is true. Send subjectId or subjectName, not both.
1 <= length <= 64createSubjectIfMissing?booleanWith subjectName, creates the subject when the user has none by that name. Needs the subjects:create scope. Only send it when the user named a course they haven't added yet.
terms*array<>1-2000 terms, each a flashcard: the term on the front, the definition on the back.
1 <= items <= 2000Created
application/json- response
data*curl -X POST 'https://workroad.app/api/v1/sets' \ -H 'Authorization: Bearer $WORKROAD_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "name": "string", "terms": [ { "term": "string", "definition": "string" } ]}'{ "data": { "id": "7e6d5c4b-3a2f-4e1d-8c0b-9a8f7e6d5c4b", "name": "Spanish verbs", "description": "Irregular preterite forms.", "subjectId": "5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f", "archived": false, "favorite": false, "termCount": 1, "createdAt": "2026-01-15T09:30:00.000Z", "lastUsed": "2026-02-01T12:00:00.000Z", "updatedAt": "2026-02-01T12:00:00.000Z", "terms": [ { "id": "2a1b0c9d-8e7f-4a6b-9c5d-4e3f2a1b0c9d", "term": "tener (yo)", "definition": "tuve", "hints": [ "irregular stem tuv-" ] } ], "termsTruncated": false }}Update a study set
Changes the name, description or subject, or archives, restores or favourites the set. Only the fields you send change; null clears a nullable field. Terms and their review history are never touched. To archive or restore many sets at once, use POST /sets/archive.
Required scopes: sets:update
AuthorizationBearer <token>setId*stringStudy set id, scoped to the key's user.
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$uuidapplication/json- body
Send only the fields to change. An omitted field stays as it is. Terms are never touched.
name?string1 <= length <= 120description?|null or an empty string clears it.
subjectId?|Subject id owned by the user. null unfiles the set.
subjectName?stringThe subject (the course or module, such as Software Engineering) by name instead of by id. It must match one of the user's active subjects, ignoring case and accents. A name that matches none is refused with 400 unknown-subject, whose details.subjects lists the user's subjects, unless createSubjectIfMissing is true. Send subjectId or subjectName, not both.
1 <= length <= 64createSubjectIfMissing?booleanWith subjectName, creates the subject when the user has none by that name. Needs the subjects:create scope. Only send it when the user named a course they haven't added yet.
archived?booleanArchives or restores the set. An archived set keeps its terms and review history but leaves reviews, practice and the study picker.
favorite?booleanKeeps the set first in the user's sets list.
OK
application/json- response
data*curl -X PATCH 'https://workroad.app/api/v1/sets/497f6eca-6276-4993-bfeb-53cbbbba6f08' \ -H 'Authorization: Bearer $WORKROAD_API_KEY' \ -H 'Content-Type: application/json' \ -d '{}'{ "data": { "id": "7e6d5c4b-3a2f-4e1d-8c0b-9a8f7e6d5c4b", "name": "Spanish verbs", "description": "Irregular preterite forms.", "subjectId": "5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f", "archived": false, "favorite": false, "termCount": 1, "createdAt": "2026-01-15T09:30:00.000Z", "lastUsed": "2026-02-01T12:00:00.000Z", "updatedAt": "2026-02-01T12:00:00.000Z" }}Archive or restore several study sets
Archives up to 500 sets in one call, or restores them with archived: false. An archived set keeps its terms and review history but leaves reviews, practice and the study picker. Sets that aren't the user's, are in the trash or are already in that state are skipped. To retire every set of a course that is over, archive its subject instead (PATCH /subjects/{subjectId}).
Required scopes: sets: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 <= 255application/json- body
setIds*array<>1-500 study set ids.
1 <= items <= 500archived*booleantrue archives the sets, false restores them.
OK
application/json- response
data*curl -X POST 'https://workroad.app/api/v1/sets/archive' \ -H 'Authorization: Bearer $WORKROAD_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "setIds": [ "2b2b1882-988f-491b-855c-b513bf31a936" ], "archived": true}'{ "data": { "count": 1 }}Move a study set to the trash
The set disappears from every read. Its terms, images and review history stay as they are, and POST /sets/{setId}/restore brings it back. The trash is emptied after 30 days, which removes the set's review history for good.
Required scopes: sets:delete
AuthorizationBearer <token>setId*stringStudy set id, scoped to the key's user.
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$uuidOK
application/json- response
data*curl -X DELETE 'https://workroad.app/api/v1/sets/497f6eca-6276-4993-bfeb-53cbbbba6f08' \ -H 'Authorization: Bearer $WORKROAD_API_KEY'{ "data": { "id": "7e6d5c4b-3a2f-4e1d-8c0b-9a8f7e6d5c4b", "deleted": true }}Restore a study set from the trash
Brings a trashed set back with its terms, images and review history, and returns it as GET /sets/{setId} would. Needs sets:delete, the scope that trashed it. A set whose folder is still in the trash comes back unfiled.
Required scopes: sets:delete
AuthorizationBearer <token>setId*stringStudy set id, scoped to the key's user.
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$uuidIdempotency-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/sets/497f6eca-6276-4993-bfeb-53cbbbba6f08/restore' \ -H 'Authorization: Bearer $WORKROAD_API_KEY'{ "data": { "id": "7e6d5c4b-3a2f-4e1d-8c0b-9a8f7e6d5c4b", "name": "Spanish verbs", "description": "Irregular preterite forms.", "subjectId": "5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f", "archived": false, "favorite": false, "termCount": 1, "createdAt": "2026-01-15T09:30:00.000Z", "lastUsed": "2026-02-01T12:00:00.000Z", "updatedAt": "2026-02-01T12:00:00.000Z", "terms": [ { "id": "2a1b0c9d-8e7f-4a6b-9c5d-4e3f2a1b0c9d", "term": "tener (yo)", "definition": "tuve", "hints": [ "irregular stem tuv-" ] } ], "termsTruncated": false }}Add terms to a study set
Appends up to 500 terms per call. Existing terms and their review history stay as they are. A set holds at most 2000 terms, and a call that would pass that adds nothing.
Use this to save content you wrote yourself. It uses none of the user's WorkRoad AI uses.
Required scopes: sets:update
AuthorizationBearer <token>setId*stringStudy set id, scoped to the key's user.
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$uuidIdempotency-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 <= 255application/json- body
terms*array<>1-500 terms per call.
1 <= items <= 500Created
application/json- response
data*curl -X POST 'https://workroad.app/api/v1/sets/497f6eca-6276-4993-bfeb-53cbbbba6f08/terms' \ -H 'Authorization: Bearer $WORKROAD_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "terms": [ { "term": "string", "definition": "string" } ]}'{ "data": { "terms": [ { "id": "6c5b4a39-2817-4f06-9e5d-4c3b2a190817", "term": "ser (yo)", "definition": "fui", "hints": [] } ], "termCount": 2 }}Update several terms
Changes up to 500 terms of one set in one call. Send each term's termId with only the fields to change. Every term keeps its id, image and review history. All the changes are saved together, or none when a termId isn't the set's.
Use this to save content you wrote yourself. It uses none of the user's WorkRoad AI uses.
Required scopes: sets:update
AuthorizationBearer <token>setId*stringStudy set id, scoped to the key's user.
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$uuidapplication/json- body
terms*array<>1-500 terms, each with its termId and only the fields to change.
1 <= items <= 500OK
application/json- response
data*curl -X PATCH 'https://workroad.app/api/v1/sets/497f6eca-6276-4993-bfeb-53cbbbba6f08/terms' \ -H 'Authorization: Bearer $WORKROAD_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "terms": [ { "termId": "928ed303-93ba-4a4d-ad80-c8fb71f9e31e" } ]}'{ "data": { "terms": [ { "id": "2a1b0c9d-8e7f-4a6b-9c5d-4e3f2a1b0c9d", "term": "tener (yo)", "definition": "tuve", "hints": [ "irregular stem tuv-" ] } ] }}Update a term
Changes the term, definition or hints. Only the fields you send change. The term keeps its id, image and review history.
Required scopes: sets:update
AuthorizationBearer <token>setId*stringStudy set id, scoped to the key's user.
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$uuidtermId*stringTerm id within that set.
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$uuidapplication/json- body
Send only the fields to change. An omitted field stays as it is.
term?string1 <= length <= 2000definition?string1 <= length <= 4000hints?array<>Replaces the full list when sent.
items <= 10OK
application/json- response
data*curl -X PATCH 'https://workroad.app/api/v1/sets/497f6eca-6276-4993-bfeb-53cbbbba6f08/terms/497f6eca-6276-4993-bfeb-53cbbbba6f08' \ -H 'Authorization: Bearer $WORKROAD_API_KEY' \ -H 'Content-Type: application/json' \ -d '{}'{ "data": { "id": "2a1b0c9d-8e7f-4a6b-9c5d-4e3f2a1b0c9d", "term": "tener (yo)", "definition": "tuve", "hints": [ "irregular stem tuv-" ] }}Delete several terms
Deletes up to 500 terms of one set in one call, with their images. This permanently removes each term's flashcard review history. The set keeps at least one term: a call that would delete them all deletes nothing; delete the set instead. Nothing is deleted when a termId isn't the set's.
Required scopes: sets:delete
AuthorizationBearer <token>setId*stringStudy set id, scoped to the key's user.
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$uuidIdempotency-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 <= 255application/json- body
termIds*array<>1-500 ids of this set's terms.
1 <= items <= 500OK
application/json- response
data*curl -X POST 'https://workroad.app/api/v1/sets/497f6eca-6276-4993-bfeb-53cbbbba6f08/terms/delete' \ -H 'Authorization: Bearer $WORKROAD_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "termIds": [ "9b14b0fc-df3a-498f-8682-731209211a87" ]}'{ "data": { "termIds": [ "2a1b0c9d-8e7f-4a6b-9c5d-4e3f2a1b0c9d" ], "deleted": true }}Delete a term
Deletes the term and its image. This permanently removes the term's flashcard review history and scheduling; nothing can restore them. To change a term's wording, update it instead. Requires sets:delete because it erases review history. A set's last term cannot be deleted; delete the set instead.
Required scopes: sets:delete
AuthorizationBearer <token>setId*stringStudy set id, scoped to the key's user.
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$uuidtermId*stringTerm id within that set.
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$uuidOK
application/json- response
data*curl -X DELETE 'https://workroad.app/api/v1/sets/497f6eca-6276-4993-bfeb-53cbbbba6f08/terms/497f6eca-6276-4993-bfeb-53cbbbba6f08' \ -H 'Authorization: Bearer $WORKROAD_API_KEY'{ "data": { "id": "2a1b0c9d-8e7f-4a6b-9c5d-4e3f2a1b0c9d", "deleted": true }}