Skip to main content

API reference

Every endpoint of the Ultrathink REST API. Start with API: getting started for keys, limits and errors.

All paths are relative to https://us-central1-ultrathink-50f7f.cloudfunctions.net/api/v1, and every request needs Authorization: Bearer YOUR_DEVICE_KEY. Replies wrap their result in data, as described on the getting started page.

EndpointWhat it doesKey needed
GET /entriesList entries, with filters and pagingRead only
GET /entries/{id}Read one entryRead only
POST /entriesCreate an entryRead and write
PATCH /entries/{id}Change an entryRead and write
DELETE /entries/{id}Delete an entryRead and write
GET /entries/{id}/relatedAn entry’s similar and linked entriesRead only
GET /topicsYour topics, with entry countsRead only
GET /peopleThe people in your entries, with entry countsRead only
POST /searchAsk a question, get an answer and the entries it usedRead only
POST /uploadUpload a file and get its addressRead and write

The entry object

Every endpoint that returns entries returns them in this shape.

FieldTypeWhat it is
idstringThe entry’s id
titlestringThe title
typestringThe entry type (see Entry types)
notesstring or nullYour notes
contentstring or nullCaptured text, such as a page or a conversation
urlstring or nullThe source address
topicsarrayEvery topic, the ones a person set first
userTopicsarrayTopics you or another person set. The AI never changes these
aiTopicsarrayTopics the AI added
peoplearrayPeople the entry is about
visibilitystringprivate, or org when shared with your organisation
ownedbooleanfalse for an entry another member shared with your organisation: you can read it, but only its owner can change or delete it
entitystring or nullproject, task, knowledge or unclassified
taskStatusstring or nullA status id from the owner’s status workflows
statusDoneboolean or nullWhether that status counts as done. null with no status
dueDatestring or nullYYYY-MM-DD
dueTimestring or nullHH:MM, 24 hours, in your own local time
waitingOnstring or null"me", or one person’s name
webLinksarrayWeb pages kept on the entry, each { "label", "url" }
categorystring or nullwork or personal
summarystring or nullThe AI summary
keyPoints, actionItems, suggestedQuestionsarray or nullWhat the AI picked out
transcriptstring or nullThe transcript, for audio entries
speakersarray or nullThe transcript split by speaker, for audio entries
sourcestring or nullWhere the entry came from, for example api
externalIdstring or nullYour own id for the entry, if you gave one
createdAt, updatedAtstringISO 8601 times

GET /entries

Lists your own entries, newest first unless you sort otherwise.

ParameterDefaultWhat it does
limit20Entries per page, up to 100
cursorThe previous page’s pagination.nextCursor. Continues from there, and page is ignored
page1Page number. Can skip at most 2,000 entries; past that, use cursor
typeOne entry type
entityproject, task, knowledge or unclassified
topicsTopic names separated by commas. Matches entries with any of them, up to 30
personOne person’s exact name, as GET /people lists it. Not with topics
waitingOnme, or one person’s exact name
taskStatusA status id from your workflows (for example in-progress), or @open or @done for every status that does not, or does, count as done. An unknown status returns a 400 listing yours
donetrue or false: whether the status counts as done. Entries with no status are left out. Not with taskStatus
externalIdThe entry with this externalId, exact match
dueAfterDue on or after this date, YYYY-MM-DD
dueBeforeDue on or before this date, YYYY-MM-DD
updatedSinceChanged since this date (YYYY-MM-DD) or ISO 8601 time. Always sorted by updatedAt. At most 2,000 entries can match
sortcreatedAtcreatedAt, updatedAt or due_date
orderdescasc or desc

The reply’s data has items (entry objects) and pagination with page, limit, total, totalPages, hasMore and nextCursor (null on the last page).

Filters that work together

  • No filters, with any sort and order.
  • Any one or two filters with the default sort (createdAt, newest first).
  • topics and a due date range, with type or entity as well.
  • topics with sort=createdAt or sort=updatedAt, in either order.
  • A due date range with any sort. More than 2,000 entries due in the range returns a 400.
  • entity with sort=due_date&order=asc, for example tasks by due date.
  • taskStatus or done on their own with sort=createdAt, with sort=due_date&order=asc, or with a due date range.
  • taskStatus or done with topics or entity, using the default sort, sort=due_date&order=asc or a due date range.
  • externalId with the default sort.
  • person on its own or with entity, in either order.
  • waitingOn on its own or with entity, also with sort=due_date&order=asc.
  • updatedSince on its own, or with one of entity, type, topics or person. Not with a due date range, or a sort other than updatedAt.

