@proteos/sdk 0.49.0 → 0.50.1
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/index.cjs +111 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +327 -2
- package/dist/index.d.ts +327 -2
- package/dist/index.js +111 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/auth/platform-entities.ts +6 -0
- package/src/conversation/index.ts +177 -9
- package/src/conversation/types.ts +290 -0
- package/src/errors.ts +20 -3
- package/src/index.ts +49 -17
- package/src/workflow/types.ts +2 -0
package/package.json
CHANGED
|
@@ -88,6 +88,12 @@ export const PLATFORM_ENTITIES: readonly PlatformEntity[] = [
|
|
|
88
88
|
{ slug: 'contact-groups', name: 'Contact Groups' },
|
|
89
89
|
// Tone-of-voice synthesis: per-user setups + generated instruction profiles.
|
|
90
90
|
{ slug: 'tone-profiles', name: 'Tone Profiles' },
|
|
91
|
+
// Outbound send constraints (windows, connection limits, frequency caps) +
|
|
92
|
+
// their preset catalog.
|
|
93
|
+
{ slug: 'sending-rules', name: 'Sending Rules' },
|
|
94
|
+
// The channel_action ledger: acts through a connection that are neither a
|
|
95
|
+
// message nor a reaction (LinkedIn invitations, profile visits, InMail).
|
|
96
|
+
{ slug: 'channel-actions', name: 'Channel Actions' },
|
|
91
97
|
// Connectors (connector-service). `connections` above is shared; this is the
|
|
92
98
|
// manifest catalog.
|
|
93
99
|
{ slug: 'connectors', name: 'Connectors' },
|
|
@@ -2,14 +2,18 @@ import type { ProteosClient } from '../client.js'
|
|
|
2
2
|
import type {
|
|
3
3
|
AcceptMistranscribedTermRequest,
|
|
4
4
|
AgentListener,
|
|
5
|
+
ApplySendingLimitPresetRequest,
|
|
5
6
|
AttachContactAddressRequest,
|
|
6
7
|
BlockContactRequest,
|
|
8
|
+
CallTokenResponse,
|
|
9
|
+
ChannelAction,
|
|
7
10
|
Connection,
|
|
8
11
|
Contact,
|
|
9
12
|
ContactAddress,
|
|
10
13
|
ContactErasureRequest,
|
|
11
14
|
ContactGroup,
|
|
12
15
|
ContactMergeProposal,
|
|
16
|
+
ContactRecordLink,
|
|
13
17
|
Conversation,
|
|
14
18
|
ConversationFilter,
|
|
15
19
|
ConversationFilterEvent,
|
|
@@ -17,13 +21,12 @@ import type {
|
|
|
17
21
|
CreateAgentListenerRequest,
|
|
18
22
|
CreateConnectionRequest,
|
|
19
23
|
CreateContactGroupRequest,
|
|
20
|
-
CreateContactRequest,
|
|
21
|
-
ContactRecordLink,
|
|
22
24
|
CreateContactRecordLinkRequest,
|
|
23
|
-
|
|
25
|
+
CreateContactRequest,
|
|
24
26
|
CreateConversationFilterRequest,
|
|
25
27
|
CreateConversationTypeRequest,
|
|
26
28
|
CreateGlossaryTermRequest,
|
|
29
|
+
CreateSendingRuleRequest,
|
|
27
30
|
CreateToneProfileSetupRequest,
|
|
28
31
|
CreateTranscriptionRequest,
|
|
29
32
|
DeleteConnectionQuery,
|
|
@@ -32,10 +35,12 @@ import type {
|
|
|
32
35
|
InstallConnectionRequest,
|
|
33
36
|
InstallConnectionResponse,
|
|
34
37
|
ListAgentListenersQuery,
|
|
38
|
+
ListChannelActionsQuery,
|
|
35
39
|
ListConnectionsQuery,
|
|
36
40
|
ListContactAddressesQuery,
|
|
37
41
|
ListContactGroupsQuery,
|
|
38
42
|
ListContactMergeProposalsQuery,
|
|
43
|
+
ListContactRecordLinksQuery,
|
|
39
44
|
ListContactsQuery,
|
|
40
45
|
ListConversationFilterEventsQuery,
|
|
41
46
|
ListConversationFiltersQuery,
|
|
@@ -47,21 +52,27 @@ import type {
|
|
|
47
52
|
ListReactionsResponse,
|
|
48
53
|
ListResponse,
|
|
49
54
|
ListRoomsQuery,
|
|
55
|
+
ListSendingLimitPresetsQuery,
|
|
56
|
+
ListSendingRulesQuery,
|
|
50
57
|
ListToneProfileSetupsQuery,
|
|
51
58
|
ListToneProfilesQuery,
|
|
52
59
|
ListTranscriptionsQuery,
|
|
53
|
-
CallTokenResponse,
|
|
54
60
|
MaterializeTranscriptionRequest,
|
|
55
|
-
MintCallTokenRequest,
|
|
56
|
-
PhoneNumber,
|
|
57
|
-
UpdatePhoneNumberRequest,
|
|
58
61
|
MergeContactsRequest,
|
|
59
62
|
Message,
|
|
63
|
+
MintCallTokenRequest,
|
|
60
64
|
MistranscribedTerm,
|
|
65
|
+
PerformChannelActionRequest,
|
|
66
|
+
PhoneNumber,
|
|
61
67
|
Reaction,
|
|
62
68
|
RecordPermissionEventRequest,
|
|
63
69
|
ResolveToneProfileQuery,
|
|
70
|
+
RespondChannelActionRequest,
|
|
64
71
|
Room,
|
|
72
|
+
SendEligibility,
|
|
73
|
+
SendEligibilityRequest,
|
|
74
|
+
SendingLimitPreset,
|
|
75
|
+
SendingRule,
|
|
65
76
|
SendMessageRequest,
|
|
66
77
|
SyncConnectionRequest,
|
|
67
78
|
ToneProfile,
|
|
@@ -77,6 +88,8 @@ import type {
|
|
|
77
88
|
UpdateConversationTypeRequest,
|
|
78
89
|
UpdateDraftRequest,
|
|
79
90
|
UpdateGlossaryTermRequest,
|
|
91
|
+
UpdatePhoneNumberRequest,
|
|
92
|
+
UpdateSendingRuleRequest,
|
|
80
93
|
UpdateTranscriptionRequest,
|
|
81
94
|
} from './types.js'
|
|
82
95
|
import { type VoiceService, VoiceServiceImpl } from './voice.js'
|
|
@@ -102,6 +115,13 @@ export class ConversationClient {
|
|
|
102
115
|
readonly agentListeners: AgentListenerService
|
|
103
116
|
/** Ingest-time filter rules (drop-with-audit) + their event trail. */
|
|
104
117
|
readonly conversationFilters: ConversationFilterService
|
|
118
|
+
/** Outbound send constraints: windows, connection limits, frequency caps + presets. */
|
|
119
|
+
readonly sendingRules: SendingRuleService
|
|
120
|
+
/**
|
|
121
|
+
* Channel actions: acts performed through a connection that are neither a
|
|
122
|
+
* message nor a reaction — LinkedIn invitations, profile visits, InMail.
|
|
123
|
+
*/
|
|
124
|
+
readonly channelActions: ChannelActionService
|
|
105
125
|
/** Per-org glossary: custom vocabulary that boosts transcription accuracy. */
|
|
106
126
|
readonly glossaryTerms: GlossaryTermService
|
|
107
127
|
/** Conversation taxonomy: the types the pre-summary classifier assigns. */
|
|
@@ -126,6 +146,8 @@ export class ConversationClient {
|
|
|
126
146
|
this.messages = new MessageServiceImpl(client)
|
|
127
147
|
this.agentListeners = new AgentListenerServiceImpl(client)
|
|
128
148
|
this.conversationFilters = new ConversationFilterServiceImpl(client)
|
|
149
|
+
this.sendingRules = new SendingRuleServiceImpl(client)
|
|
150
|
+
this.channelActions = new ChannelActionServiceImpl(client)
|
|
129
151
|
this.glossaryTerms = new GlossaryTermServiceImpl(client)
|
|
130
152
|
this.conversationTypes = new ConversationTypeServiceImpl(client)
|
|
131
153
|
this.contactGroups = new ContactGroupServiceImpl(client)
|
|
@@ -369,7 +391,9 @@ class ContactServiceImpl implements ContactService {
|
|
|
369
391
|
return this.client.request('POST', `${CONVERSATION_BASE_PATH}/contact-record-links`, request)
|
|
370
392
|
}
|
|
371
393
|
|
|
372
|
-
listRecordLinks(
|
|
394
|
+
listRecordLinks(
|
|
395
|
+
query: ListContactRecordLinksQuery = {},
|
|
396
|
+
): Promise<ListResponse<ContactRecordLink>> {
|
|
373
397
|
return this.client.requestWithQuery(
|
|
374
398
|
'GET',
|
|
375
399
|
`${CONVERSATION_BASE_PATH}/contact-record-links`,
|
|
@@ -549,6 +573,12 @@ export interface MessageService {
|
|
|
549
573
|
/** One message with its read-time projections (reactions, attachments). */
|
|
550
574
|
get(messageId: string): Promise<Message>
|
|
551
575
|
send(request: SendMessageRequest): Promise<Message>
|
|
576
|
+
/**
|
|
577
|
+
* Dry-run of the sending gate: same addressing as send, no content, nothing
|
|
578
|
+
* minted. Answers whether the send may go out now and, when held, the
|
|
579
|
+
* earliest instant it may (`earliest_allowed_at`).
|
|
580
|
+
*/
|
|
581
|
+
checkSendEligibility(request: SendEligibilityRequest): Promise<SendEligibility>
|
|
552
582
|
/**
|
|
553
583
|
* Store an outbound message for human review (status=draft) — same request
|
|
554
584
|
* shape as send, nothing reaches the connector until sendDraft. Originate
|
|
@@ -600,6 +630,14 @@ class MessageServiceImpl implements MessageService {
|
|
|
600
630
|
return this.client.request('POST', `${CONVERSATION_BASE_PATH}/messages/send`, request)
|
|
601
631
|
}
|
|
602
632
|
|
|
633
|
+
checkSendEligibility(request: SendEligibilityRequest): Promise<SendEligibility> {
|
|
634
|
+
return this.client.request(
|
|
635
|
+
'POST',
|
|
636
|
+
`${CONVERSATION_BASE_PATH}/messages/send-eligibility`,
|
|
637
|
+
request,
|
|
638
|
+
)
|
|
639
|
+
}
|
|
640
|
+
|
|
603
641
|
draft(request: SendMessageRequest): Promise<Message> {
|
|
604
642
|
return this.client.request('POST', `${CONVERSATION_BASE_PATH}/messages/draft`, request)
|
|
605
643
|
}
|
|
@@ -1136,7 +1174,10 @@ export interface CallService {
|
|
|
1136
1174
|
request: UpdatePhoneNumberRequest,
|
|
1137
1175
|
): Promise<{ data: PhoneNumber[] }>
|
|
1138
1176
|
/** Point one number at the platform. Returns the refreshed list. */
|
|
1139
|
-
connectPhoneNumber(
|
|
1177
|
+
connectPhoneNumber(
|
|
1178
|
+
connectionId: string,
|
|
1179
|
+
numberExternalId: string,
|
|
1180
|
+
): Promise<{ data: PhoneNumber[] }>
|
|
1140
1181
|
/** Release one number (restores its previous handler). Returns the refreshed list. */
|
|
1141
1182
|
disconnectPhoneNumber(
|
|
1142
1183
|
connectionId: string,
|
|
@@ -1199,3 +1240,130 @@ export type {
|
|
|
1199
1240
|
VoiceService,
|
|
1200
1241
|
VoiceTranscriptionStream,
|
|
1201
1242
|
} from './voice.js'
|
|
1243
|
+
|
|
1244
|
+
/**
|
|
1245
|
+
* Sending rules — outbound send constraints (windows, connection limits,
|
|
1246
|
+
* frequency caps), the static limit-preset catalog and its apply expansion.
|
|
1247
|
+
* A denied send/reply/sendDraft fails with a ProteosError whose `details`
|
|
1248
|
+
* carry `earliest_allowed_at` (429) or the blocked contact (403).
|
|
1249
|
+
*/
|
|
1250
|
+
export interface SendingRuleService {
|
|
1251
|
+
list(query?: ListSendingRulesQuery): Promise<ListResponse<SendingRule>>
|
|
1252
|
+
get(id: string): Promise<SendingRule>
|
|
1253
|
+
create(request: CreateSendingRuleRequest): Promise<SendingRule>
|
|
1254
|
+
update(id: string, request: UpdateSendingRuleRequest): Promise<SendingRule>
|
|
1255
|
+
delete(id: string): Promise<void>
|
|
1256
|
+
/** The static preset catalog, optionally narrowed to one connector. */
|
|
1257
|
+
listPresets(query?: ListSendingLimitPresetsQuery): Promise<{ data: SendingLimitPreset[] }>
|
|
1258
|
+
/**
|
|
1259
|
+
* Expand a preset into limit rules linked to the given connections,
|
|
1260
|
+
* replacing the limit rules they were linked to before.
|
|
1261
|
+
*/
|
|
1262
|
+
applyPreset(request: ApplySendingLimitPresetRequest): Promise<{ data: SendingRule[] }>
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
class SendingRuleServiceImpl implements SendingRuleService {
|
|
1266
|
+
constructor(private readonly client: ProteosClient) {}
|
|
1267
|
+
|
|
1268
|
+
list(query: ListSendingRulesQuery = {}): Promise<ListResponse<SendingRule>> {
|
|
1269
|
+
return this.client.requestWithQuery('GET', `${CONVERSATION_BASE_PATH}/sending-rules`, query)
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
get(id: string): Promise<SendingRule> {
|
|
1273
|
+
return this.client.request(
|
|
1274
|
+
'GET',
|
|
1275
|
+
`${CONVERSATION_BASE_PATH}/sending-rules/${encodeURIComponent(id)}`,
|
|
1276
|
+
)
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
create(request: CreateSendingRuleRequest): Promise<SendingRule> {
|
|
1280
|
+
return this.client.request('POST', `${CONVERSATION_BASE_PATH}/sending-rules`, request)
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
update(id: string, request: UpdateSendingRuleRequest): Promise<SendingRule> {
|
|
1284
|
+
return this.client.request(
|
|
1285
|
+
'PATCH',
|
|
1286
|
+
`${CONVERSATION_BASE_PATH}/sending-rules/${encodeURIComponent(id)}`,
|
|
1287
|
+
request,
|
|
1288
|
+
)
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
async delete(id: string): Promise<void> {
|
|
1292
|
+
await this.client.request(
|
|
1293
|
+
'DELETE',
|
|
1294
|
+
`${CONVERSATION_BASE_PATH}/sending-rules/${encodeURIComponent(id)}`,
|
|
1295
|
+
)
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
listPresets(query: ListSendingLimitPresetsQuery = {}): Promise<{ data: SendingLimitPreset[] }> {
|
|
1299
|
+
return this.client.requestWithQuery(
|
|
1300
|
+
'GET',
|
|
1301
|
+
`${CONVERSATION_BASE_PATH}/sending-rules/presets`,
|
|
1302
|
+
query,
|
|
1303
|
+
)
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
applyPreset(request: ApplySendingLimitPresetRequest): Promise<{ data: SendingRule[] }> {
|
|
1307
|
+
return this.client.request(
|
|
1308
|
+
'POST',
|
|
1309
|
+
`${CONVERSATION_BASE_PATH}/sending-rules/apply-preset`,
|
|
1310
|
+
request,
|
|
1311
|
+
)
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
/**
|
|
1316
|
+
* Channel actions — the ledger of acts performed through a connection that
|
|
1317
|
+
* are neither a message nor a reaction (LinkedIn invitations, profile visits,
|
|
1318
|
+
* InMail). A denied perform fails like a denied send (ProteosError with
|
|
1319
|
+
* `details.earliest_allowed_at`); a provider refusal carries its own code
|
|
1320
|
+
* (already_connected, already_invited_recently, invitation_already_received,
|
|
1321
|
+
* connection_limit_reached, insufficient_inmail_credits, inmail_not_allowed,
|
|
1322
|
+
* not_connected_with_recipient). An invitation is pre-flighted with one silent
|
|
1323
|
+
* profile read before anything is sent, so those refusals never mint a
|
|
1324
|
+
* phantom act.
|
|
1325
|
+
*/
|
|
1326
|
+
export interface ChannelActionService {
|
|
1327
|
+
list(query?: ListChannelActionsQuery): Promise<ListResponse<ChannelAction>>
|
|
1328
|
+
get(id: string): Promise<ChannelAction>
|
|
1329
|
+
/** Performs one act; resolves to the row in its post-perform status. */
|
|
1330
|
+
perform(request: PerformChannelActionRequest): Promise<ChannelAction>
|
|
1331
|
+
/** Withdraws a performed outbound act (an invitation). */
|
|
1332
|
+
cancel(id: string): Promise<ChannelAction>
|
|
1333
|
+
/** Answers an inbound act (a received invitation). */
|
|
1334
|
+
respond(id: string, request: RespondChannelActionRequest): Promise<ChannelAction>
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
class ChannelActionServiceImpl implements ChannelActionService {
|
|
1338
|
+
constructor(private readonly client: ProteosClient) {}
|
|
1339
|
+
|
|
1340
|
+
list(query: ListChannelActionsQuery = {}): Promise<ListResponse<ChannelAction>> {
|
|
1341
|
+
return this.client.requestWithQuery('GET', `${CONVERSATION_BASE_PATH}/channel-actions`, query)
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
get(id: string): Promise<ChannelAction> {
|
|
1345
|
+
return this.client.request(
|
|
1346
|
+
'GET',
|
|
1347
|
+
`${CONVERSATION_BASE_PATH}/channel-actions/${encodeURIComponent(id)}`,
|
|
1348
|
+
)
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
perform(request: PerformChannelActionRequest): Promise<ChannelAction> {
|
|
1352
|
+
return this.client.request('POST', `${CONVERSATION_BASE_PATH}/channel-actions`, request)
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
cancel(id: string): Promise<ChannelAction> {
|
|
1356
|
+
return this.client.request(
|
|
1357
|
+
'POST',
|
|
1358
|
+
`${CONVERSATION_BASE_PATH}/channel-actions/${encodeURIComponent(id)}/cancel`,
|
|
1359
|
+
)
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
respond(id: string, request: RespondChannelActionRequest): Promise<ChannelAction> {
|
|
1363
|
+
return this.client.request(
|
|
1364
|
+
'POST',
|
|
1365
|
+
`${CONVERSATION_BASE_PATH}/channel-actions/${encodeURIComponent(id)}/respond`,
|
|
1366
|
+
request,
|
|
1367
|
+
)
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
@@ -290,6 +290,14 @@ export interface Connection {
|
|
|
290
290
|
supports_reactions: boolean
|
|
291
291
|
/** The capability descriptor; absent when unsupported. */
|
|
292
292
|
reactions?: ReactionCapability
|
|
293
|
+
/**
|
|
294
|
+
* Computed on read like reactions: the channel actions the connector
|
|
295
|
+
* performs through this connection (invitation, profile_visit, inmail, …).
|
|
296
|
+
* A separate list from reactions — a reaction toggles an edge on a
|
|
297
|
+
* message, an action is a performed act with its own lifecycle. Absent
|
|
298
|
+
* when the connector performs none.
|
|
299
|
+
*/
|
|
300
|
+
actions?: ChannelActionCapability[]
|
|
293
301
|
/**
|
|
294
302
|
* Computed on read like supports_reactions: who operates the integration
|
|
295
303
|
* (native | unipile). Absent when the connector is not registered in this
|
|
@@ -1220,6 +1228,14 @@ export interface Contact {
|
|
|
1220
1228
|
/** Merge tombstone redirect (set when status is 'merged'). */
|
|
1221
1229
|
merged_into_contact_id?: string
|
|
1222
1230
|
source: ContactSource
|
|
1231
|
+
/** IANA zone name (Europe/Berlin); absent = unknown. Filled from directory sweeps while empty. */
|
|
1232
|
+
timezone?: string
|
|
1233
|
+
/**
|
|
1234
|
+
* BCP-47 language tag with optional region (de, de-CH, pt-BR); absent =
|
|
1235
|
+
* unknown. Named locale, not language: the region carries formatting
|
|
1236
|
+
* conventions on top of the language.
|
|
1237
|
+
*/
|
|
1238
|
+
locale?: string
|
|
1223
1239
|
/** ContactGroup membership (one group per contact); absent = unassigned. */
|
|
1224
1240
|
group_key?: string
|
|
1225
1241
|
/**
|
|
@@ -1347,6 +1363,10 @@ export interface UpdateContactRequest {
|
|
|
1347
1363
|
name?: string
|
|
1348
1364
|
status?: 'active' | 'archived'
|
|
1349
1365
|
has_legal_hold?: boolean
|
|
1366
|
+
/** IANA zone name; normalized server-side, 400 contact_timezone_invalid when unparseable; '' clears. */
|
|
1367
|
+
timezone?: string
|
|
1368
|
+
/** BCP-47 language tag; normalized server-side, 400 contact_locale_invalid when unparseable; '' clears. */
|
|
1369
|
+
locale?: string
|
|
1350
1370
|
/**
|
|
1351
1371
|
* Assigns the contact to a contact group ('' clears). A PATCH assignment is
|
|
1352
1372
|
* stamped group_source='manual' — tone synthesis never overrides it.
|
|
@@ -1362,6 +1382,10 @@ export interface UpdateContactRequest {
|
|
|
1362
1382
|
export interface CreateContactRequest {
|
|
1363
1383
|
name: string
|
|
1364
1384
|
addresses: AttachContactAddressRequest[]
|
|
1385
|
+
/** IANA zone name; optional, validated as on update. */
|
|
1386
|
+
timezone?: string
|
|
1387
|
+
/** BCP-47 language tag; optional, validated as on update. */
|
|
1388
|
+
locale?: string
|
|
1365
1389
|
}
|
|
1366
1390
|
|
|
1367
1391
|
export interface AttachContactAddressRequest {
|
|
@@ -1610,3 +1634,269 @@ export interface DispatchMeetingBotRequest {
|
|
|
1610
1634
|
*/
|
|
1611
1635
|
language?: string
|
|
1612
1636
|
}
|
|
1637
|
+
|
|
1638
|
+
// ── Sending rules ─────────────────────────────────────────────────────────────
|
|
1639
|
+
|
|
1640
|
+
/**
|
|
1641
|
+
* Discriminates a sending rule: window (WHEN sending is allowed, recipient-
|
|
1642
|
+
* local weekday ranges), limit (HOW MUCH one connection may send per rolling
|
|
1643
|
+
* period), frequency_cap (HOW OFTEN one contact may be contacted per rolling
|
|
1644
|
+
* period).
|
|
1645
|
+
*/
|
|
1646
|
+
export type SendingRuleType = 'window' | 'limit' | 'frequency_cap'
|
|
1647
|
+
/** Rolling lookback ("last 24 hours from now") — never a calendar day. */
|
|
1648
|
+
export type SendingPeriod = 'rolling_24h' | 'rolling_7d' | 'rolling_30d'
|
|
1649
|
+
/**
|
|
1650
|
+
* ONE kind of act performed through a channel connection — shared by a
|
|
1651
|
+
* limit's `action` (what it counts), a channel action's `action_type` (what
|
|
1652
|
+
* was performed) and the eligibility check. `message` is the plain send
|
|
1653
|
+
* (valid on a limit, never on a channel action row).
|
|
1654
|
+
*/
|
|
1655
|
+
export type ChannelActionType = 'message' | 'invitation' | 'inmail' | 'profile_visit'
|
|
1656
|
+
export type Weekday =
|
|
1657
|
+
| 'monday'
|
|
1658
|
+
| 'tuesday'
|
|
1659
|
+
| 'wednesday'
|
|
1660
|
+
| 'thursday'
|
|
1661
|
+
| 'friday'
|
|
1662
|
+
| 'saturday'
|
|
1663
|
+
| 'sunday'
|
|
1664
|
+
|
|
1665
|
+
/** One open range on one weekday, "HH:MM" wall-clock, from < until, same day. */
|
|
1666
|
+
export interface WindowDay {
|
|
1667
|
+
day: Weekday
|
|
1668
|
+
from: string
|
|
1669
|
+
until: string
|
|
1670
|
+
}
|
|
1671
|
+
export interface WindowRuleConfig {
|
|
1672
|
+
days: WindowDay[]
|
|
1673
|
+
/** IANA zone used for contacts without a timezone. */
|
|
1674
|
+
fallback_timezone: string
|
|
1675
|
+
}
|
|
1676
|
+
export interface LimitRuleConfig {
|
|
1677
|
+
action: ChannelActionType
|
|
1678
|
+
max_count: number
|
|
1679
|
+
period: SendingPeriod
|
|
1680
|
+
/** Minimum spacing between consecutive sends; 0/absent = none. */
|
|
1681
|
+
min_gap_seconds?: number
|
|
1682
|
+
}
|
|
1683
|
+
export interface FrequencyCapRuleConfig {
|
|
1684
|
+
max_count: number
|
|
1685
|
+
period: SendingPeriod
|
|
1686
|
+
}
|
|
1687
|
+
export type SendingRuleConfig = WindowRuleConfig | LimitRuleConfig | FrequencyCapRuleConfig
|
|
1688
|
+
|
|
1689
|
+
/**
|
|
1690
|
+
* One outbound send constraint, defined once and LINKED to any number of
|
|
1691
|
+
* connections and/or channels (both empty = org-wide). Per rule type the most
|
|
1692
|
+
* specific tier wins at send time: names the connection > names the channel >
|
|
1693
|
+
* org-wide. Replies skip the rule when `is_reply_exempt`.
|
|
1694
|
+
*/
|
|
1695
|
+
export interface SendingRule {
|
|
1696
|
+
id: string
|
|
1697
|
+
org_id: string
|
|
1698
|
+
name: string
|
|
1699
|
+
connection_ids: string[]
|
|
1700
|
+
channels: Channel[]
|
|
1701
|
+
rule_type: SendingRuleType
|
|
1702
|
+
rule_config?: SendingRuleConfig
|
|
1703
|
+
is_enabled: boolean
|
|
1704
|
+
is_reply_exempt: boolean
|
|
1705
|
+
created_at: string
|
|
1706
|
+
created_by: UserRef
|
|
1707
|
+
updated_at: string
|
|
1708
|
+
updated_by: UserRef
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
export interface CreateSendingRuleRequest {
|
|
1712
|
+
name?: string
|
|
1713
|
+
connection_ids?: string[]
|
|
1714
|
+
channels?: Channel[]
|
|
1715
|
+
rule_type: SendingRuleType
|
|
1716
|
+
rule_config: Record<string, unknown>
|
|
1717
|
+
/** Defaults to true. */
|
|
1718
|
+
is_enabled?: boolean
|
|
1719
|
+
/** Defaults per type: window + frequency_cap true, limit false. */
|
|
1720
|
+
is_reply_exempt?: boolean
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
export interface UpdateSendingRuleRequest {
|
|
1724
|
+
name?: string
|
|
1725
|
+
/** Replaces the stored links wholesale when present. */
|
|
1726
|
+
connection_ids?: string[]
|
|
1727
|
+
channels?: Channel[]
|
|
1728
|
+
/** rule_type and rule_config must be sent together. */
|
|
1729
|
+
rule_type?: SendingRuleType
|
|
1730
|
+
rule_config?: Record<string, unknown>
|
|
1731
|
+
is_enabled?: boolean
|
|
1732
|
+
is_reply_exempt?: boolean
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
export interface ListSendingRulesQuery extends PaginationQuery {
|
|
1736
|
+
/** Rules LINKED to this connection. */
|
|
1737
|
+
connection_id?: string
|
|
1738
|
+
channel?: Channel
|
|
1739
|
+
rule_type?: SendingRuleType
|
|
1740
|
+
is_enabled?: boolean
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
/** A recommended limit bundle for one class of sender account (static catalog). */
|
|
1744
|
+
export interface SendingLimitPreset {
|
|
1745
|
+
key: string
|
|
1746
|
+
name: string
|
|
1747
|
+
description: string
|
|
1748
|
+
connector_keys: ConnectorKey[]
|
|
1749
|
+
is_recommended: boolean
|
|
1750
|
+
rules: { rule_type: 'limit'; rule_config: LimitRuleConfig }[]
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
export interface ListSendingLimitPresetsQuery {
|
|
1754
|
+
connector_key?: ConnectorKey
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
export interface ApplySendingLimitPresetRequest {
|
|
1758
|
+
connection_ids: string[]
|
|
1759
|
+
preset_key: string
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
/** Dry-run twin of SendMessageRequest: addressing only, nothing minted. */
|
|
1763
|
+
export interface SendEligibilityRequest {
|
|
1764
|
+
conversation_id?: string
|
|
1765
|
+
reply_to_message_id?: string
|
|
1766
|
+
connection_id?: string
|
|
1767
|
+
to?: SendRecipient[]
|
|
1768
|
+
cc?: SendRecipient[]
|
|
1769
|
+
bcc?: SendRecipient[]
|
|
1770
|
+
/**
|
|
1771
|
+
* Widens the check to a channel action (invitation, profile_visit, inmail):
|
|
1772
|
+
* originate mode only, the first `to` recipient is the target. Absent =
|
|
1773
|
+
* message.
|
|
1774
|
+
*/
|
|
1775
|
+
action_type?: ChannelActionType
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
/**
|
|
1779
|
+
* "May this send go out now?" — `reason` is the error code a real send would
|
|
1780
|
+
* fail with (sending_window_closed | sending_limit_reached |
|
|
1781
|
+
* frequency_cap_reached | contact_blocked | contact_opted_out);
|
|
1782
|
+
* `earliest_allowed_at` is set for the temporal three.
|
|
1783
|
+
*/
|
|
1784
|
+
export interface SendEligibility {
|
|
1785
|
+
is_allowed: boolean
|
|
1786
|
+
reason?: string
|
|
1787
|
+
rule_id?: string
|
|
1788
|
+
rule_type?: SendingRuleType
|
|
1789
|
+
earliest_allowed_at?: string
|
|
1790
|
+
contact_id?: string
|
|
1791
|
+
contact_address_id?: string
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
// ── Channel actions ───────────────────────────────────────────────────────────
|
|
1795
|
+
|
|
1796
|
+
/**
|
|
1797
|
+
* Lifecycle of a channel action. Execution: pending → performed | failed.
|
|
1798
|
+
* Outcome (invitations): performed → accepted | declined | withdrawn |
|
|
1799
|
+
* expired; an inbound received invitation starts pending and ends
|
|
1800
|
+
* accepted | declined | expired.
|
|
1801
|
+
*/
|
|
1802
|
+
export type ChannelActionStatus =
|
|
1803
|
+
| 'pending'
|
|
1804
|
+
| 'performed'
|
|
1805
|
+
| 'failed'
|
|
1806
|
+
| 'accepted'
|
|
1807
|
+
| 'declined'
|
|
1808
|
+
| 'withdrawn'
|
|
1809
|
+
| 'expired'
|
|
1810
|
+
/** Our answer to an inbound channel action (a received invitation). */
|
|
1811
|
+
export type ChannelActionResponse = 'accept' | 'decline'
|
|
1812
|
+
/** What an action type acts on: a person on the channel, or an external object. */
|
|
1813
|
+
export type ChannelActionTargetKind = 'contact-address' | 'external'
|
|
1814
|
+
|
|
1815
|
+
/**
|
|
1816
|
+
* One action type a connector performs, projected onto `connection.actions`.
|
|
1817
|
+
*/
|
|
1818
|
+
export interface ChannelActionCapability {
|
|
1819
|
+
action_type: ChannelActionType
|
|
1820
|
+
target_kind: ChannelActionTargetKind
|
|
1821
|
+
/** The act can be withdrawn after performing (an invitation). */
|
|
1822
|
+
is_cancelable: boolean
|
|
1823
|
+
/** An inbound act of this type can be answered (accept / decline). */
|
|
1824
|
+
is_respondable: boolean
|
|
1825
|
+
/** Performing also sends a message that lands as a Message + Conversation (InMail). */
|
|
1826
|
+
is_message_minting: boolean
|
|
1827
|
+
/** Bound of the free-text note the act carries (LinkedIn invitation: 300). */
|
|
1828
|
+
max_note_length?: number
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
export interface InvitationParams {
|
|
1832
|
+
note?: string
|
|
1833
|
+
email?: string
|
|
1834
|
+
}
|
|
1835
|
+
export type ProfileVisitParams = Record<string, never>
|
|
1836
|
+
export interface InmailParams {
|
|
1837
|
+
subject?: string
|
|
1838
|
+
content: ContentBlock[]
|
|
1839
|
+
}
|
|
1840
|
+
export type ChannelActionParams = InvitationParams | ProfileVisitParams | InmailParams
|
|
1841
|
+
|
|
1842
|
+
/**
|
|
1843
|
+
* One act performed through a channel connection that is neither a message
|
|
1844
|
+
* nor a reaction — a LinkedIn invitation, a profile visit, an InMail (which
|
|
1845
|
+
* ALSO mints a message, see message_id). A LEDGER row: appended, transitioned
|
|
1846
|
+
* along its lifecycle, never toggled. `direction` outbound = we acted;
|
|
1847
|
+
* inbound = someone acted on us (a received invitation you may answer).
|
|
1848
|
+
* `created_by` is the performer on outbound rows (user or agent), the system
|
|
1849
|
+
* on ingested inbound rows; `updated_by` who accepted / declined / withdrew.
|
|
1850
|
+
*/
|
|
1851
|
+
export interface ChannelAction {
|
|
1852
|
+
id: string
|
|
1853
|
+
org_id: string
|
|
1854
|
+
connection_id: string
|
|
1855
|
+
connector_key: ConnectorKey
|
|
1856
|
+
channel: Channel
|
|
1857
|
+
action_type: ChannelActionType
|
|
1858
|
+
direction: MessageDirection
|
|
1859
|
+
status: ChannelActionStatus
|
|
1860
|
+
contact_id?: string
|
|
1861
|
+
contact_address_id?: string
|
|
1862
|
+
/** The provider-side identity acted on (a LinkedIn member id). */
|
|
1863
|
+
target_external_id: string
|
|
1864
|
+
contact: ContactRef
|
|
1865
|
+
/** An InMail's minted message; an invitation's note-chat once accepted. */
|
|
1866
|
+
message_id?: string
|
|
1867
|
+
conversation_id?: string
|
|
1868
|
+
params?: ChannelActionParams
|
|
1869
|
+
/** The provider handle (Unipile invitation id) — cancel / respond key. */
|
|
1870
|
+
external_action_id?: string
|
|
1871
|
+
error?: string
|
|
1872
|
+
occurred_at: string
|
|
1873
|
+
resolved_at?: string
|
|
1874
|
+
/** Provider enrichment: invitation usage %, network_distance, … */
|
|
1875
|
+
metadata: Record<string, unknown>
|
|
1876
|
+
created_at: string
|
|
1877
|
+
created_by: UserRef
|
|
1878
|
+
updated_at: string
|
|
1879
|
+
updated_by: UserRef
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
/** Performs one act. `target` is the person acted on (kind contact-address + the connector-side external id). */
|
|
1883
|
+
export interface PerformChannelActionRequest {
|
|
1884
|
+
connection_id: string
|
|
1885
|
+
action_type: ChannelActionType
|
|
1886
|
+
target: SendRecipient
|
|
1887
|
+
/** Per-type input: invitation {note?, email?}; profile_visit {}; inmail {subject?, content}. */
|
|
1888
|
+
params?: Record<string, unknown>
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
export interface RespondChannelActionRequest {
|
|
1892
|
+
response: ChannelActionResponse
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
export interface ListChannelActionsQuery extends PaginationQuery {
|
|
1896
|
+
channel?: Channel
|
|
1897
|
+
connection_id?: string
|
|
1898
|
+
action_type?: ChannelActionType
|
|
1899
|
+
direction?: MessageDirection
|
|
1900
|
+
status?: ChannelActionStatus
|
|
1901
|
+
contact_id?: string
|
|
1902
|
+
}
|
package/src/errors.ts
CHANGED
|
@@ -23,12 +23,24 @@ export class ProteosError extends Error {
|
|
|
23
23
|
readonly httpStatus: number
|
|
24
24
|
/** API error code (e.g., 'not_found', 'unauthorized') */
|
|
25
25
|
readonly code: ErrorCodeType | string
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Optional machine-readable payload beside the message — e.g. a denied send's
|
|
28
|
+
* `earliest_allowed_at` / `rule_id`, the offending `contact_id`. Absent on
|
|
29
|
+
* most errors.
|
|
30
|
+
*/
|
|
31
|
+
readonly details: Record<string, unknown> | undefined
|
|
32
|
+
|
|
33
|
+
constructor(
|
|
34
|
+
message: string,
|
|
35
|
+
httpStatus: number,
|
|
36
|
+
code: ErrorCodeType | string,
|
|
37
|
+
details?: Record<string, unknown>,
|
|
38
|
+
) {
|
|
28
39
|
super(message)
|
|
29
40
|
this.name = 'ProteosError'
|
|
30
41
|
this.httpStatus = httpStatus
|
|
31
42
|
this.code = code
|
|
43
|
+
this.details = details
|
|
32
44
|
|
|
33
45
|
// V8-specific API for cleaner stack traces; not present in all runtimes
|
|
34
46
|
const v8Capture = (Error as unknown as { captureStackTrace?: (t: object, c: unknown) => void })
|
|
@@ -112,6 +124,7 @@ export function getDefaultErrorCode(httpStatus: number): ErrorCodeType | string
|
|
|
112
124
|
export interface ApiErrorResponse {
|
|
113
125
|
code?: string
|
|
114
126
|
message?: string
|
|
127
|
+
details?: Record<string, unknown>
|
|
115
128
|
}
|
|
116
129
|
|
|
117
130
|
/**
|
|
@@ -121,6 +134,7 @@ export async function parseErrorResponse(response: Response): Promise<ProteosErr
|
|
|
121
134
|
const httpStatus = response.status
|
|
122
135
|
let code: string = getDefaultErrorCode(httpStatus)
|
|
123
136
|
let message = 'Unknown error'
|
|
137
|
+
let details: Record<string, unknown> | undefined
|
|
124
138
|
|
|
125
139
|
try {
|
|
126
140
|
const body = await response.text()
|
|
@@ -136,6 +150,9 @@ export async function parseErrorResponse(response: Response): Promise<ProteosErr
|
|
|
136
150
|
} else {
|
|
137
151
|
message = body || `HTTP ${httpStatus}`
|
|
138
152
|
}
|
|
153
|
+
if (json.details && typeof json.details === 'object') {
|
|
154
|
+
details = json.details
|
|
155
|
+
}
|
|
139
156
|
} catch {
|
|
140
157
|
// Not JSON, use body as message
|
|
141
158
|
message = body.trim() || `HTTP ${httpStatus}`
|
|
@@ -144,5 +161,5 @@ export async function parseErrorResponse(response: Response): Promise<ProteosErr
|
|
|
144
161
|
message = `HTTP ${httpStatus}`
|
|
145
162
|
}
|
|
146
163
|
|
|
147
|
-
return new ProteosError(message, httpStatus, code)
|
|
164
|
+
return new ProteosError(message, httpStatus, code, details)
|
|
148
165
|
}
|