Skip to content
Niftyhelp 2026.10.2 Support

Projects

Projects, their timeline and their deadlines.

On this page

List projects

GET/projects

Projects, with their members.

Filters combine (AND). Filters are for browsing; sync unfiltered.

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

Query parameters

NameDescription
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.
statusstring · optionalComma-separated statuses, e.g. active,on_hold. Anything else is 400.
person_idULID · optionalOnly projects this person is a member of. An unknown or deleted person is an empty page.
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": "Kitchen renovation",
      "status": "planned",
      "status_changed_at": "2026-10-10T09:30:00Z",
      "start_date": "2026-10-10",
      "deadline": "2026-10-10",
      "external_reference": "JIRA-123",
      "external_url": "https://example.com",
      "description_html": "<p>New units and worktops.</p>",
      "description_text": "New units and worktops.",
      "cover": {
        "kind": "color",
        "color": "dusk",
        "photo": {
          "url": "https://example.com",
          "preview_url": "https://example.com",
          "thumb_url": "https://example.com",
          "width": 1,
          "height": 1,
          "credit": {
            "name": "string",
            "profile_url": "https://example.com",
            "photo_url": "https://example.com"
          }
        },
        "position": 0
      },
      "members": [
        {
          "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
          "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
          "role": "string"
        }
      ],
      "pinned": true,
      "pinned_at": "2026-10-10T09:30:00Z",
      "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 project

POST/projects

Needs a name. A new project is active unless status says otherwise. Add members with POST /project_members.

curl -X POST "$NIFTY_URL/api/v1/projects" \
  -H "Authorization: Bearer $NIFTY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"project":{"name":"Kitchen renovation","status":"active","start_date":"2026-09-01","deadline":"2026-12-18"}}'

Headers

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

Request body

NameDescription
projectProjectWrite
project.namestring · optionalSpaces are squished. A name another project has, ignoring case, is a 422.
project.statusProjectStatus · optionalplanned, active and on_hold are open; done and cancelled are closed.
project.start_datedate or null · optionalYYYY-MM-DD. One that isn't a date is a 422.
project.deadlinedate or null · optionalYYYY-MM-DD; not before start_date (a 422).
project.external_referencestring or null · optionalSpaces are squished; blank is null.
project.external_urlstring or null · optionalhttp or https, with a host; without a scheme, https:// is added. Blank is null.
project.description_htmlstring or null · optionalHTML, sanitised as note bodies are. Attachments and images are dropped. A NUL or more than 1 MB is a 422. Null or "" clears it.
project.coverCoverWrite · optionalA cover to set. Every project wears one, so null is a 422 on cover. Kind color needs a color; kind photo keeps the photo the cover has (upload a new one with PUT /projects/{id}/cover_photo), so it's for moving position. An unknown colour, a photo when there's none, or a position out of range is a 422 on cover.<field>. A project created without one gets one of the colours its projects use least, at random. A photo a colour replaces is deleted.

Response

201 Created Errors: 400 401 403 409 415 422 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "name": "Kitchen renovation",
    "status": "planned",
    "status_changed_at": "2026-10-10T09:30:00Z",
    "start_date": "2026-10-10",
    "deadline": "2026-10-10",
    "external_reference": "JIRA-123",
    "external_url": "https://example.com",
    "description_html": "<p>New units and worktops.</p>",
    "description_text": "New units and worktops.",
    "cover": {
      "kind": "color",
      "color": "dusk",
      "photo": {
        "url": "https://example.com",
        "preview_url": "https://example.com",
        "thumb_url": "https://example.com",
        "width": 1,
        "height": 1,
        "credit": {
          "name": "string",
          "profile_url": "https://example.com",
          "photo_url": "https://example.com"
        }
      },
      "position": 0
    },
    "members": [
      {
        "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "role": "string"
      }
    ],
    "pinned": true,
    "pinned_at": "2026-10-10T09:30:00Z",
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Set a project's cover photo

PUT/projects/{id}/cover_photo

