Skip to content
Niftyhelp 2026.10.2 Support

Documents

PDFs, emails and images 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}

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 10 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. Emails' attachments aren't counted (they're inside their email), but their size is.

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 and emails), 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.

An email's PDF attachments (email_id set) live inside it: folder_id, include_subfolders and unsorted leave them out, as does include_attachments=false; email_id lists one email's. Unfiltered and the link filters include them.

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), without attachments.
email_idULID · optionalOnly this email's PDF attachments. Not with unsorted (400).
include_attachmentsboolean · optionalfalse: leave out emails' PDF attachments (the Library home's newest). With email_id it's 400.
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, and not an email's attachment. 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": [
    {
      "received_via_api": {
        "at": "2026-10-10T09:30:00Z",
        "source": "Zapier"
      },
      "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
      "kind": "pdf",
      "name": "Boiler warranty",
      "dated_on": "2026-10-10",
      "dated_on_set": true,
      "folder_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
      "email_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
      "email": {
        "subject": "string",
        "from": {
          "name": "Ada Lovelace",
          "address": "[email protected]",
          "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
        },
        "to": [
          {
            "name": "Ada Lovelace",
            "address": "[email protected]",
            "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
          }
        ],
        "cc": [
          {
            "name": "Ada Lovelace",
            "address": "[email protected]",
            "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
          }
        ],
        "sent_at": "2026-10-10T09:30:00Z",
        "forwarded": true,
        "forward_note": "string",
        "forwarded_at": "2026-10-10T09:30:00Z",
        "has_html": true,
        "remote_images": true,
        "body_url": "https://example.com",
        "attachments": [
          {
            "filename": "string",
            "content_type": "application/pdf",
            "byte_size": 1,
            "status": "saved",
            "document_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
          }
        ]
      },
      "from_email": {
        "subject": "string",
        "from": {
          "name": "Ada Lovelace",
          "address": "[email protected]",
          "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
        },
        "sent_at": "2026-10-10T09:30:00Z",
        "forwarded_at": "2026-10-10T09:30:00Z"
      },
      "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",
        "width": 1,
        "height": 1,
        "url": "https://example.com",
        "download_url": "https://example.com",
        "thumbnail_url": "https://example.com"
      },
      "analysis": {
        "status": "reading",
        "type": "payslip",
        "type_label": "Payslip"
      },
      "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 or an image

POST/documents

A multipart/form-data body (any other type is 415) with the PDF or image in file and, optionally, the other fields. The type is read from the bytes: anything but a PDF or a PNG, JPEG or WebP image is a 422 on file, whatever its declared type or name (a HEIC photo says to convert it to JPEG first). Up to 50 MB. An image (kind: image) must be at least 200 × 200 pixels' worth (40,000), no side under 64, and at most 40 megapixels. It's kept as sent; a JPEG's EXIF orientation is applied to its size, thumbnail and text. 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 or image. Its file name (1–255 characters, no NUL) is kept as file.filename.
namestring · optionalSpaces are squished. Blank: the file name without its extension (.pdf, .png, .jpg, .jpeg, .webp).
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": {
    "received_via_api": {
      "at": "2026-10-10T09:30:00Z",
      "source": "Zapier"
    },
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "kind": "pdf",
    "name": "Boiler warranty",
    "dated_on": "2026-10-10",
    "dated_on_set": true,
    "folder_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "email_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "email": {
      "subject": "string",
      "from": {
        "name": "Ada Lovelace",
        "address": "[email protected]",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
      },
      "to": [
        {
          "name": "Ada Lovelace",
          "address": "[email protected]",
          "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
        }
      ],
      "cc": [
        {
          "name": "Ada Lovelace",
          "address": "[email protected]",
          "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
        }
      ],
      "sent_at": "2026-10-10T09:30:00Z",
      "forwarded": true,
      "forward_note": "string",
      "forwarded_at": "2026-10-10T09:30:00Z",
      "has_html": true,
      "remote_images": true,
      "body_url": "https://example.com",
      "attachments": [
        {
          "filename": "string",
          "content_type": "application/pdf",
          "byte_size": 1,
          "status": "saved",
          "document_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
        }
      ]
    },
    "from_email": {
      "subject": "string",
      "from": {
        "name": "Ada Lovelace",
        "address": "[email protected]",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
      },
      "sent_at": "2026-10-10T09:30:00Z",
      "forwarded_at": "2026-10-10T09:30:00Z"
    },
    "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",
      "width": 1,
      "height": 1,
      "url": "https://example.com",
      "download_url": "https://example.com",
      "thumbnail_url": "https://example.com"
    },
    "analysis": {
      "status": "reading",
      "type": "payslip",
      "type_label": "Payslip"
    },
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Import an email

