Skip to content
Niftyhelp 2026.10.2 Support

System

The server's version, updates, licence and the words for values (vocabulary).

On this page

Get the version and update status

GET/system

This server's version, update channel and update status.

One resource; there's no collection, updated_since or deletion record. update.status is worked out on each read against the running version: off when this build can't check (update.can_check false: managed, no release signing key, or a development build), or when daily checks are off (update_checks false) and there's no result; else available while the last result is newer than version, up_to_date when it isn't, unknown before any result. So with daily checks off, a Check now's result still shows. A failed check keeps the last result and sets update.error. A newer release the licence doesn't cover (it came out after the licence's updates_until, or refuses its key) is not_covered instead of available, worked out on each read, so adding a licence that covers it makes it available without a new check. Without a licence (the trial, or read-only) every release is offered.

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

Response

200 OK Errors: 401 429

{
  "data": {
    "version": "1.2.0",
    "channel": "stable",
    "update_checks": true,
    "update_checks_managed": true,
    "update_checks_managed_by": "desktop",
    "error_reports": true,
    "error_reports_forced": true,
    "plain_http": true,
    "update": {
      "status": "up_to_date",
      "not_covered_reason": "updates_ended",
      "latest_released_on": "2027-12-03",
      "can_check": true,
      "latest_version": "1.3.0",
      "notes_url": "https://example.com",
      "checked_at": "2026-10-10T09:30:00Z",
      "dismissed": true,
      "error": "Nifty couldn’t reach download.thenifty.app."
    }
  }
}

Change update and error report settings

PATCH/system

Change the update channel, turn daily checks or error reports on or off, or dismiss an update.

Only the fields sent change. A channel other than stable or beta, an update_checks that isn't a boolean (or any update_checks while it's managed), an error_reports that isn't a boolean (or any error_reports on a beta, error_reports_forced), or a dismissed_version that isn't a version like 1.2.3 or 1.2.3-beta.1 (or null) is a 422. Changing channel clears the last result (status unknown); changing it or turning checks on queues a check.

curl -X PATCH "$NIFTY_URL/api/v1/system" \
  -H "Authorization: Bearer $NIFTY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"system":{"channel":"stable","update_checks":true}}'

Request body

NameDescription
systemobject
system.channelUpdateChannel · optionalstable (the default) gets stable releases; beta gets betas too, and stable releases newer than them.
system.update_checksboolean · optionalCheck daily.
system.error_reportsboolean · optionalSend error reports. Can't change on a beta (error_reports_forced).
system.dismissed_versionstring or null · optionalThe update the owner has seen; update.dismissed while it's at least latest_version. Null clears it.

Response

200 OK Errors: 400 401 403 415 422 429

{
  "data": {
    "version": "1.2.0",
    "channel": "stable",
    "update_checks": true,
    "update_checks_managed": true,
    "update_checks_managed_by": "desktop",
    "error_reports": true,
    "error_reports_forced": true,
    "plain_http": true,
    "update": {
      "status": "up_to_date",
      "not_covered_reason": "updates_ended",
      "latest_released_on": "2027-12-03",
      "can_check": true,
      "latest_version": "1.3.0",
      "notes_url": "https://example.com",
      "checked_at": "2026-10-10T09:30:00Z",
      "dismissed": true,
      "error": "Nifty couldn’t reach download.thenifty.app."
    }
  }
}

Report an error in the web UI

POST/error_reports

The web UI's uncaught errors, sent on through this server so the error report service never sees the browser's address. A 202 whether or not it's sent: nothing is kept or sent while error_reports is off (see GET /system), nor ever from a development build. The server adds the version, platform, time and fingerprint, scrubs the message and file paths again (emails, addresses, quoted text, numbers and ids go), and sends at most one report a fingerprint an hour and 50 a day. A 422 when a field is missing or over its limit, or the body is over 16 KB. Limited to 30 a minute per IP address.

curl -X POST "$NIFTY_URL/api/v1/error_reports" \
  -H "Authorization: Bearer $NIFTY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"kind":"error","type":"TypeError","message":"Cannot read properties of undefined (reading '\''id'\'')","frames":[{"function":"NoteView","file":"/assets/notes-AB12.js","line":1,"column":4021}],"browser":"chrome"}'

Headers

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

Request body