The body is the image itself, up to 20 MB, with a Content-Length (without one it's 411 length_required). The type is read from the bytes; the Content-Type must still be one of the image types below (anything else is 415). It becomes the project's cover, framed in the middle, replacing any cover; a photo it replaces is deleted. Choose a colour instead through the project's cover.

curl -X PUT "$NIFTY_URL/api/v1/projects/01j9zq3k8m5x2v7c4n6b0t1r9e/cover_photo" \
  -H "Authorization: Bearer $NIFTY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '"@file.pdf"'

Path parameters

NameDescription
idULIDA record's ID.

Response

200 OK Errors: 401 403 404 411 415 422 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "name": "Kitchen renovation",
    "status": "planned",
    "status_changed_at": "2026-10-10T09:30:00Z",
    "start_date": "2026-10-10",
    "deadline": "2026-10-10",
    "external_reference": "JIRA-123",
    "external_url": "https://example.com",
    "description_html": "<p>New units and worktops.</p>",
    "description_text": "New units and worktops.",
    "cover": {
      "kind": "color",
      "color": "dusk",
      "photo": {
        "url": "https://example.com",
        "preview_url": "https://example.com",
        "thumb_url": "https://example.com",
        "width": 1,
        "height": 1,
        "credit": {
          "name": "string",
          "profile_url": "https://example.com",
          "photo_url": "https://example.com"
        }
      },
      "position": 0
    },
    "members": [
      {
        "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "role": "string"
      }
    ],
    "pinned": true,
    "pinned_at": "2026-10-10T09:30:00Z",
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Set a project's cover to an Unsplash photo

PUT/projects/{id}/cover_photo/unsplash

A Nifty Plus feature. Downloads the Unsplash photo (GET /unsplash/photos's id), telling Unsplash it was used, and makes it the project's cover as PUT /projects/{id}/cover_photo does, with its credit. A photo Unsplash no longer has is a 422 on photo_id.

curl -X PUT "$NIFTY_URL/api/v1/projects/01j9zq3k8m5x2v7c4n6b0t1r9e/cover_photo/unsplash" \
  -H "Authorization: Bearer $NIFTY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"photo_id":"Dwu85P9SOIk"}'

Path parameters

NameDescription
idULIDA record's ID.

Request body

NameDescription
photo_idstring

Response

200 OK Errors: 400 401 403 404 422 429 502

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "name": "Kitchen renovation",
    "status": "planned",
    "status_changed_at": "2026-10-10T09:30:00Z",
    "start_date": "2026-10-10",
    "deadline": "2026-10-10",
    "external_reference": "JIRA-123",
    "external_url": "https://example.com",
    "description_html": "<p>New units and worktops.</p>",
    "description_text": "New units and worktops.",
    "cover": {
      "kind": "color",
      "color": "dusk",
      "photo": {
        "url": "https://example.com",
        "preview_url": "https://example.com",
        "thumb_url": "https://example.com",
        "width": 1,
        "height": 1,
        "credit": {
          "name": "string",
          "profile_url": "https://example.com",
          "photo_url": "https://example.com"
        }
      },
      "position": 0
    },
    "members": [
      {
        "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "role": "string"
      }
    ],
    "pinned": true,
    "pinned_at": "2026-10-10T09:30:00Z",
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Get a project

GET/projects/{id}

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

Path parameters

NameDescription
idULIDA record's ID.

Response

200 OK Errors: 401 404 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "name": "Kitchen renovation",
    "status": "planned",
    "status_changed_at": "2026-10-10T09:30:00Z",
    "start_date": "2026-10-10",
    "deadline": "2026-10-10",
    "external_reference": "JIRA-123",
    "external_url": "https://example.com",
    "description_html": "<p>New units and worktops.</p>",
    "description_text": "New units and worktops.",
    "cover": {
      "kind": "color",
      "color": "dusk",
      "photo": {
        "url": "https://example.com",
        "preview_url": "https://example.com",
        "thumb_url": "https://example.com",
        "width": 1,
        "height": 1,
        "credit": {
          "name": "string",
          "profile_url": "https://example.com",
          "photo_url": "https://example.com"
        }
      },
      "position": 0
    },
    "members": [
      {
        "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "role": "string"
      }
    ],
    "pinned": true,
    "pinned_at": "2026-10-10T09:30:00Z",
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Change a project

PATCH/projects/{id}

Only the fields sent change. Renaming it gives the notes referencing it a new updated_at (their body_text changes).

curl -X PATCH "$NIFTY_URL/api/v1/projects/01j9zq3k8m5x2v7c4n6b0t1r9e" \
  -H "Authorization: Bearer $NIFTY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"project":{"name":"Kitchen renovation","status":"active","start_date":"2026-09-01","deadline":"2026-12-18"}}'

Path parameters

NameDescription
idULIDA record's ID.

Request body

NameDescription
projectProjectWrite
project.namestring · optionalSpaces are squished. A name another project has, ignoring case, is a 422.
project.statusProjectStatus · optionalplanned, active and on_hold are open; done and cancelled are closed.
project.start_datedate or null · optionalYYYY-MM-DD. One that isn't a date is a 422.
project.deadlinedate or null · optionalYYYY-MM-DD; not before start_date (a 422).
project.external_referencestring or null · optionalSpaces are squished; blank is null.
project.external_urlstring or null · optionalhttp or https, with a host; without a scheme, https:// is added. Blank is null.
project.description_htmlstring or null · optionalHTML, sanitised as note bodies are. Attachments and images are dropped. A NUL or more than 1 MB is a 422. Null or "" clears it.
project.coverCoverWrite · optionalA cover to set. Every project wears one, so null is a 422 on cover. Kind color needs a color; kind photo keeps the photo the cover has (upload a new one with PUT /projects/{id}/cover_photo), so it's for moving position. An unknown colour, a photo when there's none, or a position out of range is a 422 on cover.<field>. A project created without one gets one of the colours its projects use least, at random. A photo a colour replaces is deleted.

Response

200 OK Errors: 400 401 403 404 415 422 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "name": "Kitchen renovation",
    "status": "planned",
    "status_changed_at": "2026-10-10T09:30:00Z",
    "start_date": "2026-10-10",
    "deadline": "2026-10-10",
    "external_reference": "JIRA-123",
    "external_url": "https://example.com",
    "description_html": "<p>New units and worktops.</p>",
    "description_text": "New units and worktops.",
    "cover": {
      "kind": "color",
      "color": "dusk",
      "photo": {
        "url": "https://example.com",
        "preview_url": "https://example.com",
        "thumb_url": "https://example.com",
        "width": 1,
        "height": 1,
        "credit": {
          "name": "string",
          "profile_url": "https://example.com",
          "photo_url": "https://example.com"
        }
      },
      "position": 0
    },
    "members": [
      {
        "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "role": "string"
      }
    ],
    "pinned": true,
    "pinned_at": "2026-10-10T09:30:00Z",
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Delete a project

DELETE/projects/{id}

Delete a project and its members.

Restorable for 10 seconds (POST /projects/{id}/restore), then permanent. The project is gone from every endpoint at once, with deletion records for the project and each member who isn't a deleted person. Notes referencing it get a new updated_at (they drop it from project_ids). Once the 10 seconds have passed, each reference becomes plain text "^Name" and those notes get a new updated_at (but not edited_at). Its name is free for a new project at once. Deleting again is a 404.

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

Path parameters

NameDescription
idULIDA record's ID.

Response

204 Deleted Errors: 401 403 404 429

Restore a project

POST/projects/{id}/restore

Undo deleting a project.

Within 10 seconds of DELETE /projects/{id}: brings the project back with its description, members and references. Its deletion records are removed, and it, its members and the notes referencing it get a new updated_at. If another project took its name meanwhile, it comes back renamed "Name (2)" (the first free number), so check name. No body. Restoring a project 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. If other projects take the name it picks twice in a row, it's a 422 on name and the project stays deleted; try again.

curl -X POST "$NIFTY_URL/api/v1/projects/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 project, as GET /projects/{id} returns it. Errors: 400 401 403 404 409 415 422 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "name": "Kitchen renovation",
    "status": "planned",
    "status_changed_at": "2026-10-10T09:30:00Z",
    "start_date": "2026-10-10",
    "deadline": "2026-10-10",
    "external_reference": "JIRA-123",
    "external_url": "https://example.com",
    "description_html": "<p>New units and worktops.</p>",
    "description_text": "New units and worktops.",
    "cover": {
      "kind": "color",
      "color": "dusk",
      "photo": {
        "url": "https://example.com",
        "preview_url": "https://example.com",
        "thumb_url": "https://example.com",
        "width": 1,
        "height": 1,
        "credit": {
          "name": "string",
          "profile_url": "https://example.com",
          "photo_url": "https://example.com"
        }
      },
      "position": 0
    },
    "members": [
      {
        "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "role": "string"
      }
    ],
    "pinned": true,
    "pinned_at": "2026-10-10T09:30:00Z",
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Pin a project

PUT/projects/{id}/pin

Sets pinned and pinned_at (the app lists pinned projects in its Projects menu), and gives the project a new updated_at. No body. Pinning a pinned project changes nothing and returns it. A deleted or unknown project is a 404.

curl -X PUT "$NIFTY_URL/api/v1/projects/01j9zq3k8m5x2v7c4n6b0t1r9e/pin" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Path parameters

NameDescription
idULIDA record's ID.

Response

200 Pinned. The project, as GET /projects/{id} returns it. Errors: 401 403 404 415 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "name": "Kitchen renovation",
    "status": "planned",
    "status_changed_at": "2026-10-10T09:30:00Z",
    "start_date": "2026-10-10",
    "deadline": "2026-10-10",
    "external_reference": "JIRA-123",
    "external_url": "https://example.com",
    "description_html": "<p>New units and worktops.</p>",
    "description_text": "New units and worktops.",
    "cover": {
      "kind": "color",
      "color": "dusk",
      "photo": {
        "url": "https://example.com",
        "preview_url": "https://example.com",
        "thumb_url": "https://example.com",
        "width": 1,
        "height": 1,
        "credit": {
          "name": "string",
          "profile_url": "https://example.com",
          "photo_url": "https://example.com"
        }
      },
      "position": 0
    },
    "members": [
      {
        "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "role": "string"
      }
    ],
    "pinned": true,
    "pinned_at": "2026-10-10T09:30:00Z",
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Unpin a project

DELETE/projects/{id}/pin

Clears pinned and pinned_at, as pinning sets them. Unpinning a project that isn't pinned changes nothing and returns it. A deleted or unknown project is a 404.

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

Path parameters

NameDescription
idULIDA record's ID.

Response

200 Unpinned. The project, as GET /projects/{id} returns it. Errors: 401 403 404 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "name": "Kitchen renovation",
    "status": "planned",
    "status_changed_at": "2026-10-10T09:30:00Z",
    "start_date": "2026-10-10",
    "deadline": "2026-10-10",
    "external_reference": "JIRA-123",
    "external_url": "https://example.com",
    "description_html": "<p>New units and worktops.</p>",
    "description_text": "New units and worktops.",
    "cover": {
      "kind": "color",
      "color": "dusk",
      "photo": {
        "url": "https://example.com",
        "preview_url": "https://example.com",
        "thumb_url": "https://example.com",
        "width": 1,
        "height": 1,
        "credit": {
          "name": "string",
          "profile_url": "https://example.com",
          "photo_url": "https://example.com"
        }
      },
      "position": 0
    },
    "members": [
      {
        "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "role": "string"
      }
    ],
    "pinned": true,
    "pinned_at": "2026-10-10T09:30:00Z",
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

List a project's activity

GET/projects/{id}/activity

What happened on a project, newest first.

One feed of the project's notes (live ones referencing it, at their created_at), completed tasks (live ones homed in it, at completed_at; reopening one removes it), linked documents (live ones, at the link's created_at; unlinking removes it), events (/project_events; a deleted person's member events are left out until they're restored) and, last, its creation. Newest first; entries at the same time go events, tasks, documents, notes, then created. Not a record: there's nothing to sync here (sync the records themselves). An unknown or deleted project is a 404.

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

Path parameters

NameDescription
idULIDA record's ID.

Query parameters

NameDescription
cursorstring · optionalmeta.next_cursor from the previous page.
limitinteger · optionalItems per page, up to 100; a larger number is taken as 100.

Response

200 OK Errors: 400 401 404 429

{
  "data": [
    {
      "kind": "note",
      "at": "2026-10-10T09:30:00Z",
      "note": {
        "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "title": "Coffee with Maya",
        "name": "Coffee with Maya",
        "title_generated": true,
        "category_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "mood": "anxious",
        "kind": "meeting",
        "kind_source": "owner",
        "tag_ids": [
          "01j9zq3k8m5x2v7c4n6b0t1r9e"
        ],
        "person_ids": [
          "01j9zq3k8m5x2v7c4n6b0t1r9e"
        ],
        "project_ids": [
          "01j9zq3k8m5x2v7c4n6b0t1r9e"
        ],
        "excerpt": "Talked about the move.",
        "image_count": 0,
        "pinned": true,
        "pinned_at": "2026-10-10T09:30:00Z",
        "edit_version": 1,
        "edited_at": "2026-10-10T09:30:00Z",
        "created_at": "2026-10-10T09:30:00Z",
        "updated_at": "2026-10-10T09:30:00Z"
      },
      "task": {
        "planned_start_on": "2026-10-10",
        "delays": 0,
        "deadline_moves": 0,
        "delegated_at": "2026-10-10T09:30:00Z",
        "now_since": "2026-10-10T09:30:00Z",
        "received_via_api": {
          "at": "2026-10-10T09:30:00Z",
          "source": "Zapier"
        },
        "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "title": "Book the plumber",
        "status": "open",
        "start_on": "2026-10-10",
        "deadline_on": "2026-10-10",
        "expires_on": "2026-10-10",
        "completed_at": "2026-10-10T09:30:00Z",
        "archived_at": "2026-10-10T09:30:00Z",
        "task_list_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "project_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "assignee_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "position": 1,
        "description_html": "<p>Ask about the boiler too.</p>",
        "description_text": "Ask about the boiler too.",
        "has_documents": true,
        "document_ids": [
          "01j9zq3k8m5x2v7c4n6b0t1r9e"
        ],
        "note_ids": [
          "01j9zq3k8m5x2v7c4n6b0t1r9e"
        ],
        "expired": true,
        "created_at": "2026-10-10T09:30:00Z",
        "updated_at": "2026-10-10T09:30:00Z"
      },
      "task_change": {
        "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "change": "added",
        "task_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "title": "string"
      },
      "document": {
        "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"
      },
      "event": {
        "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "project_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "kind": "status",
        "from": "string",
        "to": "string",
        "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "role": "Builder",
        "created_at": "2026-10-10T09:30:00Z",
        "updated_at": "2026-10-10T09:30:00Z"
      }
    }
  ],
  "meta": {
    "next_cursor": "eyJrIjpbIjIwMjYtMTAtMDlUMDg6MzA6MDBaIl19",
    "limit": 1,
    "total_count": 1
  }
}

List project changes

GET/project_events

Changes to projects' status, deadline and members.

Recorded as a project's status or deadline changes and as members are added or removed; never written through the API, and never edited. A status or deadline change within 10 minutes of the project's last of the same kind replaces it: that event is deleted (with a deletion record) and the new one runs from its from, or neither stays if it ends where that one began. Events of live projects, without a deleted person's member events. Deleting a project records a project_event deletion record for each of its events, and deleting a person for each of their member events; undoing the delete removes them and gives the events a new updated_at. For sync; GET /projects/{id}/activity shows them with the rest.

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

Query parameters

NameDescription
project_idULID · optionalA record's ID.
orderid | created_at | 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",
      "project_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
      "kind": "status",
      "from": "string",
      "to": "string",
      "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
      "role": "Builder",
      "created_at": "2026-10-10T09:30:00Z",
      "updated_at": "2026-10-10T09:30:00Z"
    }
  ],
  "meta": {
    "next_cursor": "eyJrIjpbIjIwMjYtMTAtMDlUMDg6MzA6MDBaIl19",
    "limit": 1,
    "total_count": 1
  }
}

