JavaScript sample
A minimal Node.js client for the BottleCRM API. Uses native fetch (Node 18+), no external dependencies.
A tiny client
const BASE = process.env.BOTTLECRM_URL.replace(/\/$/, "");
const TOKEN = process.env.BOTTLECRM_TOKEN;
async function api(path, init = {}) {
const res = await fetch(`${BASE}${path}`, {
...init,
headers: {
Authorization: `Bearer ${TOKEN}`,
"Content-Type": "application/json",
...(init.headers ?? {}),
},
});
if (!res.ok) {
const body = await res.text();
throw new Error(`${res.status} ${res.statusText}: ${body}`);
}
return res.status === 204 ? null : res.json();
}
export async function* listLeads(filters = {}) {
const qs = new URLSearchParams(filters).toString();
let url = `/api/leads/${qs ? `?${qs}` : ""}`;
while (url) {
const page = await api(url);
yield* page.results;
url = page.next ? new URL(page.next).pathname + new URL(page.next).search : null;
}
}
export const createLead = (fields) =>
api("/api/leads/", { method: "POST", body: JSON.stringify(fields) });
export const updateLead = (id, fields) =>
api(`/api/leads/${id}/`, { method: "PATCH", body: JSON.stringify(fields) });
Example: poll for changes
BottleCRM sends no outbound webhooks, so reacting to events means asking. Keep a high-water mark and ask for what has changed since you last looked.
import { listLeads } from "./bottlecrm-client.js";
let since = new Date(Date.now() - 60_000).toISOString();
async function poll() {
const now = new Date().toISOString();
for await (const lead of listLeads({ created_at__gte: since })) {
await enqueueOnboardingEmail(lead.email);
}
// Advance only after the batch is handled, so a crash re-reads rather than skips.
since = now;
}
setInterval(() => poll().catch(console.error), 60_000);
Example: build a custom dashboard widget
import { listLeads } from "./bottlecrm-client.js";
async function thisWeekByStatus() {
const buckets = { new: 0, contacted: 0, qualified: 0, lost: 0, converted: 0 };
const since = new Date(Date.now() - 7 * 24 * 3600 * 1000).toISOString();
for await (const lead of listLeads({ created_at__gte: since })) {
buckets[lead.status] = (buckets[lead.status] ?? 0) + 1;
}
return buckets;
}
console.log(await thisWeekByStatus());
Browser usage
In the browser, never embed a JWT in client code. Instead proxy through your own server, or use a session cookie that your origin sets after BottleCRM's OAuth flow.