@appnotes/api-client
v0.15.0
Published
Typed AppNotes HTTP client
Maintainers
Readme
@appnotes/api-client
Typed AppNotes HTTP client for browsers and Node.js.
import { AppNotesClient } from "@appnotes/api-client";
const client = new AppNotesClient({
baseUrl: "https://api.appnotes.example",
projectKey: "appnotes_pk_...",
getAccessToken: () => accessToken,
getRefreshToken: () => refreshToken,
onSessionRefresh: (session) => {
accessToken = session.accessToken;
refreshToken = session.refreshToken;
},
});
const { items } = await client.listThreads({
roomId: "account-42",
pageId: window.location.href,
scope: "page",
});
await client.createThread({
roomId: "account-42",
roomName: "Codeception",
body: "Check the current account settings.",
});
if (items[0]) {
await client.updateThread(items[0].id, {
title: "Updated context",
body: "The revised note body.",
});
await client.markThreadRead(items[0].id, items[0].updatedAt);
await client.setThreadPinned(items[0].id, true);
const uploaded = await client.uploadAttachment(
items[0].id,
new File(["report"], "report.txt", { type: "text/plain" }),
);
const content = await client.downloadAttachment(uploaded.attachment.id);
}When both token callbacks are configured, protected requests automatically rotate an
expired session and retry once.
login() creates a global session because this client exposes privileged project
and account administration. Do not ship its refresh token in an untrusted or
third-party SPA and do not persist it in localStorage. Embedded browser
applications should use @appnotes/sdk, whose refresh credential is scoped to one
project and held only in an API-origin HttpOnly cookie.
When AppNotesApiError.details.details.requiresTwoFactor is true, call
login(email, password, otp) with the current six-digit authenticator code.
Project owners and administrators can also use updateProjectMemberRole and
removeProjectMember to manage project access.
createProjectInviteLink(role) creates a reusable invitation URL that keeps the selected role and
expires after 48 hours.
Thread list responses include room/page/archive unread totals, and every thread
includes its unread message count for the authenticated user.
Files are uploaded as authenticated multipart requests. Attachment content is
always fetched through the authorized API rather than a public storage URL.
Server-side integrations
Use AppNotesIntegrationClient with a personal API key created in Dashboard →
Profile. It exposes only the versioned note and comment write API and does not
create or refresh browser sessions. Attachment methods use the same versioned,
permission-aware API:
import { readFile } from "node:fs/promises";
import { AppNotesIntegrationClient } from "@appnotes/api-client";
const appNotes = new AppNotesIntegrationClient({
baseUrl: "https://api.appnotes.tech/api",
projectKey: "appnotes_pk_...",
apiKey: process.env.APPNOTES_API_KEY!,
});
const note = await appNotes.createNote({
roomId: "customer-128",
roomName: "Acme",
body: "Created by the CRM integration.",
});
const comment = await appNotes.addComment(note.id, "The external workflow has completed.");
const reportBlob = new Blob([await readFile("./report.pdf")], { type: "application/pdf" });
const { attachment } = await appNotes.uploadAttachment(note.id, reportBlob, {
commentId: comment.id,
fileName: "report.pdf",
});
const content = await appNotes.downloadAttachment(attachment.id);The API evaluates the key owner's current project role and room access on every request, including attachment upload, download, and deletion. Keep the key in a server-side secret store; never expose it to browser or mobile client code.
