@startsimpli/api 0.5.5 → 0.5.7
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/package.json +1 -1
- package/src/constants/endpoints.ts +8 -0
- package/src/index.ts +25 -1
- package/src/lib/enrichment-api.ts +57 -0
- package/src/lib/funnels-api.ts +56 -16
- package/src/lib/message-templates-api.ts +95 -0
- package/src/lib/target-lists-api.ts +190 -0
- package/src/types/enrichment.ts +28 -0
- package/src/types/index.ts +8 -0
- package/src/types/user.ts +13 -12
package/package.json
CHANGED
|
@@ -40,6 +40,10 @@ export const ENDPOINTS = {
|
|
|
40
40
|
// Funnels
|
|
41
41
|
FUNNELS: 'api/v1/funnels',
|
|
42
42
|
FUNNEL: (id: string) => `api/v1/funnels/${id}`,
|
|
43
|
+
FUNNEL_PIPELINE: (id: string) => `api/v1/funnels/${id}/pipeline`,
|
|
44
|
+
FUNNEL_ADD_ENTITY: (id: string) => `api/v1/funnels/${id}/add_entity`,
|
|
45
|
+
FUNNEL_MOVE: (id: string) => `api/v1/funnels/${id}/move`,
|
|
46
|
+
FUNNEL_STATS: (id: string) => `api/v1/funnels/${id}/stats`,
|
|
43
47
|
FUNNEL_RUN: (id: string) => `api/v1/funnels/${id}/run`,
|
|
44
48
|
FUNNEL_RUNS: (id: string) => `api/v1/funnels/${id}/runs`,
|
|
45
49
|
FUNNEL_RUN_ITEM: (funnelId: string, runId: string) => `api/v1/funnels/${funnelId}/runs/${runId}`,
|
|
@@ -49,6 +53,10 @@ export const ENDPOINTS = {
|
|
|
49
53
|
FUNNEL_RUN_BY_ID: (runId: string) => `api/v1/funnel-runs/${runId}`,
|
|
50
54
|
FUNNEL_RUN_CANCEL: (runId: string) => `api/v1/funnel-runs/${runId}/cancel`,
|
|
51
55
|
FUNNEL_RUNS_GLOBAL: 'api/v1/funnel-runs',
|
|
56
|
+
// Enrichment
|
|
57
|
+
ENRICHMENT_QUEUE: 'api/v1/enrichment/start-processing',
|
|
58
|
+
CONTACT_ENRICH: (id: string) => `api/v1/contacts/${id}/enrich`,
|
|
59
|
+
|
|
52
60
|
// Users
|
|
53
61
|
USER_ME: 'api/v1/users/me',
|
|
54
62
|
USER_CHANGE_PASSWORD: 'api/v1/users/me/change-password',
|
package/src/index.ts
CHANGED
|
@@ -15,11 +15,29 @@ export { OrganizationsApi } from './lib/organizations-api';
|
|
|
15
15
|
export { EntitiesApi } from './lib/entities-api';
|
|
16
16
|
export { WorkflowsApi } from './lib/workflows-api';
|
|
17
17
|
export { MessagesApi } from './lib/messages-api';
|
|
18
|
-
export type { Message, MessageStatus as MessageApiStatus, MessageRecipient, MessagingChannel as MessagingChannelType } from './lib/messages-api';
|
|
18
|
+
export type { Message, MessageStatus as MessageApiStatus, MessageRecipient, MessagingChannel as MessagingChannelType, MessageFilters, CreateMessageInput, ScheduleMessageInput, SendTestInput } from './lib/messages-api';
|
|
19
|
+
export { MessageTemplatesApi } from './lib/message-templates-api';
|
|
20
|
+
export type { MessageTemplate, MessageTemplateFilters, CreateMessageTemplateInput } from './lib/message-templates-api';
|
|
19
21
|
export { UsersApi } from './lib/users-api';
|
|
20
22
|
export type { UserProfile, UpdateProfileRequest, ChangePasswordRequest, ChangePasswordResponse } from './types/user';
|
|
21
23
|
export { FunnelsApi, isFunnelRunConflict, isFunnelValidationError } from './lib/funnels-api';
|
|
22
24
|
export type { FunnelPreviewResult, FunnelRunFilters, FunnelTemplate } from './lib/funnels-api';
|
|
25
|
+
export { EnrichmentApi } from './lib/enrichment-api';
|
|
26
|
+
export { TargetListsApi } from './lib/target-lists-api';
|
|
27
|
+
export type {
|
|
28
|
+
TargetList,
|
|
29
|
+
TargetListMember,
|
|
30
|
+
CreateTargetListInput,
|
|
31
|
+
UpdateTargetListInput,
|
|
32
|
+
TargetListFilters,
|
|
33
|
+
TargetListMemberFilters,
|
|
34
|
+
TargetType,
|
|
35
|
+
ListSourceType,
|
|
36
|
+
RefreshStrategy,
|
|
37
|
+
AddMembersResult,
|
|
38
|
+
RemoveMembersResult,
|
|
39
|
+
RefreshResult,
|
|
40
|
+
} from './lib/target-lists-api';
|
|
23
41
|
|
|
24
42
|
// Feature flags
|
|
25
43
|
export { FeatureFlagsApi, FeatureFlagProvider, useFeatureFlags } from './lib/feature-flags';
|
|
@@ -129,6 +147,9 @@ import { MessagesApi } from './lib/messages-api';
|
|
|
129
147
|
import { FunnelsApi } from './lib/funnels-api';
|
|
130
148
|
import { FeatureFlagsApi } from './lib/feature-flags';
|
|
131
149
|
import { UsersApi } from './lib/users-api';
|
|
150
|
+
import { EnrichmentApi } from './lib/enrichment-api';
|
|
151
|
+
import { TargetListsApi } from './lib/target-lists-api';
|
|
152
|
+
import { MessageTemplatesApi } from './lib/message-templates-api';
|
|
132
153
|
|
|
133
154
|
import type { ApiClientConfig } from './lib/api-client';
|
|
134
155
|
|
|
@@ -149,5 +170,8 @@ export function createStartSimpliApi(config: ApiClientConfig = {}) {
|
|
|
149
170
|
funnels: new FunnelsApi(client),
|
|
150
171
|
featureFlags: new FeatureFlagsApi(client),
|
|
151
172
|
users: new UsersApi(client),
|
|
173
|
+
enrichment: new EnrichmentApi(client),
|
|
174
|
+
targetLists: new TargetListsApi(client),
|
|
175
|
+
messageTemplates: new MessageTemplatesApi(client),
|
|
152
176
|
};
|
|
153
177
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enrichment API wrapper for /api/v1/enrichment/ and /api/v1/contacts/:id/enrich/
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { EnrichmentResult, QueueStatus } from '../types';
|
|
6
|
+
import { ENDPOINTS } from '../constants/endpoints';
|
|
7
|
+
import type { ApiClient } from './api-client';
|
|
8
|
+
|
|
9
|
+
export class EnrichmentApi {
|
|
10
|
+
constructor(private client: ApiClient) {}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Enrich a single contact by ID
|
|
14
|
+
*/
|
|
15
|
+
async enrich(contactId: string): Promise<EnrichmentResult> {
|
|
16
|
+
return this.client.fetch.post<EnrichmentResult>(
|
|
17
|
+
ENDPOINTS.CONTACT_ENRICH(contactId),
|
|
18
|
+
{}
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Enrich multiple contacts. Calls the single-contact endpoint for each ID.
|
|
24
|
+
* Returns results in order, including failures.
|
|
25
|
+
*/
|
|
26
|
+
async bulkEnrich(
|
|
27
|
+
contactIds: string[],
|
|
28
|
+
onProgress?: (completed: number, total: number) => void
|
|
29
|
+
): Promise<EnrichmentResult[]> {
|
|
30
|
+
const results: EnrichmentResult[] = [];
|
|
31
|
+
|
|
32
|
+
for (let i = 0; i < contactIds.length; i++) {
|
|
33
|
+
try {
|
|
34
|
+
const result = await this.enrich(contactIds[i]);
|
|
35
|
+
results.push(result);
|
|
36
|
+
} catch (err: unknown) {
|
|
37
|
+
results.push({
|
|
38
|
+
contactId: contactIds[i],
|
|
39
|
+
contactName: '',
|
|
40
|
+
success: false,
|
|
41
|
+
fieldsUpdated: [],
|
|
42
|
+
error: err instanceof Error ? err.message : 'Enrichment failed',
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
onProgress?.(i + 1, contactIds.length);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return results;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get the current enrichment queue status
|
|
53
|
+
*/
|
|
54
|
+
async getQueueStatus(): Promise<QueueStatus> {
|
|
55
|
+
return this.client.fetch.get<QueueStatus>(ENDPOINTS.ENRICHMENT_QUEUE);
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/lib/funnels-api.ts
CHANGED
|
@@ -123,6 +123,44 @@ export class FunnelsApi {
|
|
|
123
123
|
return this.client.delete<void>(ENDPOINTS.FUNNEL(id));
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
// ── Generic pipeline actions (work with any funnel/profile) ──────────
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get entities grouped by stage (kanban view).
|
|
130
|
+
* Works for any funnel that has a membership run with entities in stages.
|
|
131
|
+
*/
|
|
132
|
+
async pipeline(id: string): Promise<Record<string, FunnelResult[]>> {
|
|
133
|
+
return this.client.get<Record<string, FunnelResult[]>>(ENDPOINTS.FUNNEL_PIPELINE(id));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Add an entity to a funnel's membership run.
|
|
138
|
+
* Creates a FunnelResult with the entity + optional context (deal amount, notes, etc.)
|
|
139
|
+
*/
|
|
140
|
+
async addEntity(
|
|
141
|
+
funnelId: string,
|
|
142
|
+
data: { entityId: string; stage?: string; context?: Record<string, unknown> }
|
|
143
|
+
): Promise<FunnelResult> {
|
|
144
|
+
return this.client.post<FunnelResult>(ENDPOINTS.FUNNEL_ADD_ENTITY(funnelId), data);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Move an entity to a different stage within a funnel.
|
|
149
|
+
*/
|
|
150
|
+
async move(
|
|
151
|
+
funnelId: string,
|
|
152
|
+
data: { entityId: string; newStage: string; context?: Record<string, unknown> }
|
|
153
|
+
): Promise<FunnelResult> {
|
|
154
|
+
return this.client.post<FunnelResult>(ENDPOINTS.FUNNEL_MOVE(funnelId), data);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Get funnel statistics (stage counts, conversion rates, etc.)
|
|
159
|
+
*/
|
|
160
|
+
async stats(funnelId: string): Promise<Record<string, unknown>> {
|
|
161
|
+
return this.client.get<Record<string, unknown>>(ENDPOINTS.FUNNEL_STATS(funnelId));
|
|
162
|
+
}
|
|
163
|
+
|
|
126
164
|
/**
|
|
127
165
|
* Execute a funnel run
|
|
128
166
|
*/
|
|
@@ -157,24 +195,26 @@ export class FunnelsApi {
|
|
|
157
195
|
return this.client.get<FunnelRun>(ENDPOINTS.FUNNEL_RUN_BY_ID(runId));
|
|
158
196
|
}
|
|
159
197
|
|
|
160
|
-
|
|
198
|
+
/**
|
|
199
|
+
* Get results (entities) from a funnel's latest completed run.
|
|
200
|
+
*/
|
|
161
201
|
async getResults(
|
|
162
|
-
|
|
163
|
-
|
|
202
|
+
funnelId: string,
|
|
203
|
+
pagination?: PaginationParams
|
|
164
204
|
): Promise<PaginatedResponse<FunnelResult>> {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
205
|
+
const params = new URLSearchParams();
|
|
206
|
+
if (pagination?.page) params.append('page', String(pagination.page));
|
|
207
|
+
if (pagination?.pageSize) params.append('page_size', String(pagination.pageSize));
|
|
208
|
+
const query = params.toString();
|
|
209
|
+
const endpoint = query ? `${ENDPOINTS.FUNNEL_RESULTS(funnelId)}?${query}` : ENDPOINTS.FUNNEL_RESULTS(funnelId);
|
|
210
|
+
return this.client.get<PaginatedResponse<FunnelResult>>(endpoint);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Preview funnel execution without persisting results.
|
|
215
|
+
*/
|
|
216
|
+
async preview(funnelId: string, stages?: Funnel['stages']): Promise<FunnelPreviewResult> {
|
|
217
|
+
return this.client.post<FunnelPreviewResult>(ENDPOINTS.FUNNEL_PREVIEW(funnelId), { stages });
|
|
178
218
|
}
|
|
179
219
|
|
|
180
220
|
/**
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message Templates API wrapper
|
|
3
|
+
*
|
|
4
|
+
* Provides type-safe access to Django Message Templates API
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ApiClient } from './api-client';
|
|
8
|
+
|
|
9
|
+
export interface MessageTemplate {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
description: string | null;
|
|
13
|
+
subjectTemplate: string;
|
|
14
|
+
bodyTemplate: string;
|
|
15
|
+
channel: string | null;
|
|
16
|
+
entityType: string | null;
|
|
17
|
+
category: string | null;
|
|
18
|
+
isDefault: boolean;
|
|
19
|
+
useCount: number;
|
|
20
|
+
blocks: string | null;
|
|
21
|
+
createdAt: string;
|
|
22
|
+
updatedAt: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface MessageTemplateFilters {
|
|
26
|
+
channel?: string;
|
|
27
|
+
entityType?: string;
|
|
28
|
+
category?: string;
|
|
29
|
+
search?: string;
|
|
30
|
+
page?: number;
|
|
31
|
+
pageSize?: number;
|
|
32
|
+
ordering?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface CreateMessageTemplateInput {
|
|
36
|
+
name: string;
|
|
37
|
+
description?: string;
|
|
38
|
+
subjectTemplate: string;
|
|
39
|
+
bodyTemplate: string;
|
|
40
|
+
channel?: string;
|
|
41
|
+
entityType?: string;
|
|
42
|
+
category?: string;
|
|
43
|
+
blocks?: string | null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class MessageTemplatesApi {
|
|
47
|
+
constructor(private client: ApiClient) {}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* List message templates with optional filters
|
|
51
|
+
*/
|
|
52
|
+
async list(filters?: MessageTemplateFilters) {
|
|
53
|
+
const params = new URLSearchParams();
|
|
54
|
+
|
|
55
|
+
if (filters?.channel) params.append('channel', filters.channel);
|
|
56
|
+
if (filters?.entityType) params.append('entityType', filters.entityType);
|
|
57
|
+
if (filters?.category) params.append('category', filters.category);
|
|
58
|
+
if (filters?.search) params.append('search', filters.search);
|
|
59
|
+
if (filters?.page) params.append('page', String(filters.page));
|
|
60
|
+
if (filters?.pageSize) params.append('pageSize', String(filters.pageSize));
|
|
61
|
+
if (filters?.ordering) params.append('ordering', filters.ordering);
|
|
62
|
+
|
|
63
|
+
return this.client.get<{ results: MessageTemplate[]; count: number; next: string | null; previous: string | null }>(
|
|
64
|
+
`/api/v1/message-templates/?${params.toString()}`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get message template by ID
|
|
70
|
+
*/
|
|
71
|
+
async get(id: string) {
|
|
72
|
+
return this.client.get<MessageTemplate>(`/api/v1/message-templates/${id}/`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Create a new message template
|
|
77
|
+
*/
|
|
78
|
+
async create(data: CreateMessageTemplateInput) {
|
|
79
|
+
return this.client.post<MessageTemplate>('/api/v1/message-templates/', data);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Update message template
|
|
84
|
+
*/
|
|
85
|
+
async update(id: string, data: Partial<CreateMessageTemplateInput>) {
|
|
86
|
+
return this.client.patch<MessageTemplate>(`/api/v1/message-templates/${id}/`, data);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Delete message template
|
|
91
|
+
*/
|
|
92
|
+
async delete(id: string) {
|
|
93
|
+
return this.client.delete(`/api/v1/message-templates/${id}/`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Target Lists API wrapper for /api/v1/targets/lists/
|
|
3
|
+
*
|
|
4
|
+
* Shared across all StartSimpli apps for managing contact/organization lists.
|
|
5
|
+
* All types use camelCase — FetchWrapper auto-converts snake_case↔camelCase.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ApiClient } from './api-client';
|
|
9
|
+
import type { PaginatedResponse } from '../types';
|
|
10
|
+
|
|
11
|
+
// ── Types (camelCase — auto-converted from Django snake_case by FetchWrapper) ──
|
|
12
|
+
|
|
13
|
+
export type TargetType = 'contact' | 'organization';
|
|
14
|
+
export type ListSourceType = 'static' | 'funnel' | 'query';
|
|
15
|
+
export type RefreshStrategy = 'manual' | 'on_funnel_run' | 'scheduled';
|
|
16
|
+
|
|
17
|
+
export interface TargetList {
|
|
18
|
+
id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
description: string;
|
|
21
|
+
targetType: TargetType;
|
|
22
|
+
sourceType: ListSourceType;
|
|
23
|
+
memberCount: number;
|
|
24
|
+
refreshStrategy: RefreshStrategy;
|
|
25
|
+
lastRefreshedAt: string | null;
|
|
26
|
+
createdAt: string;
|
|
27
|
+
updatedAt: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface TargetListMember {
|
|
31
|
+
id: string;
|
|
32
|
+
objectId: string;
|
|
33
|
+
contentType: string;
|
|
34
|
+
addedAt: string;
|
|
35
|
+
metadata: Record<string, unknown>;
|
|
36
|
+
name?: string;
|
|
37
|
+
email?: string;
|
|
38
|
+
firm?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface CreateTargetListInput {
|
|
42
|
+
name: string;
|
|
43
|
+
description?: string;
|
|
44
|
+
targetType?: TargetType;
|
|
45
|
+
sourceType?: ListSourceType;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface UpdateTargetListInput {
|
|
49
|
+
name?: string;
|
|
50
|
+
description?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface TargetListFilters {
|
|
54
|
+
targetType?: TargetType;
|
|
55
|
+
search?: string;
|
|
56
|
+
page?: number;
|
|
57
|
+
pageSize?: number;
|
|
58
|
+
ordering?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface TargetListMemberFilters {
|
|
62
|
+
search?: string;
|
|
63
|
+
page?: number;
|
|
64
|
+
pageSize?: number;
|
|
65
|
+
ordering?: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface AddMembersResult {
|
|
69
|
+
added: number;
|
|
70
|
+
alreadyExists: number;
|
|
71
|
+
total: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface RemoveMembersResult {
|
|
75
|
+
removed: number;
|
|
76
|
+
total: number;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface RefreshResult {
|
|
80
|
+
added: number;
|
|
81
|
+
removed: number;
|
|
82
|
+
total: number;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ── Endpoints ────────────────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
const LISTS_BASE = 'api/v1/targets/lists';
|
|
88
|
+
const LIST = (id: string) => `${LISTS_BASE}/${id}`;
|
|
89
|
+
const LIST_MEMBERS = (id: string) => `${LISTS_BASE}/${id}/members`;
|
|
90
|
+
const LIST_ADD_MEMBERS = (id: string) => `${LISTS_BASE}/${id}/add_members`;
|
|
91
|
+
const LIST_REMOVE_MEMBERS = (id: string) => `${LISTS_BASE}/${id}/remove_members`;
|
|
92
|
+
const LIST_REFRESH = (id: string) => `${LISTS_BASE}/${id}/refresh`;
|
|
93
|
+
|
|
94
|
+
// ── API Class ────────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
export class TargetListsApi {
|
|
97
|
+
constructor(private client: ApiClient) {}
|
|
98
|
+
|
|
99
|
+
/** List all target lists with pagination and filters */
|
|
100
|
+
async list(filters?: TargetListFilters): Promise<PaginatedResponse<TargetList>> {
|
|
101
|
+
// Query params use snake_case (sent directly to Django as URL params)
|
|
102
|
+
const params: Record<string, string> = {};
|
|
103
|
+
if (filters?.targetType) params.target_type = filters.targetType;
|
|
104
|
+
if (filters?.search) params.search = filters.search;
|
|
105
|
+
if (filters?.page) params.page = String(filters.page);
|
|
106
|
+
if (filters?.pageSize) params.page_size = String(filters.pageSize);
|
|
107
|
+
if (filters?.ordering) params.ordering = filters.ordering;
|
|
108
|
+
|
|
109
|
+
const response = await this.client.fetch.get<PaginatedResponse<TargetList> | TargetList[]>(
|
|
110
|
+
LISTS_BASE,
|
|
111
|
+
{ params }
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
if (Array.isArray(response)) {
|
|
115
|
+
return { count: response.length, results: response, next: null, previous: null };
|
|
116
|
+
}
|
|
117
|
+
return response as PaginatedResponse<TargetList>;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Get a single target list by ID */
|
|
121
|
+
async get(id: string): Promise<TargetList> {
|
|
122
|
+
return this.client.fetch.get<TargetList>(LIST(id));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Create a new target list (camelCase body → auto-converted to snake_case by serializeBody) */
|
|
126
|
+
async create(data: CreateTargetListInput): Promise<TargetList> {
|
|
127
|
+
return this.client.fetch.post<TargetList>(LISTS_BASE, {
|
|
128
|
+
name: data.name,
|
|
129
|
+
description: data.description || '',
|
|
130
|
+
targetType: data.targetType || 'contact',
|
|
131
|
+
sourceType: data.sourceType || 'static',
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Update an existing target list */
|
|
136
|
+
async update(id: string, data: UpdateTargetListInput): Promise<TargetList> {
|
|
137
|
+
return this.client.fetch.patch<TargetList>(LIST(id), data);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Delete a target list */
|
|
141
|
+
async delete(id: string): Promise<void> {
|
|
142
|
+
return this.client.fetch.delete(LIST(id));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Get members of a target list with pagination */
|
|
146
|
+
async getMembers(
|
|
147
|
+
listId: string,
|
|
148
|
+
filters?: TargetListMemberFilters
|
|
149
|
+
): Promise<PaginatedResponse<TargetListMember>> {
|
|
150
|
+
const params: Record<string, string> = {};
|
|
151
|
+
if (filters?.search) params.search = filters.search;
|
|
152
|
+
if (filters?.page) params.page = String(filters.page);
|
|
153
|
+
if (filters?.pageSize) params.page_size = String(filters.pageSize);
|
|
154
|
+
if (filters?.ordering) params.ordering = filters.ordering;
|
|
155
|
+
|
|
156
|
+
const response = await this.client.fetch.get<PaginatedResponse<TargetListMember> | TargetListMember[]>(
|
|
157
|
+
LIST_MEMBERS(listId),
|
|
158
|
+
{ params }
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
if (Array.isArray(response)) {
|
|
162
|
+
return { count: response.length, results: response, next: null, previous: null };
|
|
163
|
+
}
|
|
164
|
+
return response as PaginatedResponse<TargetListMember>;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Add members (camelCase body → auto-converted to snake_case) */
|
|
168
|
+
async addMembers(
|
|
169
|
+
listId: string,
|
|
170
|
+
objectIds: string[],
|
|
171
|
+
metadata?: Record<string, unknown>
|
|
172
|
+
): Promise<AddMembersResult> {
|
|
173
|
+
return this.client.fetch.post<AddMembersResult>(LIST_ADD_MEMBERS(listId), {
|
|
174
|
+
objectIds,
|
|
175
|
+
...(metadata ? { metadata } : {}),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Remove members */
|
|
180
|
+
async removeMembers(listId: string, objectIds: string[]): Promise<RemoveMembersResult> {
|
|
181
|
+
return this.client.fetch.post<RemoveMembersResult>(LIST_REMOVE_MEMBERS(listId), {
|
|
182
|
+
objectIds,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Refresh a funnel/query-based list */
|
|
187
|
+
async refresh(listId: string): Promise<RefreshResult> {
|
|
188
|
+
return this.client.fetch.post<RefreshResult>(LIST_REFRESH(listId));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enrichment types — shared across all StartSimpli apps
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface EnrichmentResult {
|
|
6
|
+
contactId: string;
|
|
7
|
+
contactName: string;
|
|
8
|
+
success: boolean;
|
|
9
|
+
fieldsUpdated: string[];
|
|
10
|
+
error?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface QueueStatus {
|
|
14
|
+
pending: number;
|
|
15
|
+
processing: number;
|
|
16
|
+
completed: number;
|
|
17
|
+
failed: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type EnrichmentProvider = 'apollo' | 'hunter';
|
|
21
|
+
|
|
22
|
+
export interface IntegrationStatus {
|
|
23
|
+
name: string;
|
|
24
|
+
provider: EnrichmentProvider;
|
|
25
|
+
connected: boolean;
|
|
26
|
+
description: string;
|
|
27
|
+
lastSync?: string;
|
|
28
|
+
}
|
package/src/types/index.ts
CHANGED
|
@@ -89,6 +89,14 @@ export type {
|
|
|
89
89
|
ChangePasswordResponse,
|
|
90
90
|
} from './user';
|
|
91
91
|
|
|
92
|
+
// Enrichment types
|
|
93
|
+
export type {
|
|
94
|
+
EnrichmentResult,
|
|
95
|
+
QueueStatus,
|
|
96
|
+
EnrichmentProvider,
|
|
97
|
+
IntegrationStatus,
|
|
98
|
+
} from './enrichment';
|
|
99
|
+
|
|
92
100
|
// Error types
|
|
93
101
|
export type {
|
|
94
102
|
FieldError,
|
package/src/types/user.ts
CHANGED
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* User types for /api/v1/users/ endpoints
|
|
3
|
+
* camelCase — FetchWrapper auto-converts snake_case↔camelCase at boundary
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
export interface UserProfile {
|
|
6
7
|
id: string;
|
|
7
8
|
email: string;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
firstName: string;
|
|
10
|
+
lastName: string;
|
|
11
|
+
fullName: string;
|
|
12
|
+
isEmailVerified: boolean;
|
|
12
13
|
company: string | null;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
isActive: boolean;
|
|
15
|
+
createdAt: string;
|
|
16
|
+
updatedAt: string;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export interface UpdateProfileRequest {
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
firstName?: string;
|
|
21
|
+
lastName?: string;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export interface ChangePasswordRequest {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
oldPassword: string;
|
|
26
|
+
newPassword: string;
|
|
27
|
+
newPasswordConfirm: string;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
export interface ChangePasswordResponse {
|