Categories
Coloured categories that sort notes.
On this page
List categories
GET/categories
Categories, in the owner's order.
curl "$NIFTY_URL/api/v1/categories" \
-H "Authorization: Bearer $NIFTY_TOKEN"
Query parameters
| Name | Description |
|---|---|
orderposition | id | name | updated_at · optional | The field to sort by; ties are broken by id, so the order is stable. |
directionasc | desc · optional | asc (ascending) or desc (descending), for order. |
updated_sincedate-time · optional | Only records updated at or after this time (ISO 8601 with Z or a UTC offset), ordered by updated_at, id. |
cursorstring · optional | meta.next_cursor from the previous page. |
limitinteger · optional | Items per page, up to 100; a larger number is taken as 100. |
counttrue | false · optional | true adds meta.total_count: the size of the filtered set (with updated_since too), ignoring cursor and limit. Anything but true or false is a 400. |
Response
200 OK Errors: 400 401 429
{
"data": [
{
"id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"name": "Journal",
"color": "blue",
"icon": "book-open",
"position": 1,
"created_at": "2026-10-10T09:30:00Z",
"updated_at": "2026-10-10T09:30:00Z"
}
],
"meta": {
"next_cursor": "eyJrIjpbIjIwMjYtMTAtMDlUMDg6MzA6MDBaIl19",
"limit": 1,
"total_count": 1
}
}
Add a category
POST/categories
Goes last unless position is sent. A blank color gets the first one no other category uses. A name that
another category has, ignoring case, is a 422.
curl -X POST "$NIFTY_URL/api/v1/categories" \
-H "Authorization: Bearer $NIFTY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"category":{"name":"Journal","color":"blue","icon":"book-open"}}'
Headers
| Name | Description |
|---|---|
Idempotency-Keystring · optional | Makes a POST safe to retry for 24 hours. Needs a bearer token (400 under session auth). |
Request body
| Name | Description |
|---|---|
categoryobject | |
category.namestring · optional | Spaces are squished. Unique ignoring case. |
category.colorstring or null · optional | A key from GET /category_choices. Blank picks the first one no other category uses. |
category.iconstring or null · optional | An icon from GET /category_choices, or null for none. |
category.positioninteger · optional | 1-based; out-of-range values are clamped. Moves the category there. |
Response
201 Created Errors: 400 401 403 409 415 422 429
{
"data": {
"id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"name": "Journal",
"color": "blue",
"icon": "book-open",
"position": 1,
"created_at": "2026-10-10T09:30:00Z",
"updated_at": "2026-10-10T09:30:00Z"
}
}
Get a category
GET/categories/{id}
curl "$NIFTY_URL/api/v1/categories/01j9zq3k8m5x2v7c4n6b0t1r9e" \
-H "Authorization: Bearer $NIFTY_TOKEN"
Path parameters
| Name | Description |
|---|---|
idULID | A record's ID. |
Response
200 OK Errors: 401 404 429
{
"data": {
"id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"name": "Journal",
"color": "blue",
"icon": "book-open",
"position": 1,
"created_at": "2026-10-10T09:30:00Z",
"updated_at": "2026-10-10T09:30:00Z"
}
}
Change or move a category
PATCH/categories/{id}
Also PUT /categories/{id}, the same.
Only the fields sent change. Sending position moves the category there and renumbers every
category 1..n; the others whose position changed get a new updated_at, so re-sync with
updated_since after a move.
curl -X PATCH "$NIFTY_URL/api/v1/categories/01j9zq3k8m5x2v7c4n6b0t1r9e" \
-H "Authorization: Bearer $NIFTY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"category":{"name":"Journal","color":"blue","icon":"book-open"}}'
Path parameters
| Name | Description |
|---|---|
idULID | A record's ID. |
Request body
| Name | Description |
|---|---|
categoryobject | |
category.namestring · optional | Spaces are squished. Unique ignoring case. |
category.colorstring or null · optional | A key from GET /category_choices. Blank picks the first one no other category uses. |
category.iconstring or null · optional | An icon from GET /category_choices, or null for none. |
category.positioninteger · optional | 1-based; out-of-range values are clamped. Moves the category there. |
Response
200 OK Errors: 400 401 403 404 415 422 429
{
"data": {
"id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"name": "Journal",
"color": "blue",
"icon": "book-open",
"position": 1,
"created_at": "2026-10-10T09:30:00Z",
"updated_at": "2026-10-10T09:30:00Z"
}
}
Delete a category
DELETE/categories/{id}
Permanent. Writes a category deletion record. Other categories keep their positions (gaps are fine). Its
notes become uncategorised, with a new updated_at. With if_unused=true it's deleted only if no note, live or
deleted, has it; otherwise 409 in_use and nothing changes (an Undo of a just-created category).
curl -X DELETE "$NIFTY_URL/api/v1/categories/01j9zq3k8m5x2v7c4n6b0t1r9e" \
-H "Authorization: Bearer $NIFTY_TOKEN"
Path parameters
| Name | Description |
|---|---|
idULID | A record's ID. |
Query parameters
| Name | Description |
|---|---|
if_unusedtrue | false · optional | true: delete only if no note has it. false or left out: always. Anything else is 400. |
Response
204 Deleted Errors: 400 401 403 404 409 429
List category colours and icons
GET/category_choices
The colours and icons a category can use.
Not paginated. Show colours by label; render icons from Lucide by name.
curl "$NIFTY_URL/api/v1/category_choices" \
-H "Authorization: Bearer $NIFTY_TOKEN"
Response
200 OK Errors: 401 429
{
"data": {
"colors": [
{
"key": "teal",
"label": "Teal"
}
],
"icons": [
"briefcase"
]
}
}
Note counts per category
GET/category_note_counts
How many live notes each category has.
Not a record and not paginated: there's nothing to sync, so ask again when notes change. Ordered by
category_id; categories with no live notes are left out.
curl "$NIFTY_URL/api/v1/category_note_counts" \
-H "Authorization: Bearer $NIFTY_TOKEN"
Response
200 OK Errors: 401 429
{
"data": [
{
"category_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"note_count": 1
}
]
}