POST/email_imports

Adds an email (an .eml file) as documents.

A multipart/form-data body (any other type is 415) with the email in file and, optionally, a folder and links, as POST /documents takes them. 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. A file that can't be read as an email is a 422 on file.

A forwarded email is imported as the original it forwards (the innermost, when forwards are nested): its sender, recipients, date, subject, body and attachments. Its PDF attachments (read from their bytes, whatever their declared type) become documents. Other attachments aren't kept: email.attachments lists them.

An email with PDFs and no subject, or whose body says nothing beyond greetings, stock phrases ("Please find attached"), sign-offs, signatures and disclaimers, isn't kept as an email: its PDFs are added as ordinary documents (email null), each with from_email. Otherwise it's an email document (kind: email) with its PDFs inside it (email_id).

An email whose Message-ID is already in Nifty (a live email, or documents added from it) isn't imported again: 200 with what's there and duplicate: true. It gains the links sent (the email, else each document added from it); folder_id is ignored, so it stays where it's filed. Idempotency-Key compares the fields and the file's bytes, as POST /documents.

curl -X POST "$NIFTY_URL/api/v1/email_imports" \
  -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 .eml file (1–255 character name, no NUL).
folder_idULID · optionalA record's ID.
note_ids[]ULID[] · optional
project_ids[]ULID[] · optional
task_ids[]ULID[] · optional
person_ids[]ULID[] · optional

Response

200 Already imported (duplicate: true). Errors: 400 401 403 409 411 413 415 422 429

