Skip to content
Niftyhelp 2026.10.1-beta.1 Support

Beta This is the help for Nifty 2026.10.1-beta.1, which isn't released yet. Help for the current release

Documents

PDFs in folders, linked to notes, projects, tasks and people.

On this page

List folders

GET/folders

Folders of documents, alphabetical.

Alphabetical ignores case and accents ("apple", "Banana", "éclair"); ties by id. Filters are for browsing, so sync unfiltered.

curl "$NIFTY_URL/api/v1/folders" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Query parameters

NameDescription
parent_idULID · optionalOnly this folder's subfolders. Not with root.
roottrue · optionaltrue: only top-level folders. Anything else, or with parent_id, is 400.
ordername | id | updated_at · optionalThe field to sort by; ties are broken by id, so the order is stable.
directionasc | desc · optionalasc (ascending) or desc (descending), for order.
updated_sincedate-time · optionalOnly records updated at or after this time (ISO 8601 with Z or a UTC offset), ordered by updated_at, id.
cursorstring · optionalmeta.next_cursor from the previous page.
limitinteger · optionalItems per page, up to 100; a larger number is taken as 100.
counttrue | false · optionaltrue 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": "Receipts",
      "parent_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
      "color": "amber",
      "icon": "wallet",
      "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 folder

POST/folders

Needs a name. Top level unless parent_id is sent. A blank color gets the first one no other folder uses (colours and icons are the vocabulary's colors and picker_icons, as GET /category_choices lists them).

curl -X POST "$NIFTY_URL/api/v1/folders" \
  -H "Authorization: Bearer $NIFTY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"folder":{"name":"Receipts","parent_id":null}}'

Headers

NameDescription
Idempotency-Keystring · optionalMakes a POST safe to retry for 24 hours. Needs a bearer token (400 under session auth).

Request body

NameDescription
folderobject
folder.namestring · optionalSpaces are squished. A name a sibling has, ignoring case, is a 422.
folder.parent_idULID or null · optionalThe folder it's in; null for the top level. Not an id, unknown, itself or one inside it: a 422.
folder.colorstring or null · optionalA key from the vocabulary's colors (or GET /category_choices). Blank picks the first one no other folder uses.
folder.iconstring or null · optionalAn icon from the vocabulary's picker_icons (or GET /category_choices), or null for the default folder icon.

Response

201 Created Errors: 400 401 403 409 415 422 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "name": "Receipts",
    "parent_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "color": "amber",
    "icon": "wallet",
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Get a folder

GET/folders/{id}

curl "$NIFTY_URL/api/v1/folders/01j9zq3k8m5x2v7c4n6b0t1r9e" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Path parameters

NameDescription
idULIDA record's ID.

Response

200 OK Errors: 401 404 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "name": "Receipts",
    "parent_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "color": "amber",
    "icon": "wallet",
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Change a folder

PATCH/folders/{id}

Also PUT /folders/{id}, the same.

Rename, recolour or move a folder.

Only the fields sent change. Moving is changing parent_id (null: the top level); into itself or a folder inside it is a 422 on parent_id. Documents hold only folder_id, so no document changes.

curl -X PATCH "$NIFTY_URL/api/v1/folders/01j9zq3k8m5x2v7c4n6b0t1r9e" \
  -H "Authorization: Bearer $NIFTY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"folder":{"name":"Receipts","parent_id":null}}'

Path parameters

NameDescription
idULIDA record's ID.

Request body

NameDescription
folderobject
folder.namestring · optionalSpaces are squished. A name a sibling has, ignoring case, is a 422.
folder.parent_idULID or null · optionalThe folder it's in; null for the top level. Not an id, unknown, itself or one inside it: a 422.
folder.colorstring or null · optionalA key from the vocabulary's colors (or GET /category_choices). Blank picks the first one no other folder uses.
folder.iconstring or null · optionalAn icon from the vocabulary's picker_icons (or GET /category_choices), or null for the default folder icon.

Response

200 OK Errors: 400 401 403 404 415 422 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "name": "Receipts",
    "parent_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "color": "amber",
    "icon": "wallet",
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Delete an empty folder

DELETE/folders/{id}

Permanent, with a folder deletion record. Only an empty folder (no subfolders, no documents) can be deleted; otherwise it's 409 folder_not_empty. Documents deleted in the last 20 seconds that were in it lose their folder_id (and get a new updated_at), so restoring one brings it back unfiled.

curl -X DELETE "$NIFTY_URL/api/v1/folders/01j9zq3k8m5x2v7c4n6b0t1r9e" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Path parameters

NameDescription
idULIDA record's ID.

Response

204 Deleted Errors: 401 403 404 409 429

Document counts per folder

GET/folder_counts

How many live documents each folder holds directly, and their size.

Not a record and not paginated: there's nothing to sync, so ask again when documents change. Ordered by folder_id, the null row (documents in no folder) first; folders with no live documents are left out, and so is the null row when every document is in a folder. Subfolders' documents aren't counted: add them up from parent_id. A deleted document isn't counted, even while it can be restored.

curl "$NIFTY_URL/api/v1/folder_counts" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Response

200 OK Errors: 401 429

{
  "data": [
    {
      "folder_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
      "document_count": 1,
      "byte_size": 1
    }
  ]
}

List documents

GET/documents

Documents (PDFs), alphabetical.

Live documents. Alphabetical ignores case and accents; ties by id. Filters combine (AND), except unsorted; they're for browsing, so sync unfiltered. Search by name with GET /search?types=document.

curl "$NIFTY_URL/api/v1/documents" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Query parameters

NameDescription
folder_idULID · optionalOnly this folder's documents (not its subfolders', unless include_subfolders).
include_subfoldersboolean · optionaltrue: with folder_id, its documents and those in every folder below it, at any depth. Without folder_id it's 400.
unsortedtrue · optionaltrue: only documents with no folder and no link to a live note, project, task or person. Not with any other filter; anything else is 400.
note_idULID · optionalOnly documents linked to this note. At most one of note_id, project_id, task_id and person_id (two is 400); an unknown or deleted one is an empty page.
project_idULID · optionalA record's ID.
task_idULID · optionalA record's ID.
person_idULID · optionalA record's ID.
ordername | created_at | updated_at | id · optionalThe field to sort by; ties are broken by id, so the order is stable.
directionasc | desc · optionalasc (ascending) or desc (descending), for order.
updated_sincedate-time · optionalOnly records updated at or after this time (ISO 8601 with Z or a UTC offset), ordered by updated_at, id.
cursorstring · optionalmeta.next_cursor from the previous page.
limitinteger · optionalItems per page, up to 100; a larger number is taken as 100.
counttrue | false · optionaltrue 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": "Boiler warranty",
      "folder_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
      "note_ids": [
        "01j9zq3k8m5x2v7c4n6b0t1r9e"
      ],
      "project_ids": [
        "01j9zq3k8m5x2v7c4n6b0t1r9e"
      ],
      "task_ids": [
        "01j9zq3k8m5x2v7c4n6b0t1r9e"
      ],
      "person_ids": [
        "01j9zq3k8m5x2v7c4n6b0t1r9e"
      ],
      "links": [
        {
          "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
          "note_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
          "project_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
          "task_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
          "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
        }
      ],
      "file": {
        "filename": "string",
        "byte_size": 1,
        "content_type": "application/pdf",
        "url": "https://example.com",
        "download_url": "https://example.com",
        "thumbnail_url": "https://example.com"
      },
      "created_at": "2026-10-10T09:30:00Z",
      "updated_at": "2026-10-10T09:30:00Z"
    }
  ],
  "meta": {
    "next_cursor": "eyJrIjpbIjIwMjYtMTAtMDlUMDg6MzA6MDBaIl19",
    "limit": 1,
    "total_count": 1
  }
}

