@bara-agency/dotloop-sdk
v0.2.0
Published
Server-side TypeScript SDK for the Dotloop Public API v2.
Maintainers
Readme
@bara-agency/dotloop-sdk
Server-side TypeScript SDK for the Dotloop Public API v2.
Node.js >=20 only. Keep clientId, clientSecret, and tokens on the server — never ship them to the browser.
Install
npm install @bara-agency/dotloop-sdkQuick start
import { DotloopClient } from "@bara-agency/dotloop-sdk";
const client = new DotloopClient({
clientId: process.env.DOTLOOP_CLIENT_ID!,
clientSecret: process.env.DOTLOOP_CLIENT_SECRET!,
refreshToken: process.env.DOTLOOP_REFRESH_TOKEN!,
});
const account = await client.account.get();
for await (const page of client.loops.iterate(profileId, { batchSize: 100 })) {
console.log(page.data.length);
}The SDK refreshes access tokens on 401 (once, under a refresh lock) and retries idempotent GET/DELETE failures with exponential backoff. Pass credentials explicitly — the package does not read env vars itself.
OAuth helpers
import { DotloopClient } from "@bara-agency/dotloop-sdk";
const url = DotloopClient.getAuthorizationUrl({
clientId,
redirectUri: "https://app.example/callback",
state: "csrf-token",
});
// After the user consents:
await client.exchangeCode({
code,
redirectUri: "https://app.example/callback",
});Shared token store (multi-instance)
import {
DotloopClient,
createMemoryTokenStore,
type TokenStore,
} from "@bara-agency/dotloop-sdk";
const tokenStore: TokenStore = createMemoryTokenStore();
// Or implement get/set/withRefreshLock against Redis/DB.
const client = new DotloopClient({
clientId,
clientSecret,
refreshToken,
tokenStore,
});Webhook signature verification
import { verifyWebhookSignature } from "@bara-agency/dotloop-sdk";
verifyWebhookSignature({
body: rawBodyString,
signature: request.headers["x-dotloop-signature"]!,
timestamp: request.headers["x-dotloop-timestamp"]!,
signingKey,
});Throws DotloopWebhookVerificationError on invalid or stale signatures.
Resources
account, profiles, loops, loopDetails, folders, documents, participants, tasks, activities, contacts, templates, loopIt, webhooks.subscriptions, webhooks.events.
Escape hatch for untyped or custom calls: client.request<T>(path, method?, body?).
Optional Postgres store (@bara-agency/dotloop-sdk/db)
The Dotloop API cannot search Loops by structured parameters. The optional db subpath ships a typed Postgres store (migrations + CRUD/search) so you can sync Loops and Profiles locally and query them yourself. The root entry stays dependency-free — pg is an optional peer dependency you inject.
import { createDotloopStore } from "@bara-agency/dotloop-sdk/db";
const store = createDotloopStore({ db: pool }); // your pg Pool/Client
await store.migrate();
await store.profiles.upsertMany(profiles);
await store.loops.upsertMany(loops); // loops need profileId set
const rows = await store.loops.search({
status: ["Active", "Under Contract"],
updatedAfter: "2026-07-01T00:00:00.000Z",
nameContains: "maple",
limit: 50,
});createDotloopSync({ client, store }) keeps the store current: sync() mirrors all profiles and loops from the API (opt-in prune), and ingestWebhook(payload) applies a webhook delivery (loop/profile lifecycle events, idempotent, fetches the current resource per event).
The same subpath ships a Postgres-backed token store for multi-instance deploys — plug it into the client's existing tokenStore option and OAuth refresh tokens (e.g. from exchangeCode in your integrated app) persist in the database:
import { createPostgresTokenStore } from "@bara-agency/dotloop-sdk/db";
const client = new DotloopClient({
clientId,
clientSecret,
tokenStore: createPostgresTokenStore({ db: pool }), // key defaults to "default"
});Refreshes serialize across instances via a Postgres advisory lock. Full schema and API reference: docs/database-layer.md.
Design
See docs/superpowers/specs/2026-07-29-dotloop-sdk-design.md.
Publishing (maintainers)
Releases publish via GitHub Actions (.github/workflows/publish.yml) using npm trusted publishing (OIDC). No NPM_TOKEN secret is required.
Trusted Publisher (already configured on npmjs.com for @bara-agency/dotloop-sdk):
| Field | Value |
|-------|-------|
| Organization | baraagency |
| Repository | dotloop-sdk |
| Workflow filename | publish.yml (filename only — must match exactly) |
To publish a new version
- Bump
versioninpackage.json(and commit). - Create a GitHub Release (preferred) or run the Publish workflow manually (
workflow_dispatch). - The workflow authenticates via OIDC — do not set
NODE_AUTH_TOKEN/NPM_TOKENon the publish step.
This repository is private, so npm will not attach provenance attestations (a known limitation for private source repos). That is expected; the publish still succeeds.
