@planescrm/sdk 0.2.0 → 0.4.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/index.d.mts +685 -106
- package/dist/index.d.ts +685 -106
- package/dist/index.js +580 -190
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +567 -187
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,15 +1,76 @@
|
|
|
1
1
|
import * as http from 'node:http';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Tokens managed by an OAuth-credential client, persisted via {@link TokenStorage}.
|
|
5
5
|
*/
|
|
6
|
-
interface
|
|
6
|
+
interface StoredTokens {
|
|
7
|
+
/** Current OAuth access token. */
|
|
8
|
+
accessToken: string;
|
|
9
|
+
/**
|
|
10
|
+
* Refresh token used to obtain a new access token. `null` for tokens issued
|
|
11
|
+
* via the client_credentials grant, which have no refresh token — the
|
|
12
|
+
* client re-authenticates with clientId/clientSecret instead.
|
|
13
|
+
*/
|
|
14
|
+
refreshToken: string | null;
|
|
15
|
+
/** Access-token expiry as epoch milliseconds. */
|
|
16
|
+
expiresAt: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Pluggable persistence for OAuth-managed tokens. Implement this to back the
|
|
20
|
+
* client with your own store (encrypted disk, database, secrets manager, ...).
|
|
21
|
+
* The default is {@link MemoryTokenStorage}.
|
|
22
|
+
*/
|
|
23
|
+
interface TokenStorage {
|
|
24
|
+
/** Load the currently stored tokens, or `null` if none are stored. */
|
|
25
|
+
getTokens(): Promise<StoredTokens | null>;
|
|
26
|
+
/** Persist the given tokens (called after every refresh). */
|
|
27
|
+
setTokens(tokens: StoredTokens): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Default in-memory {@link TokenStorage}. Tokens live only for the process
|
|
31
|
+
* lifetime — provide your own implementation to persist across restarts.
|
|
32
|
+
*/
|
|
33
|
+
declare class MemoryTokenStorage implements TokenStorage {
|
|
34
|
+
private tokens;
|
|
35
|
+
constructor(seed?: StoredTokens | null);
|
|
36
|
+
getTokens(): Promise<StoredTokens | null>;
|
|
37
|
+
setTokens(tokens: StoredTokens): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Configuration for the PlanesClient.
|
|
41
|
+
*
|
|
42
|
+
* Exactly one authentication mode must be supplied:
|
|
43
|
+
* - `accessToken` — a pre-obtained OAuth access token (not auto-refreshed)
|
|
44
|
+
* - `apiKey` — a `pk_live_` API key
|
|
45
|
+
* - `clientId` + `clientSecret` — OAuth-managed: the client obtains/refreshes
|
|
46
|
+
* access tokens automatically using `tokenStorage`
|
|
47
|
+
*/
|
|
48
|
+
interface PlanesConfig {
|
|
7
49
|
/** API base URL (defaults to https://api.planescrm.com/api/v1) */
|
|
8
50
|
baseUrl?: string;
|
|
9
|
-
/** OAuth access token (mutually exclusive with apiKey) */
|
|
51
|
+
/** OAuth access token (mutually exclusive with apiKey / clientId+clientSecret) */
|
|
10
52
|
accessToken?: string;
|
|
11
|
-
/** API key (mutually exclusive with accessToken) */
|
|
53
|
+
/** API key (mutually exclusive with accessToken / clientId+clientSecret) */
|
|
12
54
|
apiKey?: string;
|
|
55
|
+
/** OAuth client ID — combined with `clientSecret`, enables the OAuth-managed path */
|
|
56
|
+
clientId?: string;
|
|
57
|
+
/** OAuth client secret — combined with `clientId`, enables the OAuth-managed path */
|
|
58
|
+
clientSecret?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Scopes to request when the OAuth-managed path falls back to the
|
|
61
|
+
* client_credentials grant (no stored refresh token).
|
|
62
|
+
*/
|
|
63
|
+
scopes?: string[];
|
|
64
|
+
/**
|
|
65
|
+
* Token store for the OAuth-managed path. Seed it with tokens obtained from
|
|
66
|
+
* an authorization-code exchange. Defaults to {@link MemoryTokenStorage}.
|
|
67
|
+
*/
|
|
68
|
+
tokenStorage?: TokenStorage;
|
|
69
|
+
/**
|
|
70
|
+
* Refresh access tokens proactively when they are within this many
|
|
71
|
+
* milliseconds of expiry (OAuth-managed path only). Default 60000 (60s).
|
|
72
|
+
*/
|
|
73
|
+
tokenRefreshSkewMs?: number;
|
|
13
74
|
/** Request timeout in milliseconds (default 30000) */
|
|
14
75
|
timeout?: number;
|
|
15
76
|
/** Automatically retry on 429 rate limit responses (default true) */
|
|
@@ -20,6 +81,7 @@ interface BlueprintConfig {
|
|
|
20
81
|
declare const DEFAULT_BASE_URL = "https://api.planescrm.com/api/v1";
|
|
21
82
|
declare const DEFAULT_TIMEOUT = 30000;
|
|
22
83
|
declare const DEFAULT_MAX_RETRIES = 3;
|
|
84
|
+
declare const DEFAULT_TOKEN_REFRESH_SKEW_MS = 60000;
|
|
23
85
|
|
|
24
86
|
interface HttpRequestOptions {
|
|
25
87
|
method: string;
|
|
@@ -39,9 +101,25 @@ declare class HttpClient {
|
|
|
39
101
|
private readonly timeout;
|
|
40
102
|
private readonly retryOnRateLimit;
|
|
41
103
|
private readonly maxRetries;
|
|
42
|
-
|
|
104
|
+
private readonly oauth;
|
|
105
|
+
/** Single-flight guard so concurrent requests share one refresh. */
|
|
106
|
+
private refreshInFlight;
|
|
107
|
+
constructor(config: PlanesConfig);
|
|
43
108
|
request<T>(options: HttpRequestOptions): Promise<T>;
|
|
44
109
|
getAuthHeaders(): Record<string, string>;
|
|
110
|
+
/**
|
|
111
|
+
* Resolve the Authorization header for the current request. For the static
|
|
112
|
+
* accessToken/apiKey modes this is {@link getAuthHeaders}. For the
|
|
113
|
+
* OAuth-managed path it reads the stored token, proactively refreshing when
|
|
114
|
+
* the token is within the skew window of expiry.
|
|
115
|
+
*/
|
|
116
|
+
private resolveAuthHeaders;
|
|
117
|
+
/**
|
|
118
|
+
* Refresh the OAuth access token via `refreshAccessToken` and persist the new
|
|
119
|
+
* tokens through the configured {@link TokenStorage}. Concurrent callers share
|
|
120
|
+
* a single in-flight refresh.
|
|
121
|
+
*/
|
|
122
|
+
private refreshTokens;
|
|
45
123
|
private buildUrl;
|
|
46
124
|
private parseRetryAfter;
|
|
47
125
|
private mapError;
|
|
@@ -151,15 +229,397 @@ declare class InventoryResource extends BaseResource {
|
|
|
151
229
|
}
|
|
152
230
|
|
|
153
231
|
/**
|
|
154
|
-
*
|
|
232
|
+
* Wire shapes for the Contacts + Contact Channels API.
|
|
233
|
+
*
|
|
234
|
+
* Mirrors `createContactSchema`/`updateContactSchema` and
|
|
235
|
+
* `createContactChannelSchema`/`updateContactChannelSchema` in
|
|
236
|
+
* `packages/shared/src/validation/contact.ts`, plus the actual Drizzle-row
|
|
237
|
+
* response shapes returned by `ContactsService` / `ContactChannelsService`.
|
|
238
|
+
* Types are redefined here (not imported) so the published package carries no
|
|
239
|
+
* runtime or type dependency on `@blueprint/shared`. Internal-only columns
|
|
240
|
+
* (tenantId, deletedAt, createdBy, …) are intentionally omitted — see
|
|
241
|
+
* `CustomerDto` for the house style.
|
|
242
|
+
*/
|
|
243
|
+
type ContactSource = 'manual' | 'import' | 'convert_from_customer';
|
|
244
|
+
interface ContactDto {
|
|
245
|
+
id: string;
|
|
246
|
+
firstName: string;
|
|
247
|
+
lastName: string | null;
|
|
248
|
+
notes: string | null;
|
|
249
|
+
customFields: Record<string, unknown> | null;
|
|
250
|
+
source: ContactSource | null;
|
|
251
|
+
createdAt: string;
|
|
252
|
+
updatedAt: string;
|
|
253
|
+
}
|
|
254
|
+
interface CreateContactDto {
|
|
255
|
+
firstName: string;
|
|
256
|
+
lastName?: string | null;
|
|
257
|
+
notes?: string | null;
|
|
258
|
+
customFields?: Record<string, unknown> | null;
|
|
259
|
+
source?: ContactSource | null;
|
|
260
|
+
}
|
|
261
|
+
interface UpdateContactDto {
|
|
262
|
+
firstName?: string;
|
|
263
|
+
lastName?: string | null;
|
|
264
|
+
notes?: string | null;
|
|
265
|
+
customFields?: Record<string, unknown> | null;
|
|
266
|
+
source?: ContactSource | null;
|
|
267
|
+
}
|
|
268
|
+
type ContactChannelType = 'email' | 'phone' | 'url';
|
|
269
|
+
type ContactChannelPhoneType = 'mobile' | 'landline' | 'voip' | 'unknown';
|
|
270
|
+
interface ContactChannelDto {
|
|
271
|
+
id: string;
|
|
272
|
+
contactId: string;
|
|
273
|
+
channelType: ContactChannelType;
|
|
274
|
+
label: string | null;
|
|
275
|
+
value: string;
|
|
276
|
+
isPrimary: boolean;
|
|
277
|
+
phoneType: ContactChannelPhoneType | null;
|
|
278
|
+
smsCapable: boolean | null;
|
|
279
|
+
dnc: boolean | null;
|
|
280
|
+
marketingOptOut: boolean | null;
|
|
281
|
+
createdAt: string;
|
|
282
|
+
updatedAt: string;
|
|
283
|
+
}
|
|
284
|
+
interface CreateContactChannelDto {
|
|
285
|
+
channelType: ContactChannelType;
|
|
286
|
+
label?: string | null;
|
|
287
|
+
value: string;
|
|
288
|
+
isPrimary?: boolean;
|
|
289
|
+
dnc?: boolean | null;
|
|
290
|
+
marketingOptOut?: boolean | null;
|
|
291
|
+
}
|
|
292
|
+
interface UpdateContactChannelDto {
|
|
293
|
+
label?: string | null;
|
|
294
|
+
value?: string;
|
|
295
|
+
isPrimary?: boolean;
|
|
296
|
+
dnc?: boolean | null;
|
|
297
|
+
marketingOptOut?: boolean | null;
|
|
298
|
+
}
|
|
299
|
+
/** A channel elsewhere in the tenant with the same normalized value. */
|
|
300
|
+
interface ContactChannelDuplicateMatch {
|
|
301
|
+
contactId: string;
|
|
302
|
+
channelId: string;
|
|
303
|
+
value: string;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* `POST /contacts/:id/channels` return shape — `ContactChannelsService.addChannel()`
|
|
307
|
+
* always returns this wrapper, never a flat channel. `duplicateOf` is the
|
|
308
|
+
* dedup signal (empty array when there's no match) — the point of this API
|
|
309
|
+
* for connectors doing identity resolution (e.g. Messenger).
|
|
310
|
+
*/
|
|
311
|
+
interface AddContactChannelResult {
|
|
312
|
+
channel: ContactChannelDto;
|
|
313
|
+
duplicateOf: ContactChannelDuplicateMatch[];
|
|
314
|
+
}
|
|
315
|
+
interface ContactMergeResult {
|
|
316
|
+
winnerId: string;
|
|
317
|
+
loserId: string;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* `/contacts/:id/channels` — POST/PATCH/DELETE only. There is no list/get
|
|
322
|
+
* route: reads come embedded in the contact detail response (`GET
|
|
323
|
+
* /contacts/:id` returns `{ ...contact, channels, links, mergedInto }`), so
|
|
324
|
+
* this does NOT extend `BaseResource`.
|
|
325
|
+
*/
|
|
326
|
+
declare class ContactChannelResource {
|
|
327
|
+
private readonly http;
|
|
328
|
+
private readonly basePath;
|
|
329
|
+
constructor(http: HttpClient, contactId: string);
|
|
330
|
+
/** Returns `{ channel, duplicateOf }` — `duplicateOf` is the dedup signal. */
|
|
331
|
+
create(data: CreateContactChannelDto): Promise<AddContactChannelResult>;
|
|
332
|
+
update(channelId: string, data: UpdateContactChannelDto): Promise<ContactChannelDto>;
|
|
333
|
+
delete(channelId: string): Promise<void>;
|
|
334
|
+
}
|
|
335
|
+
declare class ContactResource extends BaseResource<ContactDto, CreateContactDto, UpdateContactDto> {
|
|
336
|
+
constructor(http: ConstructorParameters<typeof BaseResource>[0]);
|
|
337
|
+
merge(winnerId: string, loserId: string): Promise<ContactMergeResult>;
|
|
338
|
+
channels(contactId: string): ContactChannelResource;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Wire shapes for the generic activity/timeline API (E31-F1).
|
|
343
|
+
*
|
|
344
|
+
* Mirrors `recordActivitySchema` (write) and `ActivityRecord` (read) in
|
|
345
|
+
* `packages/shared/src/validation/activity.ts` — both snake_case on the wire.
|
|
346
|
+
* Types are redefined here (not imported) so the published package carries no
|
|
347
|
+
* runtime or type dependency on `@blueprint/shared`.
|
|
348
|
+
*/
|
|
349
|
+
interface CreateActivityDto {
|
|
350
|
+
entity_type: string;
|
|
351
|
+
entity_id?: string | null;
|
|
352
|
+
/** The contact a channel-targeted send was addressed to, if any. */
|
|
353
|
+
contact_id?: string | null;
|
|
354
|
+
source: string;
|
|
355
|
+
type: string;
|
|
356
|
+
occurred_at?: string;
|
|
357
|
+
actor?: string | null;
|
|
358
|
+
/** Required — a short human-readable description of the event. */
|
|
359
|
+
summary: string;
|
|
360
|
+
external_ref?: string | null;
|
|
361
|
+
match_key?: string | null;
|
|
362
|
+
payload?: Record<string, unknown>;
|
|
363
|
+
payload_version?: number;
|
|
364
|
+
}
|
|
365
|
+
interface ActivityDto {
|
|
366
|
+
id: string;
|
|
367
|
+
type: string;
|
|
368
|
+
source: string;
|
|
369
|
+
occurred_at: string;
|
|
370
|
+
actor: string | null;
|
|
371
|
+
summary: string;
|
|
372
|
+
entity_type: string;
|
|
373
|
+
entity_id: string | null;
|
|
374
|
+
contact_id: string | null;
|
|
375
|
+
payload: Record<string, unknown>;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
declare class ActivityResource {
|
|
379
|
+
private readonly http;
|
|
380
|
+
constructor(http: HttpClient);
|
|
381
|
+
create(data: CreateActivityDto): Promise<ActivityDto>;
|
|
382
|
+
list(params?: ListParams): Promise<PaginatedResponse<ActivityDto>>;
|
|
383
|
+
listForEntity(entityType: string, entityId: string, params?: ListParams): Promise<PaginatedResponse<ActivityDto>>;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Wire shapes for the Tags & entity-tagging API.
|
|
388
|
+
*
|
|
389
|
+
* Mirrors `createTagSchema`/`updateTagSchema`/`setEntityTagsSchema` in
|
|
390
|
+
* `packages/shared/src/validation/tags.ts`, plus `TagsService`'s actual
|
|
391
|
+
* response shapes — snake_case, per `toDto()`/`list()`. Types are redefined
|
|
392
|
+
* here (not imported) so the published package carries no runtime or type
|
|
393
|
+
* dependency on `@blueprint/shared`.
|
|
394
|
+
*/
|
|
395
|
+
interface TagDto {
|
|
396
|
+
id: string;
|
|
397
|
+
name: string;
|
|
398
|
+
color: string | null;
|
|
399
|
+
created_at: string;
|
|
400
|
+
updated_at: string;
|
|
401
|
+
/** Only present on `list()` — `create()`/`update()` omit it. */
|
|
402
|
+
usage_count?: number;
|
|
403
|
+
}
|
|
404
|
+
interface CreateTagDto {
|
|
405
|
+
name: string;
|
|
406
|
+
color?: string | null;
|
|
407
|
+
}
|
|
408
|
+
interface UpdateTagDto {
|
|
409
|
+
name?: string;
|
|
410
|
+
color?: string | null;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Declarative set-tags input. Provide `tag_ids` and/or `tag_names` — at least
|
|
414
|
+
* one must be present (the server rejects neither); an empty array clears
|
|
415
|
+
* that side of the set.
|
|
416
|
+
*/
|
|
417
|
+
interface SetEntityTagsDto {
|
|
418
|
+
tag_ids?: string[];
|
|
419
|
+
tag_names?: string[];
|
|
420
|
+
}
|
|
421
|
+
interface EntityTagDto {
|
|
422
|
+
id: string;
|
|
423
|
+
name: string;
|
|
424
|
+
color: string | null;
|
|
425
|
+
}
|
|
426
|
+
interface TagEntityDto {
|
|
427
|
+
entity_type: string;
|
|
428
|
+
entity_id: string;
|
|
429
|
+
}
|
|
430
|
+
interface TagEntitiesPage {
|
|
431
|
+
data: TagEntityDto[];
|
|
432
|
+
page: number;
|
|
433
|
+
limit: number;
|
|
434
|
+
total: number;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
declare class TagResource extends BaseResource<TagDto, CreateTagDto, UpdateTagDto> {
|
|
438
|
+
constructor(http: ConstructorParameters<typeof BaseResource>[0]);
|
|
439
|
+
/** `TagsService.setEntityTags()` returns the final tag set wrapped in `{ tags }`. */
|
|
440
|
+
setEntityTags(entityType: string, entityId: string, data: SetEntityTagsDto): Promise<{
|
|
441
|
+
tags: TagDto[];
|
|
442
|
+
}>;
|
|
443
|
+
getEntityTags(entityType: string, entityId: string): Promise<EntityTagDto[]>;
|
|
444
|
+
getEntities(tagId: string, params?: Record<string, unknown>): Promise<TagEntitiesPage>;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* The kinds of change a webhook event can describe.
|
|
449
|
+
*
|
|
450
|
+
* Mirrored (self-contained, NOT imported) from
|
|
451
|
+
* `packages/shared/src/types/entity-events.ts` — the SDK must not depend on
|
|
452
|
+
* `@blueprint/shared`. Keep this union byte-identical to that source of truth.
|
|
453
|
+
*/
|
|
454
|
+
type EntityEventType = 'created' | 'updated' | 'deleted' | 'status_changed' | 'entity.tagged' | 'entity.untagged' | 'list.member_added' | 'list.member_removed' | 'entity.assigned' | 'entity.unassigned' | 'team.member_added' | 'team.member_removed' | 'task.created' | 'task.updated' | 'task.completed' | 'task.cancelled' | 'comment.created';
|
|
455
|
+
/**
|
|
456
|
+
* The entity kinds a webhook event can target.
|
|
457
|
+
*
|
|
458
|
+
* Mirrored (self-contained, NOT imported) from
|
|
459
|
+
* `packages/shared/src/types/entity-events.ts`. Keep byte-identical.
|
|
460
|
+
*/
|
|
461
|
+
type EntityType = 'customer' | 'opportunity' | 'job' | 'estimate' | 'contract' | 'property' | 'inventory_item' | 'agent_execution' | 'calendar_event' | 'invoice' | 'payment';
|
|
462
|
+
/**
|
|
463
|
+
* A single entity-change event as emitted onto the Planes event bus and
|
|
464
|
+
* delivered inside a {@link WebhookDelivery}.
|
|
465
|
+
*
|
|
466
|
+
* Mirrored from `EntityEvent` in
|
|
467
|
+
* `packages/shared/src/types/entity-events.ts`.
|
|
468
|
+
*/
|
|
469
|
+
interface EntityEvent {
|
|
470
|
+
/** What happened (e.g. 'created', 'status_changed'). */
|
|
471
|
+
event_type: EntityEventType;
|
|
472
|
+
/** The kind of record this event is about. */
|
|
473
|
+
entity_type: EntityType;
|
|
474
|
+
/** The affected record's ID. */
|
|
475
|
+
entity_id: string;
|
|
476
|
+
/** Tenant ID that owns this event. */
|
|
477
|
+
tenant_id: string;
|
|
478
|
+
/** Per-field before/after values for the change. */
|
|
479
|
+
changes: Record<string, {
|
|
480
|
+
old: unknown;
|
|
481
|
+
new: unknown;
|
|
482
|
+
}>;
|
|
483
|
+
/** Full record snapshot at the time of the event. */
|
|
484
|
+
snapshot: Record<string, unknown>;
|
|
485
|
+
/** ISO 8601 timestamp of when the event occurred. */
|
|
486
|
+
timestamp: string;
|
|
487
|
+
/** Correlation ID linking related events/actions. */
|
|
488
|
+
correlation_id: string;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* The webhook delivery envelope Planes POSTs to a subscriber's URL.
|
|
492
|
+
*
|
|
493
|
+
* This is the outermost body: a delivery ID, the schema `version`, the wrapped
|
|
494
|
+
* {@link EntityEvent}, and the subscription that matched. The entity body lives
|
|
495
|
+
* at `event.snapshot` — there is no top-level `data`.
|
|
496
|
+
*/
|
|
497
|
+
interface WebhookDelivery {
|
|
498
|
+
/** Delivery ID (the delivery-log row id). */
|
|
499
|
+
id: string;
|
|
500
|
+
/**
|
|
501
|
+
* Webhook payload schema version (from the `X-Planes-Webhook-Version`
|
|
502
|
+
* header / envelope, emitted API-side per E39-F1-S2). v1 requires no
|
|
503
|
+
* branching; surfaced here so consumers can switch on it in the future.
|
|
504
|
+
*/
|
|
505
|
+
version: number;
|
|
506
|
+
/** The entity-change event that triggered this delivery. */
|
|
507
|
+
event: EntityEvent;
|
|
508
|
+
/** ID of the webhook subscription that matched and produced this delivery. */
|
|
509
|
+
subscription_id: string;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Verify an HMAC-SHA256 webhook signature using timing-safe comparison.
|
|
513
|
+
*
|
|
514
|
+
* The signature header format is: `sha256=<hex-digest>`
|
|
515
|
+
*
|
|
516
|
+
* @param payload - The raw request body (string or Buffer)
|
|
517
|
+
* @param signature - The X-Planes-Signature header value
|
|
518
|
+
* @param secret - The webhook signing secret
|
|
519
|
+
* @returns true if the signature is valid, false otherwise
|
|
520
|
+
*/
|
|
521
|
+
declare function verifyWebhookSignature(payload: string | Buffer, signature: string, secret: string): boolean;
|
|
522
|
+
/**
|
|
523
|
+
* Parse and verify a webhook delivery payload.
|
|
524
|
+
*
|
|
525
|
+
* @param body - The raw request body as a string
|
|
526
|
+
* @param signature - The X-Planes-Signature header value
|
|
527
|
+
* @param secret - The webhook signing secret
|
|
528
|
+
* @returns The parsed and verified {@link WebhookDelivery} envelope
|
|
529
|
+
* @throws WebhookVerificationError if signature is invalid
|
|
530
|
+
*/
|
|
531
|
+
declare function parseWebhookEvent(body: string, signature: string, secret: string): WebhookDelivery;
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* A handler invoked for a matching webhook delivery. May be sync or async;
|
|
535
|
+
* async handlers are awaited by {@link WebhookRouter.process}.
|
|
536
|
+
*/
|
|
537
|
+
type WebhookHandler = (delivery: WebhookDelivery) => void | Promise<void>;
|
|
538
|
+
/** Input to {@link WebhookRouter.process} — the raw request pieces. */
|
|
539
|
+
interface WebhookProcessInput {
|
|
540
|
+
/** The raw request body as a string (verbatim — not re-serialized JSON). */
|
|
541
|
+
body: string;
|
|
542
|
+
/** The `X-Planes-Signature` header value (`sha256=<hex>`). */
|
|
543
|
+
signature: string;
|
|
544
|
+
/** The webhook signing secret for this subscription. */
|
|
545
|
+
secret: string;
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Ergonomic event router over the corrected {@link WebhookDelivery} envelope.
|
|
549
|
+
*
|
|
550
|
+
* The platform models an event as two fields — `event.entity_type` and
|
|
551
|
+
* `event.event_type` — which this router joins into a dotted composite key
|
|
552
|
+
* `` `${entity_type}.${event_type}` `` (e.g. `opportunity.created`). Handlers
|
|
553
|
+
* subscribe to that key, to an entity-scoped wildcard `` `${entity_type}.*` ``
|
|
554
|
+
* (e.g. `opportunity.*`), or to the global wildcard `*`.
|
|
555
|
+
*
|
|
556
|
+
* The router is a pure dispatcher: it verifies + parses the delivery, then
|
|
557
|
+
* fans it out to matching handlers. It holds no connection and no auth — the
|
|
558
|
+
* signing secret is supplied per {@link process} call — so the same client's
|
|
559
|
+
* `webhooks` router works across subscriptions. Wire it into your own HTTP
|
|
560
|
+
* handler.
|
|
561
|
+
*
|
|
562
|
+
* @example
|
|
563
|
+
* ```ts
|
|
564
|
+
* client.webhooks
|
|
565
|
+
* .on('opportunity.created', (d) => console.log(d.event.snapshot))
|
|
566
|
+
* .on('opportunity.*', (d) => audit(d))
|
|
567
|
+
* .on('*', (d) => metrics.count(d.event.entity_type));
|
|
568
|
+
*
|
|
569
|
+
* // inside your POST handler:
|
|
570
|
+
* await client.webhooks.process({ body: rawBody, signature, secret });
|
|
571
|
+
* ```
|
|
572
|
+
*
|
|
573
|
+
* ### Dotted `event_type` composite keys
|
|
574
|
+
*
|
|
575
|
+
* Four event types are themselves dotted — `entity.tagged`, `entity.untagged`,
|
|
576
|
+
* `list.member_added`, `list.member_removed`. Joined with their entity these
|
|
577
|
+
* yield keys with more than one dot, e.g. `customer.entity.tagged`. This router
|
|
578
|
+
* matches on the literal `` `${entity_type}.${event_type}` `` string, so
|
|
579
|
+
* subscribe with the full key (`'customer.entity.tagged'`). The entity wildcard
|
|
580
|
+
* (`'customer.*'`) and global wildcard (`'*'`) still match these deliveries;
|
|
581
|
+
* only mid-segment wildcards like `'customer.entity.*'` are intentionally
|
|
582
|
+
* unsupported.
|
|
583
|
+
*/
|
|
584
|
+
declare class WebhookRouter {
|
|
585
|
+
/** pattern (composite key / entity wildcard / '*') → registered handlers. */
|
|
586
|
+
private readonly handlers;
|
|
587
|
+
/**
|
|
588
|
+
* Register a handler for an event pattern.
|
|
589
|
+
*
|
|
590
|
+
* @param pattern - `"{entity_type}.{event_type}"` (e.g. `opportunity.created`),
|
|
591
|
+
* an entity wildcard `"{entity_type}.*"` (e.g. `opportunity.*`), or the
|
|
592
|
+
* global wildcard `"*"`. Matching is exact-string against the derived key;
|
|
593
|
+
* see the class docs for the dotted-`event_type` shape.
|
|
594
|
+
* @param handler - Invoked with the full {@link WebhookDelivery} on a match.
|
|
595
|
+
* @returns `this`, for chaining.
|
|
596
|
+
*/
|
|
597
|
+
on(pattern: string, handler: WebhookHandler): this;
|
|
598
|
+
/**
|
|
599
|
+
* Verify, parse, and dispatch a webhook delivery.
|
|
600
|
+
*
|
|
601
|
+
* The signature is verified first: on failure this throws
|
|
602
|
+
* {@link WebhookVerificationError} and dispatches nothing. On success it
|
|
603
|
+
* dispatches to every matching handler — specific key, then entity wildcard,
|
|
604
|
+
* then global — awaiting async handlers before resolving.
|
|
605
|
+
*
|
|
606
|
+
* @param input - The raw `{ body, signature, secret }`.
|
|
607
|
+
* @returns The parsed {@link WebhookDelivery}.
|
|
608
|
+
* @throws WebhookVerificationError if the signature is invalid.
|
|
609
|
+
*/
|
|
610
|
+
process(input: WebhookProcessInput): Promise<WebhookDelivery>;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* PlanesClient — typed API client for the Planes CRM platform.
|
|
155
615
|
*
|
|
156
616
|
* @example
|
|
157
617
|
* ```ts
|
|
158
|
-
* const client = new
|
|
618
|
+
* const client = new PlanesClient({ accessToken: 'tok_...' });
|
|
159
619
|
* const opps = await client.opportunities.list({ page: 1, limit: 20 });
|
|
160
620
|
* ```
|
|
161
621
|
*/
|
|
162
|
-
declare class
|
|
622
|
+
declare class PlanesClient {
|
|
163
623
|
private readonly http;
|
|
164
624
|
private _opportunities?;
|
|
165
625
|
private _customers?;
|
|
@@ -170,7 +630,11 @@ declare class BlueprintClient {
|
|
|
170
630
|
private _estimates?;
|
|
171
631
|
private _invoices?;
|
|
172
632
|
private _inventory?;
|
|
173
|
-
|
|
633
|
+
private _contacts?;
|
|
634
|
+
private _activities?;
|
|
635
|
+
private _tags?;
|
|
636
|
+
private _webhooks?;
|
|
637
|
+
constructor(config: PlanesConfig);
|
|
174
638
|
get opportunities(): OpportunityResource;
|
|
175
639
|
get customers(): CustomerResource;
|
|
176
640
|
get properties(): PropertyResource;
|
|
@@ -180,12 +644,22 @@ declare class BlueprintClient {
|
|
|
180
644
|
get estimates(): EstimateResource;
|
|
181
645
|
get invoices(): InvoiceResource;
|
|
182
646
|
get inventory(): InventoryResource;
|
|
647
|
+
get contacts(): ContactResource;
|
|
648
|
+
get activities(): ActivityResource;
|
|
649
|
+
get tags(): TagResource;
|
|
650
|
+
/**
|
|
651
|
+
* Ergonomic webhook event router — `client.webhooks.on(pattern, handler)`
|
|
652
|
+
* plus `client.webhooks.process({ body, signature, secret })`. Auth-independent
|
|
653
|
+
* (the signing secret is supplied per `.process()` call), so it is created
|
|
654
|
+
* lazily without the HTTP client. See {@link WebhookRouter}.
|
|
655
|
+
*/
|
|
656
|
+
get webhooks(): WebhookRouter;
|
|
183
657
|
}
|
|
184
658
|
|
|
185
659
|
/**
|
|
186
660
|
* Base error class for all Planes SDK errors.
|
|
187
661
|
*/
|
|
188
|
-
declare class
|
|
662
|
+
declare class PlanesError extends Error {
|
|
189
663
|
readonly status: number;
|
|
190
664
|
readonly code: string;
|
|
191
665
|
constructor(message: string, status: number, code: string);
|
|
@@ -193,19 +667,19 @@ declare class BlueprintError extends Error {
|
|
|
193
667
|
/**
|
|
194
668
|
* Thrown when authentication fails (401).
|
|
195
669
|
*/
|
|
196
|
-
declare class AuthenticationError extends
|
|
670
|
+
declare class AuthenticationError extends PlanesError {
|
|
197
671
|
constructor(message?: string);
|
|
198
672
|
}
|
|
199
673
|
/**
|
|
200
674
|
* Thrown when the user lacks permission (403).
|
|
201
675
|
*/
|
|
202
|
-
declare class AuthorizationError extends
|
|
676
|
+
declare class AuthorizationError extends PlanesError {
|
|
203
677
|
constructor(message?: string);
|
|
204
678
|
}
|
|
205
679
|
/**
|
|
206
680
|
* Thrown when a resource is not found (404).
|
|
207
681
|
*/
|
|
208
|
-
declare class NotFoundError extends
|
|
682
|
+
declare class NotFoundError extends PlanesError {
|
|
209
683
|
constructor(message?: string);
|
|
210
684
|
}
|
|
211
685
|
/** Field-level validation error detail. */
|
|
@@ -217,33 +691,33 @@ interface FieldError {
|
|
|
217
691
|
/**
|
|
218
692
|
* Thrown for validation failures (400) with field-level error details.
|
|
219
693
|
*/
|
|
220
|
-
declare class ValidationError extends
|
|
694
|
+
declare class ValidationError extends PlanesError {
|
|
221
695
|
readonly fieldErrors: FieldError[];
|
|
222
696
|
constructor(message?: string, fieldErrors?: FieldError[]);
|
|
223
697
|
}
|
|
224
698
|
/**
|
|
225
699
|
* Thrown when rate limited (429). Includes retryAfter in seconds.
|
|
226
700
|
*/
|
|
227
|
-
declare class RateLimitError extends
|
|
701
|
+
declare class RateLimitError extends PlanesError {
|
|
228
702
|
readonly retryAfter: number;
|
|
229
703
|
constructor(retryAfter?: number, message?: string);
|
|
230
704
|
}
|
|
231
705
|
/**
|
|
232
706
|
* Thrown for server errors (500+).
|
|
233
707
|
*/
|
|
234
|
-
declare class ServerError extends
|
|
708
|
+
declare class ServerError extends PlanesError {
|
|
235
709
|
constructor(message?: string);
|
|
236
710
|
}
|
|
237
711
|
/**
|
|
238
712
|
* Thrown when webhook signature verification fails.
|
|
239
713
|
*/
|
|
240
|
-
declare class WebhookVerificationError extends
|
|
714
|
+
declare class WebhookVerificationError extends PlanesError {
|
|
241
715
|
constructor(message?: string);
|
|
242
716
|
}
|
|
243
717
|
/**
|
|
244
718
|
* Thrown for OAuth flow errors.
|
|
245
719
|
*/
|
|
246
|
-
declare class OAuthError extends
|
|
720
|
+
declare class OAuthError extends PlanesError {
|
|
247
721
|
readonly errorCode: string;
|
|
248
722
|
constructor(errorCode: string, message?: string);
|
|
249
723
|
}
|
|
@@ -251,54 +725,130 @@ declare class OAuthError extends BlueprintError {
|
|
|
251
725
|
/**
|
|
252
726
|
* ActionHandler, ActionContext, and ActionResult types for the SDK.
|
|
253
727
|
*
|
|
254
|
-
* These
|
|
255
|
-
*
|
|
256
|
-
*
|
|
728
|
+
* These model the REAL over-the-wire contract that the Planes platform's
|
|
729
|
+
* external-agent dispatcher POSTs to a registered agent endpoint
|
|
730
|
+
* (`apps/api/src/modules/marketplace/handlers/external-agent.handler.ts`).
|
|
731
|
+
* They are self-contained — the SDK never imports `@blueprint/shared`, so
|
|
732
|
+
* external developers can depend on this package alone.
|
|
733
|
+
*
|
|
734
|
+
* ## Wire contract (source of truth: external-agent.handler.ts)
|
|
735
|
+
*
|
|
736
|
+
* The platform POSTs a FLATTENED, WRAPPED envelope — NOT the platform's internal
|
|
737
|
+
* nested `ActionContext`:
|
|
738
|
+
*
|
|
739
|
+
* ```json
|
|
740
|
+
* {
|
|
741
|
+
* "action_context": {
|
|
742
|
+
* "action_id": "act-1",
|
|
743
|
+
* "action_type": "enrich-opportunity",
|
|
744
|
+
* "entity_type": "opportunity",
|
|
745
|
+
* "entity_id": "opp-1",
|
|
746
|
+
* "tenant_id": "tenant-1",
|
|
747
|
+
* "data": { "...": "entity snapshot" },
|
|
748
|
+
* "stage": { "id": "...", "name": "...", "rules": null, "agent_role": null, "agent_instructions": null } | null,
|
|
749
|
+
* "metadata": { "trigger": { ... }, "execution": { ... } }
|
|
750
|
+
* },
|
|
751
|
+
* "callback_token": "jwt.callback.token",
|
|
752
|
+
* "callback_base_url": "https://…/api/v1"
|
|
753
|
+
* }
|
|
754
|
+
* ```
|
|
755
|
+
*
|
|
756
|
+
* ### Field convention
|
|
757
|
+
* The SDK mirrors the wire's **snake_case** verbatim (no camelCase translation),
|
|
758
|
+
* so a parsed request body maps 1:1 onto {@link ActionContext}. The one shaping
|
|
759
|
+
* the SDK server performs is to LIFT `callback_token` / `callback_base_url` (and
|
|
760
|
+
* the `X-Planes-Request-Id` header as {@link ActionContext.request_id}) up onto
|
|
761
|
+
* the same object handed to the handler — so a handler receives one flat context.
|
|
762
|
+
*
|
|
763
|
+
* ### Result delivery
|
|
764
|
+
* The platform reads the handler's {@link ActionResult} from the **synchronous
|
|
765
|
+
* HTTP response body** (`await response.json()` in the handler, dispatching on
|
|
766
|
+
* `status`). There is currently no async callback endpoint — `callback_token` /
|
|
767
|
+
* `callback_base_url` are minted for the agent to call back INTO the Planes API,
|
|
768
|
+
* not for the platform to poll. Handlers therefore just `return` their result.
|
|
769
|
+
*
|
|
770
|
+
* ### Not on the wire (intentionally absent vs. the platform's internal type)
|
|
771
|
+
* `permissions`, `agent_installation_id`, and `action.config` are NOT sent to
|
|
772
|
+
* external agents, so they are not modeled here.
|
|
257
773
|
*/
|
|
258
774
|
/** Trigger types for action execution. */
|
|
259
775
|
type TriggerType = 'workflow_transition' | 'schedule' | 'event' | 'agent';
|
|
260
|
-
/**
|
|
776
|
+
/**
|
|
777
|
+
* Permission levels (1 = most restricted, 4 = most powerful).
|
|
778
|
+
*
|
|
779
|
+
* Not part of the external wire envelope (the platform does not send permission
|
|
780
|
+
* metadata to external agents); retained as a public type for callers that
|
|
781
|
+
* reason about permission levels elsewhere.
|
|
782
|
+
*/
|
|
261
783
|
type PermissionLevel = 1 | 2 | 3 | 4;
|
|
784
|
+
/** How this action was triggered (wire: `action_context.metadata.trigger`). */
|
|
785
|
+
interface ActionTrigger {
|
|
786
|
+
type: TriggerType;
|
|
787
|
+
source_id: string;
|
|
788
|
+
triggered_by: string;
|
|
789
|
+
}
|
|
790
|
+
/** Execution/correlation metadata (wire: `action_context.metadata.execution`). */
|
|
791
|
+
interface ActionExecution {
|
|
792
|
+
id: string;
|
|
793
|
+
correlation_id: string;
|
|
794
|
+
attempt: number;
|
|
795
|
+
timeout_ms: number;
|
|
796
|
+
}
|
|
797
|
+
/** Trigger + execution metadata (wire: `action_context.metadata`). */
|
|
798
|
+
interface ActionMetadata {
|
|
799
|
+
trigger: ActionTrigger;
|
|
800
|
+
execution: ActionExecution;
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* Destination stage metadata — present for `workflow_transition` triggers,
|
|
804
|
+
* `null` otherwise (wire: `action_context.stage`).
|
|
805
|
+
*/
|
|
806
|
+
interface ActionStage {
|
|
807
|
+
id: string;
|
|
808
|
+
name: string;
|
|
809
|
+
rules: string | null;
|
|
810
|
+
agent_role: string | null;
|
|
811
|
+
agent_instructions: string | null;
|
|
812
|
+
}
|
|
262
813
|
/**
|
|
263
814
|
* ActionContext envelope — passed to handlers on each invocation.
|
|
815
|
+
*
|
|
816
|
+
* Shape matches the platform's flattened `action_context` (see file header),
|
|
817
|
+
* with `callback_token`, `callback_base_url`, and `request_id` lifted onto the
|
|
818
|
+
* same object by the SDK server for handler convenience.
|
|
264
819
|
*/
|
|
265
820
|
interface ActionContext {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
id: string;
|
|
298
|
-
correlation_id: string;
|
|
299
|
-
attempt: number;
|
|
300
|
-
timeout_ms: number;
|
|
301
|
-
};
|
|
821
|
+
/** Unique ID of the action being executed (wire: `action_context.action_id`). */
|
|
822
|
+
action_id: string;
|
|
823
|
+
/** The action type — the SDK routes to a handler by this value. */
|
|
824
|
+
action_type: string;
|
|
825
|
+
/** The entity type this action operates on, e.g. `'opportunity'`. */
|
|
826
|
+
entity_type: string;
|
|
827
|
+
/** The entity's ID. */
|
|
828
|
+
entity_id: string;
|
|
829
|
+
/** The tenant (company) ID this action runs for. */
|
|
830
|
+
tenant_id: string;
|
|
831
|
+
/** The entity snapshot captured at trigger time (wire: `action_context.data`). */
|
|
832
|
+
data: Record<string, unknown>;
|
|
833
|
+
/** Destination stage metadata for workflow transitions; `null` otherwise. */
|
|
834
|
+
stage: ActionStage | null;
|
|
835
|
+
/** Trigger + execution metadata. */
|
|
836
|
+
metadata: ActionMetadata;
|
|
837
|
+
/**
|
|
838
|
+
* Short-lived (5-min) JWT for calling back INTO the Planes API at
|
|
839
|
+
* {@link callback_base_url}. Lifted from the envelope's top-level `callback_token`.
|
|
840
|
+
*/
|
|
841
|
+
callback_token: string;
|
|
842
|
+
/**
|
|
843
|
+
* Base URL (e.g. `https://…/api/v1`) for authenticated callbacks using
|
|
844
|
+
* {@link callback_token}. Lifted from the envelope's top-level `callback_base_url`.
|
|
845
|
+
*/
|
|
846
|
+
callback_base_url: string;
|
|
847
|
+
/**
|
|
848
|
+
* Per-invocation correlation ID from the `X-Planes-Request-Id` request header,
|
|
849
|
+
* for logging/idempotency. `undefined` if the header was absent.
|
|
850
|
+
*/
|
|
851
|
+
request_id?: string;
|
|
302
852
|
}
|
|
303
853
|
/** A follow-up action request (used with delegated status). */
|
|
304
854
|
interface ActionRequest {
|
|
@@ -308,6 +858,10 @@ interface ActionRequest {
|
|
|
308
858
|
}
|
|
309
859
|
/**
|
|
310
860
|
* ActionResult — the return value from a handler's execute() method.
|
|
861
|
+
*
|
|
862
|
+
* The platform reads this from the synchronous HTTP response body and dispatches
|
|
863
|
+
* on `status` (`completed` → `output`, `failed` → `error`/`retryable`,
|
|
864
|
+
* `needs_human` → `reason`/`context`, `delegated` → `follow_up_actions`).
|
|
311
865
|
*/
|
|
312
866
|
type ActionResult = {
|
|
313
867
|
status: 'completed';
|
|
@@ -332,14 +886,15 @@ interface ActionHandler {
|
|
|
332
886
|
execute(ctx: ActionContext): Promise<ActionResult>;
|
|
333
887
|
}
|
|
334
888
|
/**
|
|
335
|
-
* Generic typed version of ActionHandler for specific
|
|
889
|
+
* Generic typed version of ActionHandler for a specific entity-data/output shape.
|
|
890
|
+
*
|
|
891
|
+
* `TData` narrows {@link ActionContext.data} (the entity snapshot) — the external
|
|
892
|
+
* wire carries no per-action `config`, so typed handler input is the entity data.
|
|
336
893
|
*/
|
|
337
|
-
interface AgentHandler<
|
|
894
|
+
interface AgentHandler<TData = Record<string, unknown>, TOutput = unknown> {
|
|
338
895
|
readonly type: string;
|
|
339
896
|
execute(ctx: ActionContext & {
|
|
340
|
-
|
|
341
|
-
config: TInput;
|
|
342
|
-
};
|
|
897
|
+
data: TData;
|
|
343
898
|
}): Promise<{
|
|
344
899
|
status: 'completed';
|
|
345
900
|
output?: TOutput;
|
|
@@ -362,7 +917,7 @@ interface AgentHandler<TInput = Record<string, unknown>, TOutput = unknown> {
|
|
|
362
917
|
type HandlerMap = Record<string, ActionHandler>;
|
|
363
918
|
|
|
364
919
|
/**
|
|
365
|
-
* Options for
|
|
920
|
+
* Options for Planes.register() and AgentApp.listen().
|
|
366
921
|
*/
|
|
367
922
|
interface RegisterOptions {
|
|
368
923
|
/** Port to listen on (default 3000). */
|
|
@@ -375,16 +930,16 @@ interface RegisterOptions {
|
|
|
375
930
|
onError?: (error: Error) => void;
|
|
376
931
|
}
|
|
377
932
|
/**
|
|
378
|
-
*
|
|
933
|
+
* Planes agent registration namespace.
|
|
379
934
|
* Provides a convenient way to register action handlers as an HTTP server.
|
|
380
935
|
*/
|
|
381
|
-
declare const
|
|
936
|
+
declare const Planes: {
|
|
382
937
|
/**
|
|
383
938
|
* Register action handlers and start an HTTP server to receive action requests.
|
|
384
939
|
*
|
|
385
940
|
* @example
|
|
386
941
|
* ```ts
|
|
387
|
-
*
|
|
942
|
+
* Planes.register([
|
|
388
943
|
* {
|
|
389
944
|
* type: 'send_email',
|
|
390
945
|
* async execute(ctx) {
|
|
@@ -397,48 +952,49 @@ declare const Blueprint: {
|
|
|
397
952
|
*/
|
|
398
953
|
register(handlers: ActionHandler[], options?: RegisterOptions): http.Server;
|
|
399
954
|
};
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* Typed webhook event payload from Planes.
|
|
403
|
-
*/
|
|
404
|
-
interface WebhookEvent<T = unknown> {
|
|
405
|
-
/** Event ID (e.g. evt_abc123). */
|
|
406
|
-
id: string;
|
|
407
|
-
/** Event type (e.g. opportunity.created). */
|
|
408
|
-
type: string;
|
|
409
|
-
/** Tenant ID that owns this event. */
|
|
410
|
-
tenant_id: string;
|
|
411
|
-
/** ISO 8601 timestamp of when the event occurred. */
|
|
412
|
-
timestamp: string;
|
|
413
|
-
/** Entity snapshot at the time of the event. */
|
|
414
|
-
data: T;
|
|
415
|
-
/** Metadata about the event trigger. */
|
|
416
|
-
metadata: {
|
|
417
|
-
correlation_id: string;
|
|
418
|
-
triggered_by: 'workflow' | 'api' | 'user';
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
955
|
/**
|
|
422
|
-
*
|
|
956
|
+
* Builder-style agent app. Register action handlers incrementally by type,
|
|
957
|
+
* then start the server with {@link AgentApp.listen}.
|
|
423
958
|
*
|
|
424
|
-
*
|
|
959
|
+
* This is ergonomic sugar over {@link Planes.register} — both share the same
|
|
960
|
+
* underlying server/dispatch implementation ({@link createActionServer}).
|
|
425
961
|
*
|
|
426
|
-
* @
|
|
427
|
-
*
|
|
428
|
-
*
|
|
429
|
-
*
|
|
962
|
+
* @example
|
|
963
|
+
* ```ts
|
|
964
|
+
* createAgentApp()
|
|
965
|
+
* .registerAction('enrich-opportunity', async (ctx) => {
|
|
966
|
+
* // ... enrichment logic
|
|
967
|
+
* return { status: 'completed', output: { enriched: true } };
|
|
968
|
+
* })
|
|
969
|
+
* .listen({ port: 3000 });
|
|
970
|
+
* ```
|
|
430
971
|
*/
|
|
431
|
-
declare
|
|
972
|
+
declare class AgentApp {
|
|
973
|
+
private readonly handlers;
|
|
974
|
+
/**
|
|
975
|
+
* Register a handler for an action type. Accepts a bare `execute` function
|
|
976
|
+
* (the caller does not build the `{ type, execute }` object).
|
|
977
|
+
*
|
|
978
|
+
* @param type - The action type this handler responds to.
|
|
979
|
+
* @param execute - The handler function invoked with the {@link ActionContext}.
|
|
980
|
+
* @returns `this`, for chaining.
|
|
981
|
+
*/
|
|
982
|
+
registerAction(type: string, execute: ActionHandler['execute']): this;
|
|
983
|
+
/**
|
|
984
|
+
* Start an HTTP server that dispatches incoming action requests to the
|
|
985
|
+
* registered handlers. Delegates to the same server factory as
|
|
986
|
+
* {@link Planes.register}.
|
|
987
|
+
*
|
|
988
|
+
* The handler set is snapshotted at call time, so registering further actions
|
|
989
|
+
* after `listen()` does not mutate the running server (parity with
|
|
990
|
+
* `Planes.register`, which snapshots its array).
|
|
991
|
+
*/
|
|
992
|
+
listen(options?: RegisterOptions): http.Server;
|
|
993
|
+
}
|
|
432
994
|
/**
|
|
433
|
-
*
|
|
434
|
-
*
|
|
435
|
-
* @param body - The raw request body as a string
|
|
436
|
-
* @param signature - The X-Blueprint-Signature header value
|
|
437
|
-
* @param secret - The webhook signing secret
|
|
438
|
-
* @returns The parsed and typed webhook event
|
|
439
|
-
* @throws WebhookVerificationError if signature is invalid
|
|
995
|
+
* Create a new {@link AgentApp} builder.
|
|
440
996
|
*/
|
|
441
|
-
declare function
|
|
997
|
+
declare function createAgentApp(): AgentApp;
|
|
442
998
|
|
|
443
999
|
/**
|
|
444
1000
|
* Options for building an OAuth authorization URL.
|
|
@@ -484,11 +1040,25 @@ interface RefreshOptions {
|
|
|
484
1040
|
refreshToken: string;
|
|
485
1041
|
}
|
|
486
1042
|
/**
|
|
487
|
-
*
|
|
1043
|
+
* Options for the client_credentials grant (M2M auth — no user/tenant context).
|
|
1044
|
+
*/
|
|
1045
|
+
interface ClientCredentialsOptions {
|
|
1046
|
+
/** Planes API base URL. */
|
|
1047
|
+
baseUrl: string;
|
|
1048
|
+
/** OAuth client ID. */
|
|
1049
|
+
clientId: string;
|
|
1050
|
+
/** OAuth client secret. */
|
|
1051
|
+
clientSecret: string;
|
|
1052
|
+
/** Requested OAuth scopes. */
|
|
1053
|
+
scopes: string[];
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
1056
|
+
* OAuth token response. `refresh_token` is absent for the client_credentials
|
|
1057
|
+
* grant (there is no user session to refresh on behalf of).
|
|
488
1058
|
*/
|
|
489
1059
|
interface TokenResponse {
|
|
490
1060
|
access_token: string;
|
|
491
|
-
refresh_token
|
|
1061
|
+
refresh_token?: string;
|
|
492
1062
|
expires_in: number;
|
|
493
1063
|
token_type: string;
|
|
494
1064
|
scope: string;
|
|
@@ -516,6 +1086,15 @@ declare function exchangeCode(options: ExchangeCodeOptions): Promise<TokenRespon
|
|
|
516
1086
|
* @throws OAuthError on failure
|
|
517
1087
|
*/
|
|
518
1088
|
declare function refreshAccessToken(options: RefreshOptions): Promise<TokenResponse>;
|
|
1089
|
+
/**
|
|
1090
|
+
* Obtain an access token via the client_credentials grant — the M2M auth
|
|
1091
|
+
* pattern for connectors and external services with no user/tenant context.
|
|
1092
|
+
*
|
|
1093
|
+
* @param options - Client credentials options
|
|
1094
|
+
* @returns Token response (no refresh_token)
|
|
1095
|
+
* @throws OAuthError on failure
|
|
1096
|
+
*/
|
|
1097
|
+
declare function clientCredentialsGrant(options: ClientCredentialsOptions): Promise<TokenResponse>;
|
|
519
1098
|
|
|
520
1099
|
interface OpportunityDto {
|
|
521
1100
|
id: string;
|
|
@@ -593,4 +1172,4 @@ interface CustomersGeneratedResource {
|
|
|
593
1172
|
createCustomer(data: CreateCustomerDto): Promise<CustomerDto>;
|
|
594
1173
|
}
|
|
595
1174
|
|
|
596
|
-
export { type ActionContext, type ActionHandler, type ActionRequest, type ActionResult, type AgentHandler, AuthenticationError, AuthorizationError, type AuthorizeOptions, BaseResource,
|
|
1175
|
+
export { type ActionContext, type ActionHandler, type ActionRequest, type ActionResult, type ActivityDto, ActivityResource, AgentApp, type AgentHandler, AuthenticationError, AuthorizationError, type AuthorizeOptions, BaseResource, CalendarEventResource, type ClientCredentialsOptions, type ContactChannelDto, ContactChannelResource, type ContactDto, type ContactMergeResult, ContactResource, ContractResource, type CreateActivityDto, type CreateContactChannelDto, type CreateContactDto, type CreateCustomerDto, type CreateOpportunityDto, type CreateTagDto, type CustomerDto, CustomerResource, type CustomersGeneratedResource, DEFAULT_BASE_URL, DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT, DEFAULT_TOKEN_REFRESH_SKEW_MS, type EntityEvent, type EntityEventType, type EntityTagDto, type EntityType, EstimateResource, type ExchangeCodeOptions, type FieldError, type HandlerMap, InventoryResource, InvoiceResource, JobResource, type ListParams, MemoryTokenStorage, NotFoundError, OAuthError, type OpportunitiesGeneratedResource, type OpportunityDto, OpportunityResource, type PaginatedCustomerResponse, type PaginatedOpportunityResponse, type PaginatedResponse, type PaginationMeta, type PermissionLevel, Planes, PlanesClient, type PlanesConfig, PlanesError, PropertyResource, RateLimitError, type RefreshOptions, type RegisterOptions, ServerError, type SetEntityTagsDto, type StoredTokens, type TagDto, type TagEntityDto, TagResource, type TokenResponse, type TokenStorage, type TriggerType, type UpdateContactChannelDto, type UpdateContactDto, type UpdateOpportunityDto, type UpdateTagDto, ValidationError, type WebhookDelivery, type WebhookHandler, type WebhookProcessInput, WebhookRouter, WebhookVerificationError, buildAuthorizeUrl, clientCredentialsGrant, createAgentApp, exchangeCode, parseWebhookEvent, refreshAccessToken, verifyWebhookSignature };
|