Other combinations may return a 400 saying the combination is not supported. Use fewer filters, or the default sort.

curl -H "Authorization: Bearer YOUR_DEVICE_KEY" \
  "https://us-central1-ultrathink-50f7f.cloudfunctions.net/api/v1/entries?waitingOn=me&entity=task&sort=due_date&order=asc"

GET /entries/{id}

Reads one entry: one of yours, or one another member shared with your organisation (owned is then false). Every id that search returns can be read here. An entry you cannot see returns 404, the same as one that does not exist.

POST /entries

Creates an entry. Send JSON. Only title is required.

FieldWhat it is
titleRequired. Up to 500 characters
typeAn entry type. Default note
notesYour notes, up to 50,000 characters
contentCaptured text, up to 50,000 characters
urlThe source address, up to 2,000 characters
entityproject, task, knowledge or unclassified. Default knowledge. An entity you set is kept when the AI runs
topicsTopic names. Kept first and in your order; the AI adds up to three of its own after them, reusing your topics where it can
aiTopicsfalse stops the AI adding topics to this entry
peoplePeople’s names
categorywork or personal
visibilityprivate (default) or org to share with your organisation
dueDateA real date, YYYY-MM-DD. Kept when the AI runs
dueTimeHH:MM, 24 hours, your local time. Needs dueDate
waitingOn"me", or one person’s name (up to 200 characters). A new name joins your people list
webLinksUp to 20 links, each { "label": "Live post", "url": "https://..." }. The label is optional, up to 100 characters; the address must be http or https
taskStatusA status by id (in-progress) or name (In progress, any case), or @done or @open for the first status of the entry’s workflow that does, or does not, count as done. Optional
externalIdYour own id, up to 500 characters, so a later sync can find the entry
triggerAIfalse skips the AI pass. Default true

Rules:

  • Only work entries can be shared. "visibility": "org" with "category": "personal" returns a 400. An entry with no category yet can be shared.
  • Sharing needs you to be a member of an organisation now, otherwise 400.
  • A status your workflows do not have returns a 400 listing the ones you can use.

The reply is 201 with the new entry. The AI then writes its title, summary, topics and people, as for anything you save in the app.

curl -X POST \
  -H "Authorization: Bearer YOUR_DEVICE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title": "Send Ana the report", "entity": "task", "dueDate": "2026-10-20", "dueTime": "14:30", "waitingOn": "me"}' \
  "https://us-central1-ultrathink-50f7f.cloudfunctions.net/api/v1/entries"

PATCH /entries/{id}

Changes one of your own entries. Send only the fields you want to change; they are the same as for POST /entries.

  • taskStatus: null clears the status. Changing the status never moves the entry to another kanban column: columns are independent of status.
  • topics replaces the whole list. AI topics you keep stay AI topics, and the AI does not add back the ones you leave out.
  • aiTopics: false takes the AI’s topics off and stops it adding more. true lets it add them again.
  • dueTime, waitingOn and webLinks are cleared with null. Clearing dueDate clears its time too. A dueTime with no due date returns a 400.
  • webLinks replaces the whole list.
  • Moving a shared entry out of work (for example "category": "personal") also makes it private.
  • triggerAI: true runs the AI pass again. Without it, the AI does not run.

Entries shared with you by someone else cannot be changed, and return 404.

DELETE /entries/{id}

Deletes one of your own entries. It cannot be undone.

curl -X DELETE \
  -H "Authorization: Bearer YOUR_DEVICE_KEY" \
  "https://us-central1-ultrathink-50f7f.cloudfunctions.net/api/v1/entries/ENTRY_ID"

The reply’s data is { "id": "ENTRY_ID", "deleted": true }. An entry that is not yours returns 404.

GET /entries/{id}/related

An entry’s similar entries, as the AI picked them when the entry was saved, and the entries you linked to it. Only entries you can read are listed.

