Skip to content
Niftyhelp 2026.10.1-beta.1 Support

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

AI

AI providers, models and the tasks that use them.

On this page

List AI providers

GET/ai_providers

AI model providers.

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

Query parameters

NameDescription
orderid | name | 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": "Ollama",
      "kind": "openai_compatible",
      "base_url": "http://localhost:11434/v1",
      "api_key_set": true,
      "last_succeeded_at": "2026-10-10T09:30:00Z",
      "last_failed_at": "2026-10-10T09:30:00Z",
      "last_error": "string",
      "last_error_reason": "string",
      "created_at": "2026-10-10T09:30:00Z",
      "updated_at": "2026-10-10T09:30:00Z"
    }
  ],
  "meta": {
    "next_cursor": "eyJrIjpbIjIwMjYtMTAtMDlUMDg6MzA6MDBaIl19",
    "limit": 1,
    "total_count": 1
  }
}

Add an AI provider

POST/ai_providers

A name another provider has, ignoring case, is a 422. On update, moving base_url to another origin (scheme, host or port) while a key is stored needs the key sent again in the same request (422 on api_key otherwise).

curl -X POST "$NIFTY_URL/api/v1/ai_providers" \
  -H "Authorization: Bearer $NIFTY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ai_provider":{"name":"Ollama","kind":"openai_compatible","base_url":"http://localhost:11434/v1"}}'

Headers

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

Request body

NameDescription
ai_providerobject
ai_provider.namestring · optionalSpaces are squished. Unique ignoring case.
ai_provider.kindAIProviderKind · optionalopenai_compatible covers OpenAI, Ollama, LM Studio, llama.cpp and vLLM. mock needs no network and isn't available in production.
ai_provider.base_urlstring or null · optionalRequired unless mock. http(s), including the version path (e.g. http://localhost:11434/v1); no user, query or fragment. A trailing / is dropped.
ai_provider.api_keystring or null · optionalWrite-only, stored encrypted. No spaces or line breaks inside. Required for anthropic; local servers need none. Null or blank clears it. Must be sent again when base_url moves to another origin.

Response

201 Created Errors: 400 401 403 409 415 422 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "name": "Ollama",
    "kind": "openai_compatible",
    "base_url": "http://localhost:11434/v1",
    "api_key_set": true,
    "last_succeeded_at": "2026-10-10T09:30:00Z",
    "last_failed_at": "2026-10-10T09:30:00Z",
    "last_error": "string",
    "last_error_reason": "string",
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Get an AI provider

GET/ai_providers/{id}

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

Path parameters

NameDescription
idULIDA record's ID.

Response

200 OK Errors: 401 404 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "name": "Ollama",
    "kind": "openai_compatible",
    "base_url": "http://localhost:11434/v1",
    "api_key_set": true,
    "last_succeeded_at": "2026-10-10T09:30:00Z",
    "last_failed_at": "2026-10-10T09:30:00Z",
    "last_error": "string",
    "last_error_reason": "string",
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Change an AI provider

PATCH/ai_providers/{id}

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

Only the fields sent change. Omit api_key to keep it; null or "" clears it.

curl -X PATCH "$NIFTY_URL/api/v1/ai_providers/01j9zq3k8m5x2v7c4n6b0t1r9e" \
  -H "Authorization: Bearer $NIFTY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ai_provider":{"name":"Ollama","kind":"openai_compatible","base_url":"http://localhost:11434/v1"}}'

Path parameters

NameDescription
idULIDA record's ID.

Request body

