@myapihq/sdk 2.21.1 → 2.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/crm.d.ts CHANGED
@@ -116,6 +116,43 @@ export declare function deleteContact(apiKey: string, orgId: string, id: string)
116
116
  export declare function restoreContact(apiKey: string, orgId: string, id: string): Promise<Contact>;
117
117
  export declare function searchContacts(apiKey: string, orgId: string, filter?: ContactSearchFilter): Promise<ContactSearchResult>;
118
118
  export declare function promoteContact(apiKey: string, orgId: string, goldfoxPersonId: string): Promise<Contact>;
119
+ /** One page of a bulk promotion: what it did, and where to resume. */
120
+ export interface PromoteAudienceResult {
121
+ audience_id: string;
122
+ created: number;
123
+ matched: number;
124
+ /** Goldfox rows with no address — the usual reason 400 becomes 260. */
125
+ skipped_no_email: number;
126
+ failed: number;
127
+ processed: number;
128
+ offset: number;
129
+ total: number;
130
+ has_more: boolean;
131
+ next_offset: number;
132
+ note: string;
133
+ }
134
+ export declare function promoteAudience(apiKey: string, orgId: string, audienceId: string, opts?: {
135
+ limit?: number;
136
+ offset?: number;
137
+ }): Promise<PromoteAudienceResult>;
138
+ /** A row the import could not use, with the reason and the line it was on. */
139
+ export interface SkippedImportRow {
140
+ line: number;
141
+ email?: string;
142
+ /** e.g. "no email", "not an address", "previously deleted". */
143
+ reason: string;
144
+ }
145
+ export interface ImportContactsResult {
146
+ created: number;
147
+ /** Already in CRM: matched, never overwritten. A soft-deleted address is
148
+ * NOT counted here — it comes back as a skipped row, since nothing was
149
+ * imported and the person is not present. */
150
+ matched: number;
151
+ skipped: number;
152
+ skipped_rows: SkippedImportRow[];
153
+ note: string;
154
+ }
155
+ export declare function importContacts(apiKey: string, orgId: string, csv: Blob | Buffer | string, filename?: string): Promise<ImportContactsResult>;
119
156
  export declare function getContactEvents(apiKey: string, orgId: string, id: string, opts?: ListEventsOptions): Promise<EventsResponse>;
120
157
  export declare function createCompany(apiKey: string, orgId: string, input: CreateCompanyInput): Promise<Company>;
121
158
  export declare function getCompany(apiKey: string, orgId: string, id: string): Promise<Company>;
package/dist/crm.js CHANGED
@@ -8,6 +8,8 @@ exports.deleteContact = deleteContact;
8
8
  exports.restoreContact = restoreContact;
9
9
  exports.searchContacts = searchContacts;
10
10
  exports.promoteContact = promoteContact;
11
+ exports.promoteAudience = promoteAudience;
12
+ exports.importContacts = importContacts;
11
13
  exports.getContactEvents = getContactEvents;
12
14
  exports.createCompany = createCompany;
13
15
  exports.getCompany = getCompany;
