import "dotenv/config";
export class InstantlyClient {
constructor(
private apiKey = process.env.INSTANTLY_API_KEY!,
private baseUrl = "https://api.instantly.ai/api/v2"
) {}
async request<T>(path: string, init: RequestInit = {}): Promise<T> {
const res = await fetch(`${this.baseUrl}${path}`, {
...init,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${this.apiKey}`,
...init.headers,
},
});
if (res.status === 429) {
await new Promise((r) => setTimeout(r, 2000));
return this.request<T>(path, init);
}
if (!res.ok) {
throw new InstantlyApiError(res.status, path, await res.text());
}
return res.json() as Promise<T>;
}
campaigns = {
list: (limit = 50) => this.request<Campaign[]>(`/campaigns?limit=${limit}`),
get: (id: string) => this.request<Campaign>(`/campaigns/${id}`),
create: (data: CreateCampaignInput) =>
this.request<Campaign>("/campaigns", { method: "POST", body: JSON.stringify(data) }),
activate: (id: string) =>
this.request<void>(`/campaigns/${id}/activate`, { method: "POST" }),
pause: (id: string) =>
this.request<void>(`/campaigns/${id}/pause`, { method: "POST" }),
analytics: (id: string) =>
this.request<CampaignAnalytics>(`/campaigns/analytics?id=${id}`),
analyticsDaily: (id: string, start: string, end: string) =>
this.request(`/campaigns/analytics/daily?campaign_id=${id}&start_date=${start}&end_date=${end}`),
};
leads = {
create: (data: CreateLeadInput) =>
this.request<Lead>("/leads", { method: "POST", body: JSON.stringify(data) }),
list: (filter: ListLeadsInput) =>
this.request<Lead[]>("/leads/list", { method: "POST", body: JSON.stringify(filter) }),
get: (id: string) => this.request<Lead>(`/leads/${id}`),
delete: (id: string) => this.request(`/leads/${id}`, { method: "DELETE" }),
updateInterest: (email: string, campaignId: string, value: number) =>
this.request("/leads/update-interest-status", {
method: "POST",
body: JSON.stringify({ lead_email: email, campaign_id: campaignId, interest_value: value }),
}),
};
accounts = {
list: (limit = 50) => this.request<Account[]>(`/accounts?limit=${limit}`),
get: (email: string) => this.request<Account>(`/accounts/${encodeURIComponent(email)}`),
enableWarmup: (emails: string[]) =>
this.request("/accounts/warmup/enable", { method: "POST", body: JSON.stringify({ emails }) }),
warmupAnalytics: (emails: string[]) =>
this.request("/accounts/warmup-analytics", { method: "POST", body: JSON.stringify({ emails }) }),
testVitals: (emails: string[]) =>
this.request("/accounts/test/vitals", { method: "POST", body: JSON.stringify({ accounts: emails }) }),
pause: (email: string) =>
this.request(`/accounts/${encodeURIComponent(email)}/pause`, { method: "POST" }),
resume: (email: string) =>
this.request(`/accounts/${encodeURIComponent(email)}/resume`, { method: "POST" }),
};
webhooks = {
list: () => this.request<Webhook[]>("/webhooks?limit=50"),
create: (data: CreateWebhookInput) =>
this.request<Webhook>("/webhooks", { method: "POST", body: JSON.stringify(data) }),
test: (id: string) => this.request(`/webhooks/${id}/test`, { method: "POST" }),
delete: (id: string) => this.request(`/webhooks/${id}`, { method: "DELETE" }),
};
}
export const SEQUENCE_TEMPLATES = {
"3-step-cold": {
name: "3-Step Cold Outreach",
sequences: [{
steps: [
{ type: "email" as const, delay: 0, variants: [
{ subject: "{{firstName}}, quick question about {{companyName}}", body: "..." },
]},
{ type: "email" as const, delay: 3, delay_unit: "days" as const, variants: [
{ subject: "Re: {{firstName}}, quick question", body: "..." },
]},
{ type: "email" as const, delay: 5, delay_unit: "days" as const, variants: [
{ subject: "Re: {{firstName}}, quick question", body: "..." },
]},
],
}],
defaults: {
daily_limit: 50,
stop_on_reply: true,
email_gap: 120,
link_tracking: false,
open_tracking: true,
},
},
"meeting-request": {
name: "Meeting Request",
sequences: [{
steps: [
{ type: "email" as const, delay: 0, variants: [
{ subject: "15 min chat, {{firstName}}?", body: "..." },
]},
{ type: "email" as const, delay: 2, delay_unit: "days" as const, variants: [
{ subject: "Re: 15 min chat", body: "..." },
]},
],
}],
defaults: {
daily_limit: 30,
stop_on_reply: true,
email_gap: 180,
},
},
};