NameDescription
ai_providerobject
ai_provider.namestring · optionalSpaces are squished. Unique ignoring case.
ai_provider.kindAIProviderKind · optionalopenai_compatible covers OpenAI, Ollama, LM Studio, llama.cpp and vLLM. mock needs no network and isn't available in production.
ai_provider.base_urlstring or null · optionalRequired unless mock. http(s), including the version path (e.g. http://localhost:11434/v1); no user, query or fragment. A trailing / is dropped.
ai_provider.api_keystring or null · optionalWrite-only, stored encrypted. No spaces or line breaks inside. Required for anthropic; local servers need none. Null or blank clears it. Must be sent again when base_url moves to another origin.

Response

200 OK Errors: 400 401 403 404 415 422 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "name": "Ollama",
    "kind": "openai_compatible",
    "base_url": "http://localhost:11434/v1",
    "api_key_set": true,
    "last_succeeded_at": "2026-10-10T09:30:00Z",
    "last_failed_at": "2026-10-10T09:30:00Z",
    "last_error": "string",
    "last_error_reason": "string",
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Delete an AI provider

DELETE/ai_providers/{id}

Delete a provider and its models.

Permanent. Writes an ai_provider deletion record and an ai_model one per model. Deleting the default model's provider leaves no default.

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

Path parameters

NameDescription
idULIDA record's ID.

Response

204 Deleted Errors: 401 403 404 429

Test unsaved provider settings

POST/ai_providers/test

Test a provider's typed settings.

Lists the models with unsaved settings (Test connection in a provider form) and answers with only how many there are. Saves and records nothing, and ignores Idempotency-Key. GETs <base_url>/models (Anthropic: ?limit=1000), following no redirects, reading at most 5 MB, waiting up to 90 seconds.

Editing a saved provider (ai_provider_id) with a stored key, a blank or absent api_key uses the stored key only when base_url has the stored URL's origin (scheme, host and port); otherwise it's 422 key_required and no request is made, so a stored key never goes to another server. remove_api_key: true tests with no key, and kind: mock never sends one. Limited to 20 a minute per IP address, shared with check and available models.

curl -X POST "$NIFTY_URL/api/v1/ai_providers/test" \
  -H "Authorization: Bearer $NIFTY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"kind":"openai_compatible","name":"Ollama","base_url":"http://localhost:11434/v1"}'

Request body

NameDescription
ai_provider_idULID · optionalThe saved provider being edited, for its stored key. An unknown id is a 404.
namestring · optionalOnly names the provider in a failure's message; not validated.
kindAIProviderKindopenai_compatible covers OpenAI, Ollama, LM Studio, llama.cpp and vLLM. mock needs no network and isn't available in production.
base_urlstring or null · optional
api_keystring or null · optional
remove_api_keyboolean · optionalTest without the stored key.

Response

200 Connected. Errors: 400 401 403 404 415 422 429

{
  "data": {
    "model_count": 1
  }
}

Check a provider's connection

POST/ai_providers/{ai_provider_id}/check

Lists the models the provider offers. Records the outcome on the provider (last_succeeded_at, or last_failed_at and last_error), which gives it a new updated_at. Takes no body; ignores Idempotency-Key. Waits up to 90 seconds for a reply. Limited to 20 a minute per IP address, shared with POST /ai_providers/test and GET /ai_providers/{id}/available_models.

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

Path parameters

NameDescription
ai_provider_idULIDA record's ID.

Response

200 OK Errors: 400 401 403 404 415 422 429

{
  "data": {
    "models": [
      "string"
    ]
  }
}

List a provider's models

GET/ai_providers/{ai_provider_id}/available_models

The models a provider lists.

Lists the models the provider offers, with its saved settings, as check does, but records nothing: the provider's last_* and updated_at don't change. Waits up to 90 seconds for a reply. Limited to 20 a minute per IP address, shared with check and POST /ai_providers/test.

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

Path parameters

NameDescription
ai_provider_idULIDA record's ID.

Response

200 OK Errors: 401 403 404 422 429

{
  "data": {
    "models": [
      "string"
    ]
  }
}

Add several models at once

POST/ai_providers/{ai_provider_id}/model_additions

Add several models to a provider at once.

Adds models in one transaction. identifiers are picked from the provider's list (GET /ai_providers/{id}/available_models): any already added (even by a race) are skipped, not errors. identifier is one typed by name and is validated: if it's invalid or already added, nothing is added. With default_if_none (off by default) and no default model, the first model added becomes the default. Every model added gets an updated_at; no other record changes. Honours Idempotency-Key.

curl -X POST "$NIFTY_URL/api/v1/ai_providers/01j9zq3k8m5x2v7c4n6b0t1r9e/model_additions" \
  -H "Authorization: Bearer $NIFTY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"identifiers":["llama3.2","qwen3:8b"],"default_if_none":true}'

Path parameters

NameDescription
ai_provider_idULIDA record's ID.

Request body

NameDescription
identifiersstring[] or null · optionalPicked identifiers. Not an array of strings, over 100, or any over 255 characters or with a NUL: 400.
identifierstring or null · optionalOne typed by name.
default_if_noneboolean · optional

Response

201 Added. added is empty when every identifier was already added. Errors: 400 401 403 404 409 415 422

{
  "data": {
    "added": [
      {
        "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "ai_provider_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
        "identifier": "llama3.2",
        "name": "Llama 3.2",
        "is_default": true,
        "created_at": "2026-10-10T09:30:00Z",
        "updated_at": "2026-10-10T09:30:00Z"
      }
    ],
    "default_model_id": "01j9zq3k8m5x2v7c4n6b0t1r9e"
  }
}

List AI models

GET/ai_models

The models the owner uses.

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

Query parameters

NameDescription
ai_provider_idULID · optionalOnly this provider's models.
orderid | name | 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",
      "ai_provider_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
      "identifier": "llama3.2",
      "name": "Llama 3.2",
      "is_default": true,
      "created_at": "2026-10-10T09:30:00Z",
      "updated_at": "2026-10-10T09:30:00Z"
    }
  ],
  "meta": {
    "next_cursor": "eyJrIjpbIjIwMjYtMTAtMDlUMDg6MzA6MDBaIl19",
    "limit": 1,
    "total_count": 1
  }
}