@@ -21,6 +23,8 @@ const config_1 = require("./config");
21
23
  exports.EXPOSES = [
22
24
  'POST /crm/orgs/{org_id}/contacts',
23
25
  'POST /crm/orgs/{org_id}/contacts/promote',
26
+ 'POST /crm/orgs/{org_id}/contacts/promote-audience',
27
+ 'POST /crm/orgs/{org_id}/contacts/import',
24
28
  'POST /crm/orgs/{org_id}/contacts/search',
25
29
  'GET /crm/orgs/{org_id}/contacts/{id}',
26
30
  'PATCH /crm/orgs/{org_id}/contacts/{id}',
@@ -61,6 +65,37 @@ async function searchContacts(apiKey, orgId, filter = {}) {
61
65
  async function promoteContact(apiKey, orgId, goldfoxPersonId) {
62
66
  return (0, client_1.request)('POST', `${config_1.CRM_BASE}/crm/orgs/${encodeURIComponent(orgId)}/contacts/promote`, apiKey, { goldfox_person_id: goldfoxPersonId });
63
67
  }
68
+ // Promotes ONE page. Idempotent, so resuming or repeating a page costs a
69
+ // match rather than a duplicate of somebody with the same address.
70
+ async function promoteAudience(apiKey, orgId, audienceId, opts) {
71
+ return (0, client_1.request)('POST', `${config_1.CRM_BASE}/crm/orgs/${encodeURIComponent(orgId)}/contacts/promote-audience`, apiKey, {
72
+ audience_id: audienceId, limit: opts?.limit, offset: opts?.offset,
73
+ });
74
+ }
75
+ // Multipart, mirroring the container source upload. The server needs a `file`
76
+ // part; a raw CSV body is rejected with FILE_REQUIRED.
77
+ async function importContacts(apiKey, orgId, csv, filename = 'contacts.csv') {
78
+ const formData = new FormData();
79
+ const blob = typeof csv === 'string'
80
+ ? new Blob([csv], { type: 'text/csv' })
81
+ : new Blob([csv], { type: 'text/csv' });
82
+ formData.append('file', blob, filename);
83
+ const response = await fetch(`${config_1.CRM_BASE}/crm/orgs/${encodeURIComponent(orgId)}/contacts/import`, { method: 'POST', headers: { Authorization: `Bearer ${apiKey}` }, body: formData });
84
+ let result;
85
+ try {
86
+ result = await response.json();
87
+ }
88
+ catch {
89
+ throw new client_1.MyApiError('invalid_json_response', response.status);
90
+ }
91
+ if (!response.ok || !result?.success) {
92
+ const err = result?.error;
93
+ const code = typeof err === 'object' ? (err?.code || 'unknown_error') : (err || 'unknown_error');
94
+ const detail = typeof err === 'object' ? err?.message : undefined;
95
+ throw new client_1.MyApiError(code, response.status, detail, typeof err === 'object' ? err : undefined);
96
+ }
97
+ return result.data;
98
+ }
64
99
  async function getContactEvents(apiKey, orgId, id, opts = {}) {
65
100
  const qs = new URLSearchParams();
66
101
  if (opts.limit != null)
package/dist/email.d.ts CHANGED
@@ -155,3 +155,77 @@ export declare function sendTestEmail(apiKey: string, orgId: string, templateId:
155
155
  }>;
156
156
  export declare function listTemplates(apiKey: string, orgId: string): Promise<EmailTemplate[]>;
157
157
  export declare function deleteTemplate(apiKey: string, orgId: string, templateId: string): Promise<void>;
158
+ export type CampaignSourceKind = 'crm_query' | 'list';
159
+ /** A CRM contact filter, mirroring `crm contacts search`. */
160
+ export interface CampaignCRMQuery {
161
+ stage?: string;
162
+ origin?: string;
163
+ company_id?: string;
164
+ /** Engaged MORE than N days ago — includes contacts never engaged at all. */
165
+ max_last_engagement_days?: number;
166
+ /** Engaged within the last N days. */
167
+ min_last_engagement_days?: number;
168
+ }
169
+ export interface CampaignListSource {
170
+ addresses: string[];
171
+ }
172
+ export interface CreateCampaignInput {
173
+ name: string;
174
+ template_id: string;
175
+ from_address: string;
176
+ source_kind: CampaignSourceKind;
177
+ source_ref?: CampaignCRMQuery | CampaignListSource;
178
+ }
179
+ export interface Campaign {
180
+ id: string;
181
+ org_id: string;
182
+ name: string;
183
+ template_id: string;
184
+ from_address: string;
185
+ source_kind: CampaignSourceKind;
186
+ source_ref: unknown;
187
+ /** draft | ready | active | paused | paused_insufficient_funds | completed | cancelled */
188
+ status: string;
189
+ resolved_at: string | null;
190
+ recipient_count: number | null;
191
+ estimated_cost_cents: number | null;
192
+ created_at: string;
193
+ updated_at: string;
194
+ }
195
+ export interface CampaignResolution {
196
+ status: string;
197
+ recipients: number;
198
+ /** Counts by reason: suppressed, duplicate, invalid. */
199
+ excluded: Record<string, number>;
200
+ source_rows: number;
201
+ estimated_cost_cents: number;
202
+ note: string;
203
+ }
204
+ export interface CampaignRecipient {
205
+ id: string;
206
+ address: string;
207
+ contact_id: string | null;
208
+ state: string;
209
+ excluded_reason: string | null;
210
+ message_id: string | null;
211
+ error: string | null;
212
+ sent_at: string | null;
213
+ }
214
+ export interface CampaignStats {
215
+ status: string;
216
+ by_state: Record<string, number>;
217
+ sent_today: number;
218
+ per_day_limit: number;
219
+ }
220
+ export declare function createCampaign(apiKey: string, orgId: string, input: CreateCampaignInput): Promise<Campaign>;
221
+ export declare function listCampaigns(apiKey: string, orgId: string): Promise<Campaign[]>;
222
+ export declare function getCampaign(apiKey: string, orgId: string, id: string): Promise<Campaign>;
223
+ export declare function updateCampaign(apiKey: string, orgId: string, id: string, patch: Partial<CreateCampaignInput>): Promise<Campaign>;
224
+ /** Freezes the recipient set and reports who it reaches and what it would cost. Sends nothing. */
225
+ export declare function resolveCampaign(apiKey: string, orgId: string, id: string): Promise<CampaignResolution>;
226
+ export declare function startCampaign(apiKey: string, orgId: string, id: string): Promise<Campaign>;
227
+ export declare function pauseCampaign(apiKey: string, orgId: string, id: string): Promise<Campaign>;
228
+ export declare function resumeCampaign(apiKey: string, orgId: string, id: string): Promise<Campaign>;
229
+ export declare function cancelCampaign(apiKey: string, orgId: string, id: string): Promise<Campaign>;
230
+ export declare function listCampaignRecipients(apiKey: string, orgId: string, id: string, state?: string): Promise<CampaignRecipient[]>;
231
+ export declare function getCampaignStats(apiKey: string, orgId: string, id: string): Promise<CampaignStats>;
package/dist/email.js CHANGED
@@ -30,6 +30,17 @@ exports.editTemplate = editTemplate;
30
30
  exports.sendTestEmail = sendTestEmail;
31
31
  exports.listTemplates = listTemplates;
32
32
  exports.deleteTemplate = deleteTemplate;
33
+ exports.createCampaign = createCampaign;
34
+ exports.listCampaigns = listCampaigns;
35
+ exports.getCampaign = getCampaign;
36
+ exports.updateCampaign = updateCampaign;
37
+ exports.resolveCampaign = resolveCampaign;
38
+ exports.startCampaign = startCampaign;
39
+ exports.pauseCampaign = pauseCampaign;
40
+ exports.resumeCampaign = resumeCampaign;
41
+ exports.cancelCampaign = cancelCampaign;
42
+ exports.listCampaignRecipients = listCampaignRecipients;
43
+ exports.getCampaignStats = getCampaignStats;
33
44
  const client_1 = require("./client");
34
45
  const config_1 = require("./config");
35
46
  exports.EXPOSES = [
@@ -67,6 +78,18 @@ exports.EXPOSES = [
67
78
  'POST /email/orgs/{org_id}/verify',
68
79
  'POST /email/orgs/{org_id}/verify-bulk',
69
80
  'GET /email/orgs/{org_id}/verify-jobs/{id}',
81
+ // Campaigns — scheduling on top of send
82
+ 'POST /email/orgs/{org_id}/campaigns',
83
+ 'GET /email/orgs/{org_id}/campaigns',
84
+ 'GET /email/orgs/{org_id}/campaigns/{campaign_id}',
85
+ 'PATCH /email/orgs/{org_id}/campaigns/{campaign_id}',
86
+ 'POST /email/orgs/{org_id}/campaigns/{campaign_id}/resolve',
87
+ 'POST /email/orgs/{org_id}/campaigns/{campaign_id}/start',
88
+ 'POST /email/orgs/{org_id}/campaigns/{campaign_id}/pause',
89
+ 'POST /email/orgs/{org_id}/campaigns/{campaign_id}/resume',
90
+ 'POST /email/orgs/{org_id}/campaigns/{campaign_id}/cancel',
91
+ 'GET /email/orgs/{org_id}/campaigns/{campaign_id}/recipients',
92
+ 'GET /email/orgs/{org_id}/campaigns/{campaign_id}/stats',
70
93
  ];
71
94
  async function verifyEmail(apiKey, orgId, email) {
72
95
  return (0, client_1.request)('POST', `${config_1.EMAIL_BASE}/email/orgs/${encodeURIComponent(orgId)}/verify`, apiKey, { email });
@@ -197,3 +220,47 @@ async function listTemplates(apiKey, orgId) {
197
220
  async function deleteTemplate(apiKey, orgId, templateId) {
198
221
  return (0, client_1.request)('DELETE', `${config_1.EMAIL_BASE}/email/orgs/${encodeURIComponent(orgId)}/templates/${encodeURIComponent(templateId)}`, apiKey);
199
222
  }
223
+ function campaignsUrl(orgId) {
224
+ return `${config_1.EMAIL_BASE}/email/orgs/${encodeURIComponent(orgId)}/campaigns`;
225
+ }
226
+ function campaignUrl(orgId, id) {
227
+ return `${campaignsUrl(orgId)}/${encodeURIComponent(id)}`;
228
+ }
229
+ async function createCampaign(apiKey, orgId, input) {
230
+ return (0, client_1.request)('POST', campaignsUrl(orgId), apiKey, input);
231
+ }
232
+ // Walks the cursor: one page is not the whole list.
233
+ async function listCampaigns(apiKey, orgId) {
234
+ return (0, client_1.requestAll)(campaignsUrl(orgId), apiKey);
235
+ }
236
+ async function getCampaign(apiKey, orgId, id) {
237
+ return (0, client_1.request)('GET', campaignUrl(orgId, id), apiKey);
238
+ }
239
+ async function updateCampaign(apiKey, orgId, id, patch) {
240
+ return (0, client_1.request)('PATCH', campaignUrl(orgId, id), apiKey, patch);
241
+ }
242
+ /** Freezes the recipient set and reports who it reaches and what it would cost. Sends nothing. */
243
+ async function resolveCampaign(apiKey, orgId, id) {
244
+ return (0, client_1.request)('POST', `${campaignUrl(orgId, id)}/resolve`, apiKey);
245
+ }
246
+ async function startCampaign(apiKey, orgId, id) {
247
+ return (0, client_1.request)('POST', `${campaignUrl(orgId, id)}/start`, apiKey);
248
+ }
249
+ async function pauseCampaign(apiKey, orgId, id) {
250
+ return (0, client_1.request)('POST', `${campaignUrl(orgId, id)}/pause`, apiKey);
251
+ }
252
+ async function resumeCampaign(apiKey, orgId, id) {
253
+ return (0, client_1.request)('POST', `${campaignUrl(orgId, id)}/resume`, apiKey);
254
+ }
255
+ async function cancelCampaign(apiKey, orgId, id) {
256
+ return (0, client_1.request)('POST', `${campaignUrl(orgId, id)}/cancel`, apiKey);
257
+ }
258
+ async function listCampaignRecipients(apiKey, orgId, id, state) {
259
+ const url = state
260
+ ? `${campaignUrl(orgId, id)}/recipients?state=${encodeURIComponent(state)}`
261
+ : `${campaignUrl(orgId, id)}/recipients`;
262
+ return (0, client_1.requestAll)(url, apiKey);
263
+ }
264
+ async function getCampaignStats(apiKey, orgId, id) {
265
+ return (0, client_1.request)('GET', `${campaignUrl(orgId, id)}/stats`, apiKey);
266
+ }
package/dist/hq.d.ts CHANGED
@@ -76,6 +76,30 @@ export type FreeTierEntry = {
76
76
  reset_at?: string;
77
77
  };
78
78
  export declare function getFreeTier(apiKey: string): Promise<FreeTierEntry[] | null>;
79
+ export interface SendingStatus {
80
+ sending_paused: boolean;
81
+ sends_24h: number;
82
+ bounces_24h: number;
83
+ complaints_24h: number;
84
+ bounce_threshold: number;
85
+ complaint_threshold: number;
86
+ within_limits: boolean;
87
+ /** Why it was paused, as recorded THEN — not whether it would pause now. */
88
+ paused_reason_when_paused?: string;
89
+ /** Present only while paused: whether a resume would be accepted today. */
90
+ can_resume_now?: boolean;
91
+ /** Set when under 100 sends in 24h, where the ratios are not judged. */
92
+ note?: string;
93
+ }
94
+ export interface ResumeSendingResult {
95
+ sending_paused: boolean;
96
+ resumed: boolean;
97
+ sends: number;
98
+ bounces: number;
99
+ message: string;
100
+ }
101
+ export declare function getSendingStatus(apiKey: string): Promise<SendingStatus>;
102
+ export declare function resumeSending(apiKey: string): Promise<ResumeSendingResult>;
79
103
  export declare function getAccount(apiKey: string): Promise<AccountInfo>;
80
104
  export declare function getMailingAddress(apiKey: string): Promise<{
81
105
  mailing_address: string | null;
package/dist/hq.js CHANGED
@@ -6,6 +6,8 @@ exports.sendCode = sendCode;
6
6
  exports.verifyCode = verifyCode;
7
7
  exports.upgradeAccount = upgradeAccount;
8
8
  exports.getFreeTier = getFreeTier;
9
+ exports.getSendingStatus = getSendingStatus;
10
+ exports.resumeSending = resumeSending;
9
11
  exports.getAccount = getAccount;
10
12
  exports.getMailingAddress = getMailingAddress;
11
13
  exports.setMailingAddress = setMailingAddress;
@@ -42,6 +44,8 @@ exports.EXPOSES = [
42
44
  'PATCH /hq/account/upgrade',
43
45
  'GET /hq/account/me',
44
46
  'GET /hq/account/free-tier',
47
+ 'GET /hq/account/sending',
48
+ 'POST /hq/account/resume-sending',
45
49
  'POST /hq/account/create/key',
46
50
  'GET /hq/account/keys',
47
51
  'POST /hq/account/keys/revoke-all',
@@ -92,6 +96,13 @@ async function upgradeAccount(apiKey, email) {
92
96
  async function getFreeTier(apiKey) {
93
97
  return (0, client_1.request)('GET', `${config_1.HQ_BASE}/hq/account/free-tier`, apiKey);
94
98
  }
99
+ async function getSendingStatus(apiKey) {
100
+ return (0, client_1.request)('GET', `${config_1.HQ_BASE}/hq/account/sending`, apiKey);
101
+ }
102
+ // Rejected with STILL_OVER_THRESHOLD while the last 24h remain over the limit.
103
+ async function resumeSending(apiKey) {
104
+ return (0, client_1.request)('POST', `${config_1.HQ_BASE}/hq/account/resume-sending`, apiKey);
105
+ }
95
106
  async function getAccount(apiKey) {
96
107
  return (0, client_1.request)('GET', `${config_1.HQ_BASE}/hq/account/me`, apiKey);
97
108
  }
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "2.21.1";
1
+ export declare const SDK_VERSION = "2.23.0";
package/dist/version.js CHANGED
@@ -8,4 +8,4 @@ exports.SDK_VERSION = void 0;
8
8
  // Why a constant and not a package.json read: the SDK runs inside edge
9
9
  // functions (Cloudflare Workers), so it must not import node:fs. A literal
10
10
  // is the only version source that works in every runtime we ship to.
11
- exports.SDK_VERSION = '2.21.1';
11
+ exports.SDK_VERSION = '2.23.0';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@myapihq/sdk",
3
3
  "license": "Apache-2.0",
4
- "version": "2.21.1",
4
+ "version": "2.23.0",
5
5
  "description": "TypeScript SDK for the MyAPI ecosystem",
6
6
  "repository": {
7
7
  "type": "git",