Upload a PDF

POST/documents

A multipart/form-data body (any other type is 415) with the PDF in file and, optionally, the other fields. The type is read from the bytes: anything but a PDF is a 422 on file, whatever its declared type or name. Up to 50 MB. A body without a Content-Length is 411, and one over 50 MB (plus 1 MB for the other fields) is 413, both before anything is read, even the token. With no folder and no links it's Unsorted.

Idempotency-Key compares the fields and the file's bytes, not the raw body, so a retry with new multipart boundaries is the same request.

curl -X POST "$NIFTY_URL/api/v1/documents" \
  -H "Authorization: Bearer $NIFTY_TOKEN" \
  -F '[email protected]'

Headers

NameDescription
Idempotency-Keystring · optionalMakes a POST safe to retry for 24 hours. Needs a bearer token (400 under session auth).

Request body

NameDescription
filefileThe PDF. Its file name (1–255 characters, no NUL) is kept as file.filename.
namestring · optionalSpaces are squished. Blank: the file name without .pdf.
folder_idULID · optionalA record's ID.
note_ids[]ULID[] · optional
project_ids[]ULID[] · optional
task_ids[]ULID[] · optional
person_ids[]ULID[] · optional

Response

201 Created Errors: 400 401 403 409 411 413 415 422 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "name": "Boiler warranty",
    "folder_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "note_ids": [
      "01j9zq3k8m5x2v7c4n6b0t1r9e"
    ],
    "project_ids": [
      "01j9zq3k8m5x2v7c4n6b0t1r9e"
    ],
    "task_ids": [
      "01j9zq3k8m5x2v7c4n6b0t1r9e"
    ],
    "person_ids": [
      "01j9zq3k8m5x2v7c4n6b0t1r9e"
    ],
    "links": [
      {
        "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "note_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "project_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "task_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
      }
    ],
    "file": {
      "filename": "string",
      "byte_size": 1,
      "content_type": "application/pdf",
      "url": "https://example.com",
      "download_url": "https://example.com",
      "thumbnail_url": "https://example.com"
    },
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Get a document