NameDescription
kinderror | panicerror for a caught error (a route's error page), panic for an uncaught one.
typestringThe error's name.
messagestringScrubbed by the client; scrubbed again here.
framesErrorReportFrame[]The stack, innermost first, as the bundle's files (mapped to source by the error report service).
browserchrome | safari | firefox | edge | other · optional

Response

202 Accepted (sent later, or dropped while reporting is off). Errors: 400 401 403 409 415 422 429

Send a test crash report

POST/error_reports/test

A beta's Send test crash report: the server panics with a TestCrash and recovers as it does from any request's panic, so a real report (its frames included) goes through the send job to the error report service. Takes no body. A 202 whether or not it's sent (never from a development build); a 404 unless the version is a beta. Test reports skip the hourly duplicate check but count towards the 50 a day. Limited with POST /error_reports.

curl -X POST "$NIFTY_URL/api/v1/error_reports/test" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Response

202 Accepted (sent later). Errors: 401 403 404 415 429

Check for an update now

POST/system/check_for_updates

Fetches the channel's signed manifest and answers with the result (a failure is update.error, still a 200). Works while daily checks are off; a 422 (details.base) when they're managed or update.can_check is false. Takes no body; ignores Idempotency-Key. Waits up to 30 seconds. Limited to 10 a minute per IP address.

curl -X POST "$NIFTY_URL/api/v1/system/check_for_updates" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Response

200 OK Errors: 401 403 415 422 429

{
  "data": {
    "version": "1.2.0",
    "channel": "stable",
    "update_checks": true,
    "update_checks_managed": true,
    "update_checks_managed_by": "desktop",
    "error_reports": true,
    "error_reports_forced": true,
    "plain_http": true,
    "update": {
      "status": "up_to_date",
      "not_covered_reason": "updates_ended",
      "latest_released_on": "2027-12-03",
      "can_check": true,
      "latest_version": "1.3.0",
      "notes_url": "https://example.com",
      "checked_at": "2026-10-10T09:30:00Z",
      "dismissed": true,
      "error": "Nifty couldn’t reach download.thenifty.app."
    }
  }
}

Get the licence

GET/licence

The licence, or the free trial, and whether Nifty is read-only.

One resource; there's no collection, updated_since or deletion record. state is licensed when the stored key is valid, not revoked and covers this version (released on or before its updates_until); else trial for 14 days from first launch; else read_only. A stored key that doesn't apply keeps its details, with problem saying why.

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

Response

200 OK Errors: 401 429

{
  "data": {
    "state": "licensed",
    "problem": "revoked",
    "licensee": {
      "name": "Ada Lovelace",
      "email": "[email protected]"
    },
    "key_id": "3F9A-C27B-0E15",
    "order_reference": "LS-482913",
    "issued_on": "2026-10-10",
    "updates_until": "2027-10-10",
    "trial_ends_at": "2026-10-10T09:30:00Z",
    "trial_days_left": 0,
    "released_on": "2026-10-10",
    "onboarded": true,
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Get Nifty Plus

GET/plus

Whether this install is connected to Nifty Plus, the subscription's status, and whether Plus features can be used now (docs/plus.md).

While connected, a read checks the subscription again when the last check (checked_at) is more than an hour old. If the account service can't be reached, the last known status stays and error is unreachable.

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

Response

200 OK Errors: 401 429

{
  "data": {
    "state": "not_connected",
    "available": true,
    "unavailable_reason": "not_connected",
    "install_name": "Nifty on Ada's MacBook",
    "account_url": "https://account.thenifty.app/connect",
    "account": {
      "email": "[email protected]",
      "status": "active",
      "plan": "monthly",
      "period_end": "2026-10-10T09:30:00Z",
      "cancel_at_period_end": true
    },
    "error": "unreachable",
    "checked_at": "2026-10-10T09:30:00Z"
  }
}

Search Unsplash

GET/unsplash/photos

Note:

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

A Nifty Plus feature: landscape photos on Unsplash for q, 24 a page, through Nifty Plus's Unsplash service. Show urls straight from Unsplash, as its guidelines ask, with the photographer's credit. Choosing one is PUT /projects/{id}/cover_photo/unsplash. The service limits each install to 30 searches a minute and 200 an hour (429 rate_limited with Retry-After).

curl "$NIFTY_URL/api/v1/unsplash/photos?q=string" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Query parameters

NameDescription
qstringBlank or longer is 400.
pageinteger · optional

Response

200 OK Errors: 400 401 403 429 502

{
  "data": [
    {
      "id": "string",
      "alt": "string",
      "width": 1,
      "height": 1,
      "color": "string",
      "blur_hash": "string",
      "thumb_url": "https://example.com",
      "preview_url": "https://example.com",
      "credit": {
        "name": "string",
        "profile_url": "https://example.com",
        "photo_url": "https://example.com"
      }
    }
  ],
  "meta": {
    "page": 1,
    "total_pages": 1
  }
}

Get email forwarding

GET/email_forwarding

A Nifty Plus feature (docs/plus.md#forwarding-emails): the address emails can be forwarded to, who may send to it, and what collecting found. Emails forwarded there wait on Nifty Plus, sealed for this install, for up to 7 days; Nifty collects them every 2 minutes (POST /email_forwarding/check collects now), and each becomes an unsorted document as POST /email_imports makes, with forwarded_at set.

While Plus is available, a read asks the forwarding service, making the address the first time. Otherwise, or when the service can't be reached (error: unreachable), it's as last known.

Needs a token with full access (or a session): a read token gets 403 read_only_token, as the address lets anyone put documents into Nifty.

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

Response

200 OK Errors: 401 403 429

{
  "data": {
    "available": true,
    "unavailable_reason": "not_connected",
    "address": "[email protected]",
    "senders": {
      "anyone": true,
      "addresses": [
        "[email protected]"
      ]
    },
    "waiting": 0,
    "error": "unreachable",
    "checked_at": "2026-10-10T09:30:00Z",
    "collected_at": "2026-10-10T09:30:00Z",
    "lost": 0,
    "unreadable": 0
  }
}

Collect forwarded emails now

POST/email_forwarding/check

Collects the emails waiting now (and anything sent through the Send to Nifty API), rather than at the next 2-minute check. Works after the subscription ends, for emails that arrived before; not while read-only (403). 409 conflict when Plus isn't connected.

curl -X POST "$NIFTY_URL/api/v1/email_forwarding/check" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Response

200 Collected; collected_at and waiting say what's changed. Errors: 401 403 409 429 500 502

{
  "data": {
    "available": true,
    "unavailable_reason": "not_connected",
    "address": "[email protected]",
    "senders": {
      "anyone": true,
      "addresses": [
        "[email protected]"
      ]
    },
    "waiting": 0,
    "error": "unreachable",
    "checked_at": "2026-10-10T09:30:00Z",
    "collected_at": "2026-10-10T09:30:00Z",
    "lost": 0,
    "unreadable": 0
  }
}

Dismiss forwarding notices

DELETE/email_forwarding/notices

Sets lost and unreadable back to 0, once the owner has seen them.

curl -X DELETE "$NIFTY_URL/api/v1/email_forwarding/notices" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Response

200 OK Errors: 401 403 429

{
  "data": {
    "available": true,
    "unavailable_reason": "not_connected",
    "address": "[email protected]",
    "senders": {
      "anyone": true,
      "addresses": [
        "[email protected]"
      ]
    },
    "waiting": 0,
    "error": "unreachable",
    "checked_at": "2026-10-10T09:30:00Z",
    "collected_at": "2026-10-10T09:30:00Z",
    "lost": 0,
    "unreadable": 0
  }
}

Get the Send to Nifty API

GET/send_api

A Nifty Plus feature (docs/plus.md#send-to-nifty-api): with an API token, other apps and services can send tasks, notes, events, documents and emails to url. They wait on Nifty Plus, sealed for this install, for up to 7 days; Nifty collects them every 2 minutes (POST /send_api/check collects now) and adds each under the ID the sender was given, with received_via_api set.

While Plus is available, a read asks the service. Otherwise, or when it can't be reached (error: unreachable), it's as last known. Needs a token with full access (or a session): a read token gets 403 read_only_token.

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

Response

200 OK Errors: 401 403 429

{
  "data": {
    "available": true,
    "unavailable_reason": "not_connected",
    "url": "https://api.nifty.fyi",
    "tokens": [
      {
        "id": "string",
        "name": "Zapier",
        "ending": "string",
        "created_at": "2026-10-10T09:30:00Z",
        "last_used_at": "2026-10-10T09:30:00Z"
      }
    ],
    "waiting": 0,
    "error": "unreachable",
    "checked_at": "2026-10-10T09:30:00Z",
    "collected_at": "2026-10-10T09:30:00Z",
    "skipped": 0,
    "lost": 0,
    "expired": 0
  }
}

Collect what's been sent now

POST/send_api/check

Collects what's waiting now (forwarded emails too), up to 50 items, rather than at the next 2-minute check. Works after the subscription ends, for what arrived before; not while read-only (403). 409 conflict when Plus isn't connected.

curl -X POST "$NIFTY_URL/api/v1/send_api/check" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Response

200 Collected; collected_at and waiting say what's changed. Errors: 401 403 409 429 500 502

{
  "data": {
    "available": true,
    "unavailable_reason": "not_connected",
    "url": "https://api.nifty.fyi",
    "tokens": [
      {
        "id": "string",
        "name": "Zapier",
        "ending": "string",
        "created_at": "2026-10-10T09:30:00Z",
        "last_used_at": "2026-10-10T09:30:00Z"
      }
    ],
    "waiting": 0,
    "error": "unreachable",
    "checked_at": "2026-10-10T09:30:00Z",
    "collected_at": "2026-10-10T09:30:00Z",
    "skipped": 0,
    "lost": 0,
    "expired": 0
  }
}

Dismiss Send to Nifty API notices

DELETE/send_api/notices

Sets skipped, lost and expired back to 0, once the owner has seen them.

curl -X DELETE "$NIFTY_URL/api/v1/send_api/notices" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Response

200 OK Errors: 401 403 429

{
  "data": {
    "available": true,
    "unavailable_reason": "not_connected",
    "url": "https://api.nifty.fyi",
    "tokens": [
      {
        "id": "string",
        "name": "Zapier",
        "ending": "string",
        "created_at": "2026-10-10T09:30:00Z",
        "last_used_at": "2026-10-10T09:30:00Z"
      }
    ],
    "waiting": 0,
    "error": "unreachable",
    "checked_at": "2026-10-10T09:30:00Z",
    "collected_at": "2026-10-10T09:30:00Z",
    "skipped": 0,
    "lost": 0,
    "expired": 0
  }
}

Get the vocabulary

GET/vocabulary

Every fixed list a client shows, with labels.

Types, statuses, labels, colours, icons, appearance and sound choices, and upload limits, so a client needn't hard-code them. Every list is in display order. Not paginated; it changes only with a server release.

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

Response

200 OK Errors: 401 429

{
  "data": {
    "relationships": [
      {
        "key": "spouse",
        "label": "direct report",
        "group": "family"
      }
    ],
    "person_links": [
      {
        "key": "spouse",
        "label": "parent",
        "group": "family",
        "inverse_key": "spouse",
        "inverse_label": "child",
        "symmetric": true,
        "synonyms": [
          "mother",
          "father",
          "mum",
          "mom",
          "dad"
        ]
      }
    ],
    "key_date_kinds": [
      {
        "key": "birthday",
        "label": "Work anniversary",
        "icon": "cake"
      }
    ],
    "social_platforms": [
      {
        "key": "website",
        "label": "LinkedIn"
      }
    ],
    "relationship_groups": [
      {
        "key": "family",
        "label": "Family",
        "icon": "house"
      }
    ],
    "contact_labels": {
      "email_addresses": {
        "suggestions": [
          "Personal",
          "Work",
          "Other"
        ],
        "default": "string"
      },
      "phone_numbers": {
        "suggestions": [
          "Personal",
          "Work",
          "Other"
        ],
        "default": "string"
      },
      "social_profiles": {
        "suggestions": [
          "Personal",
          "Work",
          "Other"
        ],
        "default": "string"
      }
    },
    "project_statuses": [
      {
        "key": "planned",
        "label": "On hold",
        "icon": "circle-pause",
        "tone": "neutral",
        "open": true
      }
    ],
    "task_count_kinds": [
      {
        "key": "open",
        "label": "Won’t do",
        "icon": "circle-slash"
      }
    ],
    "colors": [
      {
        "key": "teal",
        "label": "Teal"
      }
    ],
    "picker_icons": [
      "folder"
    ],
    "accents": [
      {
        "key": "iris",
        "label": "Iris"
      }
    ],
    "cover_colors": [
      {
        "key": "dusk",
        "label": "Dusk",
        "kind": "gradient"
      }
    ],
    "themes": [
      {
        "key": "system",
        "label": "Match device"
      }
    ],
    "code_themes": [
      {
        "key": "match",
        "label": "Match theme"
      }
    ],
    "text_sizes": [
      {
        "key": "small",
        "label": "Medium"
      }
    ],
    "mood_groups": [
      {
        "key": "tense",
        "label": "Tense"
      }
    ],
    "moods": [
      {
        "key": "anxious",
        "label": "Anxious",
        "emoji": "😟",
        "group": "tense"
      }
    ],
    "event_types": [
      {
        "key": "string",
        "label": "string",
        "icon": "string"
      }
    ],
    "note_kinds": [
      {
        "key": "meeting",
        "label": "Meeting",
        "icon": "users"
      }
    ],
    "insight_kinds": [
      {
        "key": "you_owe",
        "label": "You owe",
        "icon": "send"
      }
    ],
    "sounds": {
      "task_completed": [
        {
          "key": "pop_in",
          "label": "Tap trio"
        }
      ],
      "created": [
        {
          "key": "blip_blip",
          "label": "Blip blip"
        }
      ],
      "deleted": [
        {
          "key": "knock_away",
          "label": "Paper away"
        }
      ],
      "calendar_alert": [
        {
          "key": "arpeggio",
          "label": "Music box"
        }
      ]
    },
    "ai_provider_kinds": [
      {
        "key": "openai_compatible",
        "label": "OpenAI-compatible"
      }
    ],
    "uploads": {
      "avatar": {
        "content_types": [
          "image/jpeg",
          "image/png"
        ],
        "max_bytes": 10485760
      },
      "note_image": {
        "content_types": [
          "image/jpeg",
          "image/png"
        ],
        "max_bytes": 10485760
      },
      "document": {
        "content_types": [
          "image/jpeg",
          "image/png"
        ],
        "max_bytes": 10485760
      },
      "email": {
        "content_types": [
          "image/jpeg",
          "image/png"
        ],
        "max_bytes": 10485760
      }
    },
    "calendar_sync_frequencies": [
      {
        "key": "30_minutes",
        "label": "30 minutes",
        "minutes": 30
      }
    ],
    "meeting_providers": [
      {
        "key": "google_meet",
        "label": "Google Meet"
      }
    ],
    "calendar_sync_errors": [
      {
        "key": "not_found",
        "label": "the server refused the address"
      }
    ],
    "document_link_kinds": [
      {
        "key": "person",
        "label": "Person"
      }
    ],
    "calendar_sources": [
      {
        "key": "google",
        "label": "Google Calendar",
        "url": "https://calendar.google.com/"
      }
    ],
    "api_token_scopes": [
      {
        "key": "full",
        "label": "Read only"
      }
    ],
    "backup_reasons": [
      {
        "key": "before_update",
        "label": "Before update"
      }
    ],
    "webhook_events": [
      {
        "key": "task.created",
        "label": "Task completed"
      }
    ],
    "webhook_outcomes": [
      {
        "key": "pending",
        "label": "Succeeded"
      }
    ],
    "limits": {
      "tag_name": 50,
      "folder_name": 100,
      "document_name": 200,
      "task_title": 200,
      "task_list_name": 100,
      "project_name": 100,
      "project_text": 100,
      "project_url": 2048,
      "project_member_role": 100,
      "position_title": 100,
      "company_name": 100,
      "calendar_feed_name": 100,
      "calendar_address": 2048,
      "webhook_name": 100,
      "webhook_url": 2048
    }
  }
}

Get open source licences

GET/licences

The open source software in this build, with its licences.

The notices distributed with Nifty, grouped by where the software is used. Each package names its licence text by licence_id in licences, so a shared text appears once. Not paginated; it changes only with a server release.

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

Response

200 OK Errors: 401 429

{
  "data": {
    "groups": [
      {
        "key": "web",
        "label": "string",
        "packages": [
          {
            "name": "string",
            "version": "string",
            "licence": "string",
            "url": "string",
            "licence_id": "string"
          }
        ]
      }
    ],
    "licences": [
      {
        "id": "string",
        "text": "string"
      }
    ]
  }
}

Identify a Nifty install

GET/.well-known/nifty

Unauthenticated. Reveals nothing about the owner.

curl "$NIFTY_URL/api/v1/.well-known/nifty" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Response

200 OK

{
  "app": "nifty",
  "version": "1.2.3",
  "min_shell_version": "0.0.0",
  "api_versions": [
    {
      "version": "v1",
      "base_url": "https://example.com"
    }
  ]
}

Menu

2026.10.2Contact support