Add an AI model

POST/ai_models

Add a model to a provider.

identifier is what the provider calls the model (from its check, or typed in); unique per provider. is_default: true makes it the default, clearing the old one.

curl -X POST "$NIFTY_URL/api/v1/ai_models" \
  -H "Authorization: Bearer $NIFTY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ai_model":{"ai_provider_id":"01j9zq3a9b8c7d6e5f4g3h2j1k","identifier":"llama3.2","name":"Llama 3.2","is_default":true}}'

Headers

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

Request body

NameDescription
ai_modelobject
ai_model.ai_provider_idULID · optionalCreate only. An unknown id is a 422 on ai_provider_id.
ai_model.identifierstring · optionalWhat the provider calls the model. Unique per provider.
ai_model.namestring · optionalThe owner's label. Spaces are squished; blank is the identifier.
ai_model.is_defaultboolean · optional

Response

201 Created Errors: 400 401 403 409 415 422 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "ai_provider_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "identifier": "llama3.2",
    "name": "Llama 3.2",
    "is_default": true,
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Get an AI model

GET/ai_models/{id}

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

Path parameters

NameDescription
idULIDA record's ID.

Response

200 OK Errors: 401 404 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "ai_provider_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "identifier": "llama3.2",
    "name": "Llama 3.2",
    "is_default": true,
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Change an AI model

PATCH/ai_models/{id}

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

Rename a model or change the default.

Only the fields sent change; ai_provider_id is ignored (a model stays with its provider). is_default: true makes this the default and gives the old default a new updated_at; false on the default leaves none, which turns AI features off.

curl -X PATCH "$NIFTY_URL/api/v1/ai_models/01j9zq3k8m5x2v7c4n6b0t1r9e" \
  -H "Authorization: Bearer $NIFTY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ai_model":{"ai_provider_id":"01j9zq3a9b8c7d6e5f4g3h2j1k","identifier":"llama3.2","name":"Llama 3.2","is_default":true}}'

Path parameters

NameDescription
idULIDA record's ID.

Request body

NameDescription
ai_modelobject
ai_model.ai_provider_idULID · optionalCreate only. An unknown id is a 422 on ai_provider_id.
ai_model.identifierstring · optionalWhat the provider calls the model. Unique per provider.
ai_model.namestring · optionalThe owner's label. Spaces are squished; blank is the identifier.
ai_model.is_defaultboolean · optional

Response

200 OK Errors: 400 401 403 404 415 422 429