List upcoming deadlines

GET/upcoming_deadlines

Upcoming project deadlines.

Open projects (planned, active, on_hold) due from today to days from today, inclusive, and every overdue one, ordered by deadline, then name, then id; with limit alone, the nearest limit open deadlines with no window. Today is in the owner's time zone. Not paginated.

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

Query parameters

NameDescription
daysinteger · optionalThe window in days. Without days or limit, 30 (as /upcoming_key_dates); with limit alone, no window.
limitinteger · optionalAt most this many. With limit and no days: the nearest limit open deadlines, whatever the date, overdue first. With both, both apply.

Response

200 OK Errors: 400 401 429

{
  "data": [
    {
      "date": "2026-10-10",
      "overdue": true,
      "project": {
        "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "name": "Kitchen renovation",
        "status": "planned",
        "status_changed_at": "2026-10-10T09:30:00Z",
        "start_date": "2026-10-10",
        "deadline": "2026-10-10",
        "external_reference": "JIRA-123",
        "external_url": "https://example.com",
        "description_html": "<p>New units and worktops.</p>",
        "description_text": "New units and worktops.",
        "cover": {
          "kind": "color",
          "color": "dusk",
          "photo": {
            "url": "https://example.com",
            "preview_url": "https://example.com",
            "thumb_url": "https://example.com",
            "width": 1,
            "height": 1,
            "credit": {
              "name": "string",
              "profile_url": "https://example.com",
              "photo_url": "https://example.com"
            }
          },
          "position": 0
        },
        "members": [
          {
            "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
            "person_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
            "role": "string"
          }
        ],
        "pinned": true,
        "pinned_at": "2026-10-10T09:30:00Z",
        "created_at": "2026-10-10T09:30:00Z",
        "updated_at": "2026-10-10T09:30:00Z"
      }
    }
  ]
}

Each project's last activity

GET/project_last_activity

When each live project's newest activity entry happened (GET /projects/{id}/activity's first at; its creation when nothing has happened since), for ordering Projects by last activity. Every live project, by id. Not paginated, and not a record: ask again when projects, notes, tasks or documents change.

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

Response

200 OK Errors: 401 429

{
  "data": [
    {
      "project_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
      "last_activity_at": "2026-10-10T09:30:00Z"
    }
  ]
}

Menu

2026.10.2Contact support