{
  "data": {
    "email": {
      "received_via_api": {
        "at": "2026-10-10T09:30:00Z",
        "source": "Zapier"
      },
      "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
      "kind": "pdf",
      "name": "Boiler warranty",
      "dated_on": "2026-10-10",
      "dated_on_set": true,
      "folder_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
      "email_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
      "email": {
        "subject": "string",
        "from": {
          "name": "Ada Lovelace",
          "address": "[email protected]",
          "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
        },
        "to": [
          {
            "name": "Ada Lovelace",
            "address": "[email protected]",
            "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
          }
        ],
        "cc": [
          {
            "name": "Ada Lovelace",
            "address": "[email protected]",
            "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
          }
        ],
        "sent_at": "2026-10-10T09:30:00Z",
        "forwarded": true,
        "forward_note": "string",
        "forwarded_at": "2026-10-10T09:30:00Z",
        "has_html": true,
        "remote_images": true,
        "body_url": "https://example.com",
        "attachments": [
          {
            "filename": "string",
            "content_type": "application/pdf",
            "byte_size": 1,
            "status": "saved",
            "document_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
          }
        ]
      },
      "from_email": {
        "subject": "string",
        "from": {
          "name": "Ada Lovelace",
          "address": "[email protected]",
          "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
        },
        "sent_at": "2026-10-10T09:30:00Z",
        "forwarded_at": "2026-10-10T09:30:00Z"
      },
      "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",
        "width": 1,
        "height": 1,
        "url": "https://example.com",
        "download_url": "https://example.com",
        "thumbnail_url": "https://example.com"
      },
      "analysis": {
        "status": "reading",
        "type": "payslip",
        "type_label": "Payslip"
      },
      "created_at": "2026-10-10T09:30:00Z",
      "updated_at": "2026-10-10T09:30:00Z"
    },
    "documents": [
      {
        "received_via_api": {
          "at": "2026-10-10T09:30:00Z",
          "source": "Zapier"
        },
        "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "kind": "pdf",
        "name": "Boiler warranty",
        "dated_on": "2026-10-10",
        "dated_on_set": true,
        "folder_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "email_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "email": {
          "subject": "string",
          "from": {
            "name": "Ada Lovelace",
            "address": "[email protected]",
            "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
          },
          "to": [
            {
              "name": "Ada Lovelace",
              "address": "[email protected]",
              "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
            }
          ],
          "cc": [
            {
              "name": "Ada Lovelace",
              "address": "[email protected]",
              "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
            }
          ],
          "sent_at": "2026-10-10T09:30:00Z",
          "forwarded": true,
          "forward_note": "string",
          "forwarded_at": "2026-10-10T09:30:00Z",
          "has_html": true,
          "remote_images": true,
          "body_url": "https://example.com",
          "attachments": [
            {
              "filename": "string",
              "content_type": "application/pdf",
              "byte_size": 1,
              "status": "saved",
              "document_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
            }
          ]
        },
        "from_email": {
          "subject": "string",
          "from": {
            "name": "Ada Lovelace",
            "address": "[email protected]",
            "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
          },
          "sent_at": "2026-10-10T09:30:00Z",
          "forwarded_at": "2026-10-10T09:30:00Z"
        },
        "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",
          "width": 1,
          "height": 1,
          "url": "https://example.com",
          "download_url": "https://example.com",
          "thumbnail_url": "https://example.com"
        },
        "analysis": {
          "status": "reading",
          "type": "payslip",
          "type_label": "Payslip"
        },
        "created_at": "2026-10-10T09:30:00Z",
        "updated_at": "2026-10-10T09:30:00Z"
      }
    ],
    "duplicate": true
  }
}

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": {
    "received_via_api": {
      "at": "2026-10-10T09:30:00Z",
      "source": "Zapier"
    },
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "kind": "pdf",
    "name": "Boiler warranty",
    "dated_on": "2026-10-10",
    "dated_on_set": true,
    "folder_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "email_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "email": {
      "subject": "string",
      "from": {
        "name": "Ada Lovelace",
        "address": "[email protected]",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
      },
      "to": [
        {
          "name": "Ada Lovelace",
          "address": "[email protected]",
          "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
        }
      ],
      "cc": [
        {
          "name": "Ada Lovelace",
          "address": "[email protected]",
          "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
        }
      ],
      "sent_at": "2026-10-10T09:30:00Z",
      "forwarded": true,
      "forward_note": "string",
      "forwarded_at": "2026-10-10T09:30:00Z",
      "has_html": true,
      "remote_images": true,
      "body_url": "https://example.com",
      "attachments": [
        {
          "filename": "string",
          "content_type": "application/pdf",
          "byte_size": 1,
          "status": "saved",
          "document_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
        }
      ]
    },
    "from_email": {
      "subject": "string",
      "from": {
        "name": "Ada Lovelace",
        "address": "[email protected]",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
      },
      "sent_at": "2026-10-10T09:30:00Z",
      "forwarded_at": "2026-10-10T09:30:00Z"
    },
    "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",
      "width": 1,
      "height": 1,
      "url": "https://example.com",
      "download_url": "https://example.com",
      "thumbnail_url": "https://example.com"
    },
    "analysis": {
      "status": "reading",
      "type": "payslip",
      "type_label": "Payslip"
    },
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Change a document

PATCH/documents/{id}

Rename, date, 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 10 seconds are kept, so undoing their delete restores them). The file can't be replaced: delete the document and upload again.

An email's attachment stays in its email's folder: a folder_id for one is a 422 (move the email, and its attachments move with it). Moving an email moves its attachments.

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 or project counts as deleted.
document.person_idsULID[] or null · optional
document.dated_ondate or null · optionalYYYY-MM-DD: the owner's choice, which nothing else replaces. Null goes back to Nifty's choice. One that isn't a date is a 422.

Response

200 OK Errors: 400 401 403 404 415 422 429