GET/documents/{id}

A deleted document is a 404.

curl "$NIFTY_URL/api/v1/documents/01j9zq3k8m5x2v7c4n6b0t1r9e" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Path parameters

NameDescription
idULIDA record's ID.

Response

200 OK Errors: 401 404 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "name": "Boiler warranty",
    "folder_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "note_ids": [
      "01j9zq3k8m5x2v7c4n6b0t1r9e"
    ],
    "project_ids": [
      "01j9zq3k8m5x2v7c4n6b0t1r9e"
    ],
    "task_ids": [
      "01j9zq3k8m5x2v7c4n6b0t1r9e"
    ],
    "person_ids": [
      "01j9zq3k8m5x2v7c4n6b0t1r9e"
    ],
    "links": [
      {
        "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "note_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "project_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "task_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
      }
    ],
    "file": {
      "filename": "string",
      "byte_size": 1,
      "content_type": "application/pdf",
      "url": "https://example.com",
      "download_url": "https://example.com",
      "thumbnail_url": "https://example.com"
    },
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Change a document

PATCH/documents/{id}

Also PUT /documents/{id}, the same.

Rename, file or link a document.

JSON. Only the fields sent change. Each id list replaces that kind's links (to live records; links to ones deleted in the last 20 seconds are kept, so undoing their delete restores them). The file can't be replaced: delete the document and upload again.

curl -X PATCH "$NIFTY_URL/api/v1/documents/01j9zq3k8m5x2v7c4n6b0t1r9e" \
  -H "Authorization: Bearer $NIFTY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"document":{"name":"Boiler warranty","folder_id":"01j9zq3f0a2b4c6d8e0f2g4h6j","project_ids":["01j9zq3p4q6r8s0t2v5w7x9y1z"]}}'

Path parameters

NameDescription
idULIDA record's ID.

Request body

NameDescription
documentDocumentWrite
document.namestring · optionalSpaces are squished. Needn't be unique.
document.folder_idULID or null · optionalNull: no folder. Not an id, or an unknown folder, is a 422.
document.note_idsULID[] or null · optionalReplaces its links to notes; null or [] removes them. An unknown or deleted id, or one that isn't an id, is a 422 on the field.
document.project_idsULID[] or null · optional
document.task_idsULID[] or null · optionalA task in a deleted list, person or project counts as deleted.
document.person_idsULID[] or null · optional

Response

200 OK Errors: 400 401 403 404 415 422 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "name": "Boiler warranty",
    "folder_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "note_ids": [
      "01j9zq3k8m5x2v7c4n6b0t1r9e"
    ],
    "project_ids": [
      "01j9zq3k8m5x2v7c4n6b0t1r9e"
    ],
    "task_ids": [
      "01j9zq3k8m5x2v7c4n6b0t1r9e"
    ],
    "person_ids": [
      "01j9zq3k8m5x2v7c4n6b0t1r9e"
    ],
    "links": [
      {
        "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "note_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "project_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "task_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
      }
    ],
    "file": {
      "filename": "string",
      "byte_size": 1,
      "content_type": "application/pdf",
      "url": "https://example.com",
      "download_url": "https://example.com",
      "thumbnail_url": "https://example.com"
    },
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Delete a document

DELETE/documents/{id}

Restorable for 20 seconds (POST /documents/{id}/restoration), with its folder and links; then permanent, file and all. Gone from every endpoint at once, with a document deletion record. Deleting again is a 404.

curl -X DELETE "$NIFTY_URL/api/v1/documents/01j9zq3k8m5x2v7c4n6b0t1r9e" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Path parameters

NameDescription
idULIDA record's ID.

Response

204 Deleted Errors: 401 403 404 429

Restore a document

POST/documents/{document_id}/restoration

Undo deleting a document.

Within 20 seconds of DELETE /documents/{id}: brings it back with its folder and links. Its deletion record is removed and it gets a new updated_at. No body. Restoring one that isn't deleted changes nothing and returns it, so a retry is safe. Once the 20 seconds have passed, or for an unknown id, it's a 404.