{
  "entryId": "abc123",
  "mode": "classic",
  "similar": [
    {
      "id": "def456",
      "title": "Partner deals tracker",
      "type": "note",
      "entity": "task",
      "timestamp": "2026-08-01T10:00:00.000Z",
      "excerpt": "Deals by partner, weekly.",
      "score": null,
      "reason": "Both track partner deals",
      "duplicate": false
    }
  ],
  "links": [
    {
      "id": "ghi789",
      "title": "Call with Ana",
      "type": "note",
      "entity": "knowledge",
      "timestamp": "2026-07-01T10:00:00.000Z",
      "excerpt": "Ana wants the report in Teams.",
      "linkType": "related"
    }
  ]
}

reason says why the AI thought the entries are alike. excerpt is the first words of the entry as plain text.

GET /topics and GET /people

Your topics, or the people in your entries, each with how many entries have it, most used first.

ParameterDefaultWhat it does
qOnly names containing this text, any case (up to 200 characters)
limit100How many names to return, up to 500
{
  "items": [
    { "name": "planning", "count": 12 },
    { "name": "meetings", "count": 9 }
  ],
  "total": 48,
  "entriesCounted": 1311,
  "partial": false
}

total is how many names match before limit. partial is true only when you have more than 10,000 entries, and then the newest 10,000 are counted. Counts can lag a few minutes behind changes made in the app.

POST /search

Asks a question in plain words. The AI reads your most recent entries, up to the Entry limit in Settings, then AI search (500 unless you change it), and answers with the entries it used. Entries shared with your organisation are read too.

FieldWhat it is
queryRequired. The question, up to 1,000 characters
entityFilterOnly search project, task or knowledge entries
limitHow many recent entries to read. It can only lower your entry limit, never raise it
{
  "answer": "You agreed to move the pricing page launch to November.",
  "mode": "classic",
  "matches": [
    {
      "id": "abc123",
      "title": "Pricing page review",
      "type": "note",
      "entity": "project",
      "timestamp": "2026-10-02T09:15:00.000Z",
      "source": "user",
      "score": null,
      "cited": true,
      "excerpt": "Launch moves to November so the copy can be checked..."
    }
  ],
  "entryCount": 500,
  "durationMs": 2100
}
FieldWhat it is
answerA short answer from your entries
matchesThe entries the answer used. Each id can be read with GET /entries/{id}
sourceuser for your own entries, org for ones shared by your organisation
entryCountHow many entries were read
durationMsHow long the answer took

Search only sees the entries inside your entry limit. To find something older, filter with GET /entries instead.

POST /upload

Uploads a file and returns its address. It does not create an entry by itself.

Send it as multipart/form-data with the file in a field called file, and give the file’s type, as the API checks it:

curl -X POST \
  -H "Authorization: Bearer YOUR_DEVICE_KEY" \
  -F "file=@meeting.m4a;type=audio/mp4" \
  "https://us-central1-ultrathink-50f7f.cloudfunctions.net/api/v1/upload"

Or as JSON, with the file as a data URL:

{ "file": "data:application/pdf;base64,JVBERi0...", "filename": "report.pdf" }
FileTypesLargest size
DocumentsPDF, Word (doc, docx), Excel (xls, xlsx), PowerPoint (ppt, pptx), plain text, Markdown50 MB
ImagesPNG, JPEG, WebP, GIF, HEIC50 MB
AudioMP3, WAV, M4A, OGG, WebM200 MB
VideoMP4, MOV, WebM200 MB

A request over about 32 MB fails before it reaches Ultrathink, whatever the file. Upload large recordings and videos from the web app or the desktop app instead.

The reply is 201:

{
  "url": "https://firebasestorage.googleapis.com/v0/b/...",
  "path": "user-uploads/USER_ID/audio/1760440000000-ab12cd34.m4a",
  "mimeType": "audio/mp4",
  "size": 4823011,
  "category": "audio"
}

Entry types

These are the values type takes.

TypeWhat it is
linkA web page
note, snippet, paraShort text
idea, long-noteLonger notes and research
pdf, markdownDocuments
ms-word, ms-excel, ms-powerpoint, ms-onenoteMicrosoft Office files and pages
screenshot, imagePictures
audio, videoRecordings and video links
chatgpt, claude, perplexityAI conversations
outlookAn Outlook email
notionA Notion page
profileA LinkedIn, X or GitHub profile
fileAny other file

OpenAPI file

The same API is described in an OpenAPI file, which you can import into Postman, Insomnia or a ChatGPT custom GPT:

https://tryultrathink.com/docs/api/openapi.yaml