{
  "data": {
    "received_via_api": {
      "at": "2026-10-10T09:30:00Z",
      "source": "Zapier"
    },
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "kind": "pdf",
    "name": "Boiler warranty",
    "dated_on": "2026-10-10",
    "dated_on_set": true,
    "folder_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "email_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "email": {
      "subject": "string",
      "from": {
        "name": "Ada Lovelace",
        "address": "[email protected]",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
      },
      "to": [
        {
          "name": "Ada Lovelace",
          "address": "[email protected]",
          "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
        }
      ],
      "cc": [
        {
          "name": "Ada Lovelace",
          "address": "[email protected]",
          "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
        }
      ],
      "sent_at": "2026-10-10T09:30:00Z",
      "forwarded": true,
      "forward_note": "string",
      "forwarded_at": "2026-10-10T09:30:00Z",
      "has_html": true,
      "remote_images": true,
      "body_url": "https://example.com",
      "attachments": [
        {
          "filename": "string",
          "content_type": "application/pdf",
          "byte_size": 1,
          "status": "saved",
          "document_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
        }
      ]
    },
    "from_email": {
      "subject": "string",
      "from": {
        "name": "Ada Lovelace",
        "address": "[email protected]",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
      },
      "sent_at": "2026-10-10T09:30:00Z",
      "forwarded_at": "2026-10-10T09:30:00Z"
    },
    "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",
      "width": 1,
      "height": 1,
      "url": "https://example.com",
      "download_url": "https://example.com",
      "thumbnail_url": "https://example.com"
    },
    "analysis": {
      "status": "reading",
      "type": "payslip",
      "type_label": "Payslip"
    },
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Delete a document

DELETE/documents/{id}

Restorable for 10 seconds (POST /documents/{id}/restore), 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. Deleting an email deletes its attachments too (each with its deletion record), and restoring it brings them back; an attachment can also be deleted on its own.

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/{id}/restore

Undo deleting a document.

Within 10 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 10 seconds have passed, or for an unknown id, it's a 404. An email comes back with the attachments deleted with it; an attachment whose email is deleted can't come back alone (404).

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

Path parameters

NameDescription
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": {
    "received_via_api": {
      "at": "2026-10-10T09:30:00Z",
      "source": "Zapier"
    },
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "kind": "pdf",
    "name": "Boiler warranty",
    "dated_on": "2026-10-10",
    "dated_on_set": true,
    "folder_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "email_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "email": {
      "subject": "string",
      "from": {
        "name": "Ada Lovelace",
        "address": "[email protected]",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
      },
      "to": [
        {
          "name": "Ada Lovelace",
          "address": "[email protected]",
          "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
        }
      ],
      "cc": [
        {
          "name": "Ada Lovelace",
          "address": "[email protected]",
          "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
        }
      ],
      "sent_at": "2026-10-10T09:30:00Z",
      "forwarded": true,
      "forward_note": "string",
      "forwarded_at": "2026-10-10T09:30:00Z",
      "has_html": true,
      "remote_images": true,
      "body_url": "https://example.com",
      "attachments": [
        {
          "filename": "string",
          "content_type": "application/pdf",
          "byte_size": 1,
          "status": "saved",
          "document_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
        }
      ]
    },
    "from_email": {
      "subject": "string",
      "from": {
        "name": "Ada Lovelace",
        "address": "[email protected]",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
      },
      "sent_at": "2026-10-10T09:30:00Z",
      "forwarded_at": "2026-10-10T09:30:00Z"
    },
    "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",
      "width": 1,
      "height": 1,
      "url": "https://example.com",
      "download_url": "https://example.com",
      "thumbnail_url": "https://example.com"
    },
    "analysis": {
      "status": "reading",
      "type": "payslip",
      "type_label": "Payslip"
    },
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Download a document's file

GET/documents/{id}/file

