API Reference
Complete reference for the Notion ORM API. The sections follow a learning progression: start with runtime access, then databases, then agents, then advanced usage.
Runtime access
import NotionORM from "@haustle/notion-orm";
const notion = new NotionORM({
auth: process.env.NOTION_KEY!,
});
// DatabaseClient
const db = notion.databases.yourDatabaseName;
// AgentClient
const agent = notion.agents.yourAgentName;
notion.databasesRecord<string, DatabaseClient>notion.agentsRecord<string, AgentClient>Database client
Properties
idnameMethods
findUnique({ where: { id } })findFirst(args?)nullfindMany(args?)Partial<Schema>[] by default, PaginateResult with after, or AsyncIterable with streamcount(args?)create({ properties, icon?, cover?, markdown? })createMany({ properties })update({ where: { id }, properties })updateMany({ where, properties })upsert({ where, create, update })delete({ where: { id } })deleteMany({ where })findUnique
Retrieves a single page by its Notion page ID. Returns null for missing or partial pages. Supports select / omit projection with the same semantics as findMany.
const page = await db.findUnique({
where: { id: "page-id-here" },
select: ["bookName", "status"],
});
findFirst
Returns the first row matching the filter, or null if none match.
const book = await db.findFirst({
where: { bookName: { equals: "Creativity, Inc." } },
});
Args: Same as findMany but without stream, after, or size.
findMany
Queries the database with optional filtering, sorting, projection, pagination, and streaming.
Overloads:
stream or afterPromise<Partial<Schema>[]>after: string | nullPromise<PaginateResult<Schema>>stream: numberAsyncIterable<Partial<Schema>>Args:
whereQueryFiltersortByQuerySort<ColumnTypes>sizenumberselectreadonly (keyof Schema & string)[]omitreadonly (keyof Schema & string)[]streamnumberafterstring | nullselect and omit are mutually exclusive — providing both throws at runtime.
// Simple query — returns all rows
const allBooks = await db.findMany();
// Filtered + sorted + projected
const recent = await db.findMany({
where: { publishDate: { on_or_after: "2025-01-01" } },
sortBy: [{ property: "publishDate", direction: "descending" }],
select: ["bookName", "publishDate"],
size: 20,
});
// Cursor pagination
const firstPage = await db.findMany({ after: null, size: 10 });
const secondPage = await db.findMany({ after: firstPage.nextCursor, size: 10 });
// firstPage.data, firstPage.hasMore, firstPage.nextCursor
// Streaming large datasets
for await (const row of db.findMany({ stream: 100 })) {
console.log(row.bookName);
}
PaginateResult
Returned by findMany when after is provided.
dataPartial<Schema>[]nextCursorstring | nullafter for the next page, null when donehasMorebooleanFiltering
Filters are typed by your generated schema. Single-property filters and compound and/or are supported.
Single filter:
await db.findMany({
where: { genre: { contains: "Sci-Fi" } },
});
Compound filters:
await db.findMany({
where: {
and: [
{ genre: { contains: "Sci-Fi" } },
{ numberOfPages: { greater_than: 200 } },
{
or: [
{ status: { equals: "In progress" } },
{ status: { equals: "Not started" } },
],
},
],
},
});
Filter operators vary by property type — see the Notion filter reference for the full list.
count
Returns the total number of rows matching an optional filter. Paginates internally to count all results.
const total = await db.count();
const filtered = await db.count({
where: { status: { equals: "Done" } },
});
create
Creates a single page. Properties are typed to your generated schema.
const result = await db.create({
properties: {
bookName: "The Dream Machine",
genre: ["Non-fiction"],
numberOfPages: 460,
},
icon: { type: "emoji", emoji: "📗" },
});
// result.id — the new page ID
Pass markdown to add body content to the page in a single call. This uses Notion's enhanced markdown format — headings, lists, code blocks, quotes, checklists, and more are all supported.
await db.create({
properties: {
bookName: "Reading Notes",
},
markdown: "# Key Takeaways\n\n- **Creativity requires candor** — honest feedback loops matter\n- Protect the new — early ideas are fragile\n\n> \"Quality is the best business plan.\"",
});
markdown is mutually exclusive with children / content.
createMany
Creates multiple pages sequentially and returns all responses.
const results = await db.createMany({
properties: [
{ bookName: "Book A", genre: ["Sci-Fi"], numberOfPages: 300 },
{ bookName: "Book B", genre: ["Biography"], numberOfPages: 250 },
],
});
update
Updates a single page by ID. Requires at least one property.
await db.update({
where: { id: "page-id" },
properties: { status: "Done", numberOfPages: 512 },
});
updateMany
Finds all pages matching a filter and applies the same property updates to each.
await db.updateMany({
where: { status: { equals: "Draft" } },
properties: { status: "In progress" },
});
upsert
Finds the first row matching where. If found, applies update; otherwise runs create.
await db.upsert({
where: { bookName: { equals: "The Dream Machine" } },
create: {
bookName: "The Dream Machine",
genre: ["Non-fiction"],
numberOfPages: 460,
},
update: { numberOfPages: 500 },
});
delete
Archives a single page by ID (Notion does not hard-delete pages).
await db.delete({ where: { id: "page-id" } });
deleteMany
Archives all pages matching a filter.
await db.deleteMany({
where: { status: { equals: "Archived" } },
});
Supported database properties
titlestring"The Dream Machine"rich_textstring"Long-form notes from the page"numbernumber460date{ start: string; end: string }{ start: "2026-03-01", end: "2026-03-02" }statusstring"In progress"selectstring"Non-fiction"multi_selectstring[]["Sci-Fi", "Biography"]checkboxbooleantrueemailstring"tyrus@haustle.studio"phone_numberstring"0000000000"urlstring"https://developers.notion.com/"filesArray<{ name: string; url: string }>[{ name: "brief.pdf", url: "https://..." }]peoplestring[]["1f4e6f4a-5b58-4d91-a7fc-2f5f2a0f6bb1"]relationstring[]["6f7f9cbf-8d45-48f8-a194-661e73f7f5d9"]created_bystring"Ada Lovelace"last_edited_bystring"user_123"created_timestring"2026-03-01T10:30:00.000Z"last_edited_timestring"2026-03-01T13:15:00.000Z"unique_idstring"TASK-42"Unsupported properties
These property types are intentionally excluded from the generated schema and client surface:
formularollupFormula properties are therefore omitted from generated database modules and are unavailable in typed reads, projections, or filters.
Agent client
Properties
idnameiconnull)Methods
chat({ message, threadId? })Sends a message and creates/resumes a threadawait agent.chat({ message: "Hello" })chatStream({ message, threadId?, onMessage? })Streams messages and returns final ThreadInfoawait agent.chatStream({ message: "Hi", onMessage: (m) => {} })pollThread(threadId, options?)Polls until thread processing completesawait agent.pollThread(threadId)getMessages(threadId, { role? })Gets full (or role-filtered) message historyawait agent.getMessages(threadId, { role: "agent" })AgentClient.getAgentResponse(threadInfo)Joins every agent message in ThreadInfo into one stringAgentClient.getAgentResponse(thread)listThreads()Lists recent threads with id, title, and statusawait agent.listThreads()getThreadInfo(threadId)Fetches a single thread recordawait agent.getThreadInfo(threadId)getThreadTitle(threadId)Convenience helper to fetch just the thread titleawait agent.getThreadTitle(threadId)chat
Sends a user message. Omit threadId to start a new thread; pass a prior threadId to continue. The response includes the thread id and status — it does not embed the assistant’s reply text; call getMessages or use chatStream when you need message bodies.
const first = await notion.agents.helpBot.chat({
message: "Give me a high-protein dinner idea under 30 minutes.",
});
// first.threadId, first.status, first.isNewChat
const followUp = await notion.agents.helpBot.chat({
threadId: first.threadId,
message: "Now make it vegetarian.",
});
chat return shape:
threadIdstringchat, getMessages, etc.statusThreadStatusisNewChatbooleantrue when threadId was omitted in the requestchatStream
Streams message chunks via onMessage and resolves to ThreadInfo with the full transcript when the stream completes.
import { AgentClient } from "@haustle/notion-orm";
const thread = await notion.agents.helpBot.chatStream({
message: "How do I reset my password?",
onMessage: (msg) => {
if (msg.role === "agent") process.stdout.write(msg.content);
},
});
const plainText = AgentClient.getAgentResponse(thread);
Resume an existing thread by passing threadId:
const continued = await notion.agents.helpBot.chatStream({
threadId: thread.threadId,
message: "Shorter version, bullet points only.",
onMessage: (msg) => {
/* … */
},
});
ThreadInfo
Returned by chatStream. Use messages for the transcript, or AgentClient.getAgentResponse for a single concatenated agent reply.
threadIdstringagentIdstringmessagesArray<{ role: "user" | "agent"; content: string }>Message items:
roleuser | agentcontentstringAgentClient.getAgentResponse
Static helper: joins every agent message in a ThreadInfo into a single string (useful with chatStream results).
import { AgentClient } from "@haustle/notion-orm";
const thread = await notion.agents.helpBot.chatStream({
message: "One paragraph about our refund policy.",
});
const answer = AgentClient.getAgentResponse(thread);
pollThread
Waits until the thread finishes processing (configurable backoff). Often used after chat before reading messages.
const chat = await notion.agents.helpBot.chat({
message: "Summarize yesterday’s standup notes.",
});
await notion.agents.helpBot.pollThread(chat.threadId);
// Optional: tune polling (defaults: maxAttempts 60, baseDelayMs 1000, …)
await notion.agents.helpBot.pollThread(chat.threadId, {
maxAttempts: 30,
initialDelayMs: 500,
});
getMessages
Loads the full history for a thread. Pass role to return only user or only agent messages.
await notion.agents.helpBot.pollThread(threadId);
const all = await notion.agents.helpBot.getMessages(threadId);
const agentOnly = await notion.agents.helpBot.getMessages(threadId, {
role: "agent",
});
listThreads
Returns recent threads for this agent, each with id, title, and status.
const threads = await notion.agents.helpBot.listThreads();
for (const t of threads) {
console.log(t.id, t.title, t.status);
}
getThreadInfo
Fetches a single thread by ID. Returns the Agents SDK ThreadListItem (includes id, title, status, and related fields).
const info = await notion.agents.helpBot.getThreadInfo("thread-id-here");
// info.id, info.title, info.status, …
getThreadTitle
Shortcut when you only need the title string.
const title = await notion.agents.helpBot.getThreadTitle("thread-id-here");
Generated exports
For script-level usage without the NotionORM wrapper:
@haustle/notion-orm/build/db/<databaseName><databaseName>(auth) factory, DatabaseSchemaType, QuerySchemaType, generated Zod schema, generated option tuples (for select/status/multi-select), schema/type aliases@haustle/notion-orm/build/agents/<agentName><agentName>(auth) factory that returns an AgentClient@haustle/notion-orm/build/dbdatabases barrel object (all database factories)@haustle/notion-orm/build/agentsagents barrel object (all agent factories)