curl -X POST "$NIFTY_URL/api/v1/documents/01j9zq3k8m5x2v7c4n6b0t1r9e/restoration" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Path parameters

NameDescription
document_idULIDA record's ID.

Headers

NameDescription
Idempotency-Keystring · optionalMakes a POST safe to retry for 24 hours. Needs a bearer token (400 under session auth).

Response

200 Restored. The document, as GET /documents/{id} returns it. Errors: 400 401 403 404 409 415 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "name": "Boiler warranty",
    "folder_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "note_ids": [
      "01j9zq3k8m5x2v7c4n6b0t1r9e"
    ],
    "project_ids": [
      "01j9zq3k8m5x2v7c4n6b0t1r9e"
    ],
    "task_ids": [
      "01j9zq3k8m5x2v7c4n6b0t1r9e"
    ],
    "person_ids": [
      "01j9zq3k8m5x2v7c4n6b0t1r9e"
    ],
    "links": [
      {
        "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "note_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "project_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "task_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
      }
    ],
    "file": {
      "filename": "string",
      "byte_size": 1,
      "content_type": "application/pdf",
      "url": "https://example.com",
      "download_url": "https://example.com",
      "thumbnail_url": "https://example.com"
    },
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Download a document's PDF

GET/documents/{document_id}/file

The PDF itself.

The file, with Content-Type: application/pdf, X-Content-Type-Options: nosniff and Cache-Control: private, max-age=0 (no-store under the web session). Content-Disposition names it after the document's current name plus .pdf. Send a single Range (e.g. bytes=0-65535) for part of it (206, with Content-Range); several ranges get the whole file. Needs the token (or the web session) like any endpoint: there's no public URL. The web app shows it inline in a same-origin <iframe> (X-Frame-Options: SAMEORIGIN).

curl "$NIFTY_URL/api/v1/documents/01j9zq3k8m5x2v7c4n6b0t1r9e/file" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Path parameters

NameDescription
document_idULIDA record's ID.

Query parameters

NameDescription
dispositioninline | attachment · optionalattachment for a download; anything but inline or attachment is 400.

Headers

NameDescription
Rangestring · optional

Response

200 The whole PDF. Errors: 400 401 404 416 429

"@file.pdf"

Get a document's thumbnail

GET/documents/{document_id}/thumbnail

A JPEG of the first page.

The first page, 480 pixels wide (at most 1440 tall) on white, with Content-Type: image/jpeg, X-Content-Type-Options: nosniff and Cache-Control: private, max-age=0 (no-store under the web session). 404 when the document has none (file.thumbnail_url is null). Needs the token (or the web session).

curl "$NIFTY_URL/api/v1/documents/01j9zq3k8m5x2v7c4n6b0t1r9e/thumbnail" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Path parameters

NameDescription
document_idULIDA record's ID.

Response

200 The JPEG. Errors: 401 404 429

"@file.pdf"

POST/document_links

Link a document to a note, project, task or person.

Exactly one target. The document gets a new updated_at and lists the link in links. An unknown or deleted document or target (a task in a deleted list, person or project counts as deleted), no target or two, or one already linked, is a 422 on the field.

curl -X POST "$NIFTY_URL/api/v1/document_links" \
  -H "Authorization: Bearer $NIFTY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"document_link":{"document_id":"01j9zq3d1e3f5g7h9j1k3m5n7p","note_id":"01j9zq3n6p8q0r2s4t6v8w0x2y"}}'

Headers

NameDescription
Idempotency-Keystring · optionalMakes a POST safe to retry for 24 hours. Needs a bearer token (400 under session auth).

Request body

NameDescription
document_linkDocumentLinkWritedocument_id and exactly one of note_id, project_id, task_id and person_id.
document_link.document_idULIDA record's ID.
document_link.note_idULID or null · optionalA record's ID.
document_link.project_idULID or null · optionalA record's ID.
document_link.task_idULID or null · optionalA record's ID.
document_link.person_idULID or null · optionalA record's ID.

Response

201 Created Errors: 400 401 403 409 415 422 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "document_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "note_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "project_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "task_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "created_at": "2026-10-10T09:30:00Z"
  }
}

DELETE/document_links/{id}

Permanent; the document stays (in Unsorted if that was its last folder or link) and gets a new updated_at. An unknown link, one whose document is deleted, or one to a deleted record (not in links; it comes back if that record's delete is undone) is a 404.

curl -X DELETE "$NIFTY_URL/api/v1/document_links/01j9zq3k8m5x2v7c4n6b0t1r9e" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Path parameters

NameDescription
idULIDA record's ID.

Response

204 Deleted Errors: 401 403 404 429

Menu

2026.10.1-beta.1Contact support