The PDF or image itself (an email's .eml, message/rfc822, always as a download named after it plus .eml).

The file, with its Content-Type (file.content_type), 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 its extension (.pdf, .png, .jpg, .webp). 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
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 file. Errors: 400 401 404 416 429

"@file.pdf"

An email's body, as a page

GET/documents/{id}/email_body

An email document's body as a self-contained HTML page, for a sandboxed <iframe>: its HTML cleaned (no scripts, forms, frames or event handlers; links open in a new window), or its plain text. Served with a Content-Security-Policy that runs nothing and loads nothing from elsewhere: remote images load only with images=true. Not an email (or deleted): 404.

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

Path parameters

NameDescription
idULIDA record's ID.

Query parameters

NameDescription
imagesboolean · optionaltrue: let remote images load.

Response

200 The page. Errors: 400 401 404 429

"string"

Get a document's thumbnail

GET/documents/{id}/thumbnail

A PDF's first page as a JPEG, or an image fitted within 1024 × 768 as a WebP.

A PDF's first page is 480 pixels wide (at most 1440 tall) on white. Served with its Content-Type, 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
idULIDA record's ID.

Response

200 The JPEG or WebP. Errors: 401 404 429

"@file.pdf"

Get a document's text

GET/documents/{id}/text

The text Nifty read from the document, as search and summaries use it: a PDF's own text, an image's read by OCR (or, with the Image processing AI task, by its model, with OCR's text beside it), an email's. Empty, with read_by null, until it's read; empty when it has none. 404 for a deleted document.

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

Path parameters

NameDescription
idULIDA record's ID.

Response

200 The text. Errors: 401 404 429

{
  "data": {
    "text": "string",
    "truncated": true,
    "read_by": "pdf",
    "ocr_text": "string"
  }
}

Get a document's analysis

GET/documents/{id}/analysis

Note:

This endpoint is in beta, so it can change in any release (Versions).

The PDF's own information, its text's size and the Document summaries AI task's result: a summary, the document's type, and the fields extracted for it. 404 for a deleted document.

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

Path parameters

NameDescription
idULIDA record's ID.

Response

200 The analysis. Errors: 401 404 429

{
  "data": {
    "status": "reading",
    "error": "The provider didn't answer within 5 minutes.",
    "type": "string",
    "type_label": "string",
    "summary": "string",
    "facts": [
      {
        "key": "net_pay",
        "label": "Net pay",
        "kind": "text",
        "unit": "kWh",
        "value": null,
        "columns": [
          {
            "key": "string",
            "label": "string",
            "kind": "text",
            "unit": "string"
          }
        ]
      }
    ],
    "fields": [
      {
        "key": "net_pay",
        "label": "Net pay",
        "kind": "text",
        "unit": "kWh",
        "value": null,
        "columns": [
          {
            "key": "string",
            "label": "string",
            "kind": "text",
            "unit": "string"
          }
        ]
      }
    ],
    "model": "string",
    "analysed_at": "2026-10-10T09:30:00Z",
    "pages_analysed": 1,
    "image": {
      "status": "pending",
      "description": "A screenshot of a product registration page for a washing machine.",
      "model": "string",
      "error": "string"
    },
    "pdf": {
      "title": "string",
      "author": "string",
      "subject": "string",
      "keywords": "string",
      "creator": "string",
      "producer": "string",
      "created_at": "2026-10-10T09:30:00Z",
      "modified_at": "2026-10-10T09:30:00Z",
      "page_count": 1
    },
    "text": {
      "characters": 1,
      "truncated": true
    }
  }
}

Analyze a document again

POST/documents/{id}/analyze

Note:

This endpoint is in beta, so it can change in any release (Versions).

Queues the Document summaries AI task for the document, replacing its analysis once done (the earlier one stays meanwhile), and returns it as pending. 422 (details.base) when the task is off or has no model, or the document has no text to read. 404 for a deleted document. Limited to 20 a minute per IP address.

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

Path parameters

NameDescription
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

202 Queued. Errors: 401 403 404 409 422 429

{
  "data": {
    "status": "reading",
    "error": "The provider didn't answer within 5 minutes.",
    "type": "string",
    "type_label": "string",
    "summary": "string",
    "facts": [
      {
        "key": "net_pay",
        "label": "Net pay",
        "kind": "text",
        "unit": "kWh",
        "value": null,
        "columns": [
          {
            "key": "string",
            "label": "string",
            "kind": "text",
            "unit": "string"
          }
        ]
      }
    ],
    "fields": [
      {
        "key": "net_pay",
        "label": "Net pay",
        "kind": "text",
        "unit": "kWh",
        "value": null,
        "columns": [
          {
            "key": "string",
            "label": "string",
            "kind": "text",
            "unit": "string"
          }
        ]
      }
    ],
    "model": "string",
    "analysed_at": "2026-10-10T09:30:00Z",
    "pages_analysed": 1,
    "image": {
      "status": "pending",
      "description": "A screenshot of a product registration page for a washing machine.",
      "model": "string",
      "error": "string"
    },
    "pdf": {
      "title": "string",
      "author": "string",
      "subject": "string",
      "keywords": "string",
      "creator": "string",
      "producer": "string",
      "created_at": "2026-10-10T09:30:00Z",
      "modified_at": "2026-10-10T09:30:00Z",
      "page_count": 1
    },
    "text": {
      "characters": 1,
      "truncated": true
    }
  }
}

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. Linking a person accepts the document's pending person suggestion for them, if any.

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.2Contact support