{
  "data": {
    "id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "ai_provider_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "identifier": "llama3.2",
    "name": "Llama 3.2",
    "is_default": true,
    "created_at": "2026-10-10T09:30:00Z",
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Remove an AI model

DELETE/ai_models/{id}

Permanent. Writes an ai_model deletion record. Removing the default leaves none, which turns AI features off.

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

Path parameters

NameDescription
idULIDA record's ID.

Response

204 Deleted Errors: 401 403 404 429

Try a model

POST/ai_models/{ai_model_id}/trial

Try a model with a test prompt.

Sends prompt to the model and returns its reply and how long it took. Nothing is stored or logged, and Idempotency-Key is ignored. Records the outcome on the provider, as a check does. Waits up to 90 seconds for a reply. Limited to 20 a minute per IP address.

curl -X POST "$NIFTY_URL/api/v1/ai_models/01j9zq3k8m5x2v7c4n6b0t1r9e/trial" \
  -H "Authorization: Bearer $NIFTY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"prompt":"Say hello in five words."}'

Path parameters

NameDescription
ai_model_idULIDA record's ID.

Request body

NameDescription
promptstring

Response

200 The reply. Errors: 400 401 403 404 415 422 429

{
  "data": {
    "reply": "string",
    "duration_ms": 1
  }
}

List AI tasks

GET/ai_tasks

The built-in AI tasks and their settings.

Every task the app has (e.g. note_titles), in a fixed order. Not paginated, and tasks are never deleted, so there's no updated_since. A task is off until turned on, and uses the default model (GET /ai_models, is_default) unless ai_model_id picks another.

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

Response

200 OK Errors: 401 429

{
  "data": [
    {
      "key": "note_titles",
      "name": "Note titles",
      "description": "Suggests a title for a note without one.",
      "enabled": true,
      "ai_model_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
      "reasoning": "none",
      "max_output_tokens": 16,
      "default_max_output_tokens": 1,
      "backfill_pending": 1,
      "updated_at": "2026-10-10T09:30:00Z"
    }
  ]
}

Get an AI task

GET/ai_tasks/{key}

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

Path parameters

NameDescription
keystringThe task's key, e.g. note_titles.

Response

200 OK Errors: 401 404 429

{
  "data": {
    "key": "note_titles",
    "name": "Note titles",
    "description": "Suggests a title for a note without one.",
    "enabled": true,
    "ai_model_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "reasoning": "none",
    "max_output_tokens": 16,
    "default_max_output_tokens": 1,
    "backfill_pending": 1,
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Change an AI task

PATCH/ai_tasks/{key}

Also PUT /ai_tasks/{key}, the same.

Turn a task on or off, choose its model, or set its request options.

Only the fields sent change: enabled, ai_model_id, reasoning or max_output_tokens alone leaves the others as they were. A non-boolean enabled, an unknown ai_model_id, an unknown reasoning, or a max_output_tokens that isn't a whole number from 16 to 32,768 is a 422.

curl -X PATCH "$NIFTY_URL/api/v1/ai_tasks/string" \
  -H "Authorization: Bearer $NIFTY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ai_task":{"enabled":true,"ai_model_id":null,"reasoning":"low"}}'

Path parameters

NameDescription
keystringThe task's key, e.g. note_titles.

Request body

NameDescription
ai_taskobject
ai_task.enabledboolean · optional
ai_task.ai_model_idULID or null · optionalThe model to use; null for the default. An unknown id is a 422 on ai_model_id.
ai_task.reasoningAITaskReasoning · optionalHow hard a reasoning model thinks before replying; none answers at once. Sent to OpenAI-compatible providers as reasoning_effort; null (the default) sends nothing, leaving it to the model. Anthropic and Mock models ignore it (the value is kept). Anything else is a 422.
ai_task.max_output_tokensinteger or null · optionalNull for the task's default. Not a whole number in range (including non-finite numbers) is a 422.

Response

200 OK Errors: 400 401 403 404 415 422 429

{
  "data": {
    "key": "note_titles",
    "name": "Note titles",
    "description": "Suggests a title for a note without one.",
    "enabled": true,
    "ai_model_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
    "reasoning": "none",
    "max_output_tokens": 16,
    "default_max_output_tokens": 1,
    "backfill_pending": 1,
    "updated_at": "2026-10-10T09:30:00Z"
  }
}

Run a task on existing records

POST/ai_tasks/{ai_task_key}/backfill

Queues the task for the records it would do (backfill_pending), e.g. a title for each untitled note, and returns how many. The work happens in the background, one model call at a time. Running it again is safe. 404 for a task without a backfill; 422 (details.base) when the task is off or has no model. Limited to 5 a minute per IP address.

curl -X POST "$NIFTY_URL/api/v1/ai_tasks/string/backfill" \
  -H "Authorization: Bearer $NIFTY_TOKEN"

Path parameters

NameDescription
ai_task_keystring

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: 400 401 403 404 409 415 422 429

{
  "data": {
    "queued": 0
  }
}

Menu

2026.10.1-beta.1Contact support