@attrove/sdk 0.1.14 → 0.1.16

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.
Files changed (58) hide show
  1. package/cjs/admin-client.js +2 -0
  2. package/cjs/client.js +14 -12
  3. package/cjs/constants.js +1 -1
  4. package/cjs/index.js +4 -1
  5. package/cjs/resources/entities.js +90 -0
  6. package/cjs/resources/index.js +5 -1
  7. package/cjs/resources/messages.js +9 -3
  8. package/cjs/resources/webhooks.js +171 -0
  9. package/cjs/types/index.js +3 -1
  10. package/cjs/utils/fetch.js +1 -0
  11. package/cjs/utils/index.js +4 -1
  12. package/cjs/utils/webhooks.js +132 -0
  13. package/esm/admin-client.d.ts +5 -0
  14. package/esm/admin-client.d.ts.map +1 -1
  15. package/esm/admin-client.js +2 -0
  16. package/esm/admin-client.js.map +1 -1
  17. package/esm/client.d.ts +15 -10
  18. package/esm/client.d.ts.map +1 -1
  19. package/esm/client.js +29 -27
  20. package/esm/client.js.map +1 -1
  21. package/esm/constants.d.ts +1 -1
  22. package/esm/constants.js +1 -1
  23. package/esm/index.d.ts +22 -18
  24. package/esm/index.d.ts.map +1 -1
  25. package/esm/index.js +9 -8
  26. package/esm/index.js.map +1 -1
  27. package/esm/resources/entities.d.ts +71 -0
  28. package/esm/resources/entities.d.ts.map +1 -0
  29. package/esm/resources/entities.js +87 -0
  30. package/esm/resources/entities.js.map +1 -0
  31. package/esm/resources/index.d.ts +21 -17
  32. package/esm/resources/index.d.ts.map +1 -1
  33. package/esm/resources/index.js +12 -10
  34. package/esm/resources/index.js.map +1 -1
  35. package/esm/resources/messages.d.ts +2 -2
  36. package/esm/resources/messages.d.ts.map +1 -1
  37. package/esm/resources/messages.js +9 -3
  38. package/esm/resources/messages.js.map +1 -1
  39. package/esm/resources/webhooks.d.ts +100 -0
  40. package/esm/resources/webhooks.d.ts.map +1 -0
  41. package/esm/resources/webhooks.js +168 -0
  42. package/esm/resources/webhooks.js.map +1 -0
  43. package/esm/types/index.d.ts +87 -22
  44. package/esm/types/index.d.ts.map +1 -1
  45. package/esm/types/index.js +2 -0
  46. package/esm/types/index.js.map +1 -1
  47. package/esm/utils/fetch.d.ts.map +1 -1
  48. package/esm/utils/fetch.js +1 -0
  49. package/esm/utils/fetch.js.map +1 -1
  50. package/esm/utils/index.d.ts +6 -4
  51. package/esm/utils/index.d.ts.map +1 -1
  52. package/esm/utils/index.js +3 -2
  53. package/esm/utils/index.js.map +1 -1
  54. package/esm/utils/webhooks.d.ts +40 -0
  55. package/esm/utils/webhooks.d.ts.map +1 -0
  56. package/esm/utils/webhooks.js +127 -0
  57. package/esm/utils/webhooks.js.map +1 -0
  58. package/package.json +1 -1
@@ -13,6 +13,7 @@ const index_js_2 = require("./types/index.js");
13
13
  const index_js_3 = require("./types/index.js");
14
14
  const constants_js_1 = require("./constants.js");
15
15
  const settings_js_1 = require("./resources/settings.js");
16
+ const webhooks_js_1 = require("./resources/webhooks.js");
16
17
  /**
17
18
  * Validate the admin client configuration.
18
19
  *
@@ -197,6 +198,7 @@ class AttroveAdmin {
197
198
  // Initialize resources
198
199
  this.users = new AdminUsersResource(this.http, this.config.clientId);
199
200
  this.settings = new settings_js_1.AdminSettingsResource(this.http, this.config.clientId);
201
+ this.webhooks = new webhooks_js_1.AdminWebhooksResource(this.http, this.config.clientId);
200
202
  }
201
203
  }
202
204
  exports.AttroveAdmin = AttroveAdmin;
package/cjs/client.js CHANGED
@@ -13,6 +13,7 @@ const users_js_1 = require("./resources/users.js");
13
13
  const messages_js_1 = require("./resources/messages.js");
14
14
  const conversations_js_1 = require("./resources/conversations.js");
15
15
  const integrations_js_1 = require("./resources/integrations.js");
16
+ const entities_js_1 = require("./resources/entities.js");
16
17
  const calendars_js_1 = require("./resources/calendars.js");
17
18
  const events_js_1 = require("./resources/events.js");
18
19
  const meetings_js_1 = require("./resources/meetings.js");
@@ -29,27 +30,27 @@ const constants_js_1 = require("./constants.js");
29
30
  */
30
31
  function validateConfig(config) {
31
32
  // Validate apiKey
32
- if (!config.apiKey || typeof config.apiKey !== "string") {
33
- throw new index_js_1.ValidationError("apiKey is required and must be a non-empty string", index_js_2.ErrorCodes.VALIDATION_REQUIRED_FIELD, { field: "apiKey" });
33
+ if (!config.apiKey || typeof config.apiKey !== 'string') {
34
+ throw new index_js_1.ValidationError('apiKey is required and must be a non-empty string', index_js_2.ErrorCodes.VALIDATION_REQUIRED_FIELD, { field: 'apiKey' });
34
35
  }
35
- if (!config.apiKey.startsWith("sk_")) {
36
- throw new index_js_1.ValidationError('apiKey must start with "sk_" prefix. Make sure you are using a valid API key.', index_js_2.ErrorCodes.VALIDATION_INVALID_FORMAT, { field: "apiKey", expected: "sk_..." });
36
+ if (!config.apiKey.startsWith('sk_')) {
37
+ throw new index_js_1.ValidationError('apiKey must start with "sk_" prefix. Make sure you are using a valid API key.', index_js_2.ErrorCodes.VALIDATION_INVALID_FORMAT, { field: 'apiKey', expected: 'sk_...' });
37
38
  }
38
39
  // Validate userId
39
- if (!config.userId || typeof config.userId !== "string") {
40
- throw new index_js_1.ValidationError("userId is required and must be a non-empty string", index_js_2.ErrorCodes.VALIDATION_REQUIRED_FIELD, { field: "userId" });
40
+ if (!config.userId || typeof config.userId !== 'string') {
41
+ throw new index_js_1.ValidationError('userId is required and must be a non-empty string', index_js_2.ErrorCodes.VALIDATION_REQUIRED_FIELD, { field: 'userId' });
41
42
  }
42
43
  if (!(0, index_js_3.isValidUUID)(config.userId)) {
43
- throw new index_js_1.ValidationError('userId must be a valid UUID format (e.g., "123e4567-e89b-12d3-a456-426614174000")', index_js_2.ErrorCodes.VALIDATION_INVALID_FORMAT, { field: "userId", expected: "UUID format" });
44
+ throw new index_js_1.ValidationError('userId must be a valid UUID format (e.g., "123e4567-e89b-12d3-a456-426614174000")', index_js_2.ErrorCodes.VALIDATION_INVALID_FORMAT, { field: 'userId', expected: 'UUID format' });
44
45
  }
45
46
  // Validate optional fields
46
47
  if (config.timeout !== undefined &&
47
- (typeof config.timeout !== "number" || config.timeout <= 0)) {
48
- throw new index_js_1.ValidationError("timeout must be a positive number in milliseconds", index_js_2.ErrorCodes.VALIDATION_OUT_OF_RANGE, { field: "timeout", value: config.timeout });
48
+ (typeof config.timeout !== 'number' || config.timeout <= 0)) {
49
+ throw new index_js_1.ValidationError('timeout must be a positive number in milliseconds', index_js_2.ErrorCodes.VALIDATION_OUT_OF_RANGE, { field: 'timeout', value: config.timeout });
49
50
  }
50
51
  if (config.maxRetries !== undefined &&
51
- (typeof config.maxRetries !== "number" || config.maxRetries < 0)) {
52
- throw new index_js_1.ValidationError("maxRetries must be a non-negative number", index_js_2.ErrorCodes.VALIDATION_OUT_OF_RANGE, { field: "maxRetries", value: config.maxRetries });
52
+ (typeof config.maxRetries !== 'number' || config.maxRetries < 0)) {
53
+ throw new index_js_1.ValidationError('maxRetries must be a non-negative number', index_js_2.ErrorCodes.VALIDATION_OUT_OF_RANGE, { field: 'maxRetries', value: config.maxRetries });
53
54
  }
54
55
  }
55
56
  /**
@@ -118,6 +119,7 @@ class Attrove {
118
119
  this.messages = new messages_js_1.MessagesResource(this.http, this.config.userId);
119
120
  this.conversations = new conversations_js_1.ConversationsResource(this.http, this.config.userId);
120
121
  this.integrations = new integrations_js_1.IntegrationsResource(this.http, this.config.userId);
122
+ this.entities = new entities_js_1.EntitiesResource(this.http, this.config.userId);
121
123
  this.calendars = new calendars_js_1.CalendarsResource(this.http, this.config.userId);
122
124
  this.events = new events_js_1.EventsResource(this.http, this.config.userId);
123
125
  this.meetings = new meetings_js_1.MeetingsResource(this.http, this.config.userId);
@@ -224,7 +226,7 @@ class Attrove {
224
226
  // Add the user's prompt to history
225
227
  const fullHistory = [
226
228
  ...history,
227
- { role: "user", content: prompt },
229
+ { role: 'user', content: prompt },
228
230
  ];
229
231
  // Extract query options (separate from stream options)
230
232
  const queryOptions = {
package/cjs/constants.js CHANGED
@@ -13,7 +13,7 @@ exports.getWsCloseReason = exports.WS_CLOSE_CODES = exports.RETRYABLE_STATUS_SET
13
13
  * Automatically synchronized with package.json during the release process.
14
14
  * For programmatic access, use `getVersion()` from './version'.
15
15
  */
16
- exports.SDK_VERSION = "0.1.14"; // auto-synced from package.json during release
16
+ exports.SDK_VERSION = "0.1.16"; // auto-synced from package.json during release
17
17
  /**
18
18
  * Default API base URL for Attrove services.
19
19
  */
package/cjs/index.js CHANGED
@@ -33,7 +33,7 @@
33
33
  * @packageDocumentation
34
34
  */
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.getVersion = exports.DEFAULT_MAX_RETRIES = exports.DEFAULT_TIMEOUT = exports.DEFAULT_BASE_URL = exports.SDK_VERSION = exports.generateMessageId = exports.isServerError = exports.isTimeoutError = exports.isNetworkError = exports.isRateLimitError = exports.isValidationError = exports.isNotFoundError = exports.isAuthorizationError = exports.isAuthenticationError = exports.isAttroveError = exports.ServerError = exports.TimeoutError = exports.NetworkError = exports.RateLimitError = exports.ValidationError = exports.NotFoundError = exports.AuthorizationError = exports.AuthenticationError = exports.AttroveError = exports.AttroveAdmin = exports.Attrove = void 0;
36
+ exports.getVersion = exports.DEFAULT_MAX_RETRIES = exports.DEFAULT_TIMEOUT = exports.DEFAULT_BASE_URL = exports.SDK_VERSION = exports.verifyWebhookSignatureDetailed = exports.verifyWebhookSignature = exports.generateMessageId = exports.isServerError = exports.isTimeoutError = exports.isNetworkError = exports.isRateLimitError = exports.isValidationError = exports.isNotFoundError = exports.isAuthorizationError = exports.isAuthenticationError = exports.isAttroveError = exports.ServerError = exports.TimeoutError = exports.NetworkError = exports.RateLimitError = exports.ValidationError = exports.NotFoundError = exports.AuthorizationError = exports.AuthenticationError = exports.AttroveError = exports.AttroveAdmin = exports.Attrove = void 0;
37
37
  const tslib_1 = require("tslib");
38
38
  const client_js_1 = require("./client.js");
39
39
  const admin_client_js_1 = require("./admin-client.js");
@@ -86,6 +86,9 @@ Object.defineProperty(exports, "isServerError", { enumerable: true, get: functio
86
86
  // Export streaming utilities
87
87
  var streaming_js_1 = require("./utils/streaming.js");
88
88
  Object.defineProperty(exports, "generateMessageId", { enumerable: true, get: function () { return streaming_js_1.generateMessageId; } });
89
+ var webhooks_js_1 = require("./utils/webhooks.js");
90
+ Object.defineProperty(exports, "verifyWebhookSignature", { enumerable: true, get: function () { return webhooks_js_1.verifyWebhookSignature; } });
91
+ Object.defineProperty(exports, "verifyWebhookSignatureDetailed", { enumerable: true, get: function () { return webhooks_js_1.verifyWebhookSignatureDetailed; } });
89
92
  // Export constants for advanced usage
90
93
  var constants_js_1 = require("./constants.js");
91
94
  Object.defineProperty(exports, "SDK_VERSION", { enumerable: true, get: function () { return constants_js_1.SDK_VERSION; } });
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ /**
3
+ * Entities Resource
4
+ *
5
+ * Provides methods for listing and retrieving contacts (entities)
6
+ * that a user has communicated with across connected integrations.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.EntitiesResource = void 0;
10
+ /**
11
+ * Entities resource for accessing contact data.
12
+ *
13
+ * Provides methods for listing and retrieving contacts (people and bots)
14
+ * that the user has communicated with across connected integrations
15
+ * (Gmail, Slack, Outlook, etc.).
16
+ */
17
+ class EntitiesResource {
18
+ constructor(http, userId) {
19
+ this.http = http;
20
+ this.userId = userId;
21
+ }
22
+ /**
23
+ * List contacts with optional filtering.
24
+ *
25
+ * @param options - Filtering and pagination options
26
+ * @returns Paginated list of contacts
27
+ *
28
+ * @throws {AuthenticationError} If the API key is invalid or expired
29
+ * @throws {ValidationError} If the filter parameters are invalid
30
+ * @throws {NetworkError} If unable to reach the API
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * // List all contacts
35
+ * const { data, pagination } = await attrove.entities.list();
36
+ *
37
+ * // Search by name or email
38
+ * const { data } = await attrove.entities.list({ search: 'john' });
39
+ *
40
+ * // Only non-bot contacts
41
+ * const { data } = await attrove.entities.list({ isBot: false });
42
+ *
43
+ * // Filter by type
44
+ * const { data } = await attrove.entities.list({ entityType: 'person' });
45
+ * ```
46
+ */
47
+ async list(options = {}) {
48
+ const params = {};
49
+ if (options.search) {
50
+ params.search = options.search;
51
+ }
52
+ if (options.entityType) {
53
+ params.entity_type = options.entityType;
54
+ }
55
+ if (options.isBot !== undefined) {
56
+ params.is_bot = String(options.isBot);
57
+ }
58
+ if (options.limit !== undefined) {
59
+ params.limit = String(options.limit);
60
+ }
61
+ if (options.offset !== undefined) {
62
+ params.offset = String(options.offset);
63
+ }
64
+ const response = await this.http.request(`/v1/users/${this.userId}/entities`, { method: 'GET' }, params);
65
+ return {
66
+ data: response.data,
67
+ pagination: response.pagination,
68
+ };
69
+ }
70
+ /**
71
+ * Get a single contact by ID.
72
+ *
73
+ * @param id - Entity ID (ent_xxx opaque format or raw UUID)
74
+ * @returns The requested contact
75
+ *
76
+ * @throws {AuthenticationError} If the API key is invalid or expired
77
+ * @throws {NotFoundError} If the contact does not exist
78
+ * @throws {NetworkError} If unable to reach the API
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * const contact = await attrove.entities.get('ent_5cyYg0:a9KRt4Y');
83
+ * console.log(contact.name, contact.entity_type);
84
+ * ```
85
+ */
86
+ async get(id) {
87
+ return this.http.get(`/v1/users/${this.userId}/entities/${id}`);
88
+ }
89
+ }
90
+ exports.EntitiesResource = EntitiesResource;
@@ -3,7 +3,7 @@
3
3
  * Resource exports
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.AdminSettingsResource = exports.QueryResource = exports.ThreadsResource = exports.MeetingsResource = exports.EventsResource = exports.CalendarsResource = exports.IntegrationsResource = exports.ConversationsResource = exports.MessagesResource = exports.UsersResource = void 0;
6
+ exports.AdminWebhooksResource = exports.AdminSettingsResource = exports.QueryResource = exports.ThreadsResource = exports.MeetingsResource = exports.EventsResource = exports.CalendarsResource = exports.EntitiesResource = exports.IntegrationsResource = exports.ConversationsResource = exports.MessagesResource = exports.UsersResource = void 0;
7
7
  var users_js_1 = require("./users.js");
8
8
  Object.defineProperty(exports, "UsersResource", { enumerable: true, get: function () { return users_js_1.UsersResource; } });
9
9
  var messages_js_1 = require("./messages.js");
@@ -12,6 +12,8 @@ var conversations_js_1 = require("./conversations.js");
12
12
  Object.defineProperty(exports, "ConversationsResource", { enumerable: true, get: function () { return conversations_js_1.ConversationsResource; } });
13
13
  var integrations_js_1 = require("./integrations.js");
14
14
  Object.defineProperty(exports, "IntegrationsResource", { enumerable: true, get: function () { return integrations_js_1.IntegrationsResource; } });
15
+ var entities_js_1 = require("./entities.js");
16
+ Object.defineProperty(exports, "EntitiesResource", { enumerable: true, get: function () { return entities_js_1.EntitiesResource; } });
15
17
  var calendars_js_1 = require("./calendars.js");
16
18
  Object.defineProperty(exports, "CalendarsResource", { enumerable: true, get: function () { return calendars_js_1.CalendarsResource; } });
17
19
  var events_js_1 = require("./events.js");
@@ -24,3 +26,5 @@ var query_js_1 = require("./query.js");
24
26
  Object.defineProperty(exports, "QueryResource", { enumerable: true, get: function () { return query_js_1.QueryResource; } });
25
27
  var settings_js_1 = require("./settings.js");
26
28
  Object.defineProperty(exports, "AdminSettingsResource", { enumerable: true, get: function () { return settings_js_1.AdminSettingsResource; } });
29
+ var webhooks_js_1 = require("./webhooks.js");
30
+ Object.defineProperty(exports, "AdminWebhooksResource", { enumerable: true, get: function () { return webhooks_js_1.AdminWebhooksResource; } });
@@ -45,7 +45,7 @@ class MessagesResource {
45
45
  async list(options = {}) {
46
46
  const params = {};
47
47
  if (options.ids?.length) {
48
- params.ids = options.ids.join(",");
48
+ params.ids = options.ids.join(',');
49
49
  }
50
50
  if (options.integrationId) {
51
51
  params.integration_id = options.integrationId;
@@ -65,13 +65,19 @@ class MessagesResource {
65
65
  if (options.offset !== undefined) {
66
66
  params.offset = String(options.offset);
67
67
  }
68
+ if (options.sentBy) {
69
+ params.sent_by = options.sentBy;
70
+ }
71
+ if (options.entityId) {
72
+ params.entity_id = options.entityId;
73
+ }
68
74
  if (options.excludeBots !== undefined) {
69
75
  params.exclude_bots = String(options.excludeBots);
70
76
  }
71
77
  if (options.expand?.length) {
72
- params.expand = options.expand.join(",");
78
+ params.expand = options.expand.join(',');
73
79
  }
74
- const response = await this.http.request(`/v1/users/${this.userId}/messages`, { method: "GET" }, params);
80
+ const response = await this.http.request(`/v1/users/${this.userId}/messages`, { method: 'GET' }, params);
75
81
  return {
76
82
  data: response.data,
77
83
  pagination: response.pagination,
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AdminWebhooksResource = void 0;
4
+ const index_js_1 = require("../errors/index.js");
5
+ const index_js_2 = require("../types/index.js");
6
+ function validateWebhookId(id, fieldName = 'id') {
7
+ if (!(0, index_js_2.isValidUUID)(id)) {
8
+ throw new index_js_1.ValidationError(`${fieldName} must be a valid UUID format`, index_js_2.ErrorCodes.VALIDATION_INVALID_FORMAT, { field: fieldName, expected: 'UUID format' });
9
+ }
10
+ }
11
+ function validateWebhookCreateOptions(options) {
12
+ if (!options.url || typeof options.url !== 'string') {
13
+ throw new index_js_1.ValidationError('url is required and must be a non-empty string', index_js_2.ErrorCodes.VALIDATION_REQUIRED_FIELD, { field: 'url' });
14
+ }
15
+ if (!options.url.startsWith('https://')) {
16
+ throw new index_js_1.ValidationError('url must use HTTPS', index_js_2.ErrorCodes.VALIDATION_INVALID_FORMAT, { field: 'url' });
17
+ }
18
+ if (!Array.isArray(options.eventTypes) || options.eventTypes.length === 0) {
19
+ throw new index_js_1.ValidationError('eventTypes must contain at least one event type', index_js_2.ErrorCodes.VALIDATION_REQUIRED_FIELD, { field: 'eventTypes' });
20
+ }
21
+ }
22
+ function validateWebhookUpdateOptions(options) {
23
+ if (options.url === undefined &&
24
+ options.eventTypes === undefined &&
25
+ options.isActive === undefined) {
26
+ throw new index_js_1.ValidationError('At least one field must be provided for update', index_js_2.ErrorCodes.VALIDATION_REQUIRED_FIELD, { field: 'options' });
27
+ }
28
+ if (typeof options.url === 'string' && !options.url.startsWith('https://')) {
29
+ throw new index_js_1.ValidationError('url must use HTTPS', index_js_2.ErrorCodes.VALIDATION_INVALID_FORMAT, { field: 'url' });
30
+ }
31
+ if (options.eventTypes !== undefined &&
32
+ (!Array.isArray(options.eventTypes) || options.eventTypes.length === 0)) {
33
+ throw new index_js_1.ValidationError('eventTypes must contain at least one event type', index_js_2.ErrorCodes.VALIDATION_INVALID_FORMAT, { field: 'eventTypes' });
34
+ }
35
+ }
36
+ function mapEndpoint(raw) {
37
+ return {
38
+ id: raw.id,
39
+ partnerId: raw.partner_id,
40
+ url: raw.url,
41
+ eventTypes: raw.event_types,
42
+ isActive: raw.is_active,
43
+ autoPausedAt: raw.auto_paused_at,
44
+ consecutiveFailures: raw.consecutive_failures,
45
+ secretPrefix: raw.secret_prefix,
46
+ secretSuffix: raw.secret_suffix,
47
+ createdByUserId: raw.created_by_user_id,
48
+ createdAt: raw.created_at,
49
+ updatedAt: raw.updated_at,
50
+ };
51
+ }
52
+ function mapDelivery(raw) {
53
+ return {
54
+ id: raw.id,
55
+ eventId: raw.event_id,
56
+ endpointId: raw.endpoint_id,
57
+ attempt: raw.attempt,
58
+ status: raw.status,
59
+ nextAttemptAt: raw.next_attempt_at,
60
+ responseStatus: raw.response_status,
61
+ responseBodyTruncated: raw.response_body_truncated,
62
+ durationMs: raw.duration_ms,
63
+ errorCode: raw.error_code,
64
+ webhookId: raw.webhook_id,
65
+ deliveredAt: raw.delivered_at,
66
+ createdAt: raw.created_at,
67
+ updatedAt: raw.updated_at,
68
+ eventType: raw.event_type,
69
+ eventVersion: raw.event_version,
70
+ occurredAt: raw.occurred_at,
71
+ };
72
+ }
73
+ class AdminWebhooksResource {
74
+ constructor(http, clientId) {
75
+ this.http = http;
76
+ this.clientId = clientId;
77
+ }
78
+ /** Creates a new webhook endpoint. The response includes the plaintext secret (shown only once). */
79
+ async create(options) {
80
+ validateWebhookCreateOptions(options);
81
+ const raw = await this.http.post('/v1/webhooks', {
82
+ url: options.url,
83
+ event_types: options.eventTypes,
84
+ is_active: options.isActive,
85
+ });
86
+ return {
87
+ ...mapEndpoint(raw),
88
+ secret: raw.secret,
89
+ };
90
+ }
91
+ /** Lists all webhook endpoints for this partner. */
92
+ async list() {
93
+ const raw = await this.http.get('/v1/webhooks');
94
+ return raw.map(mapEndpoint);
95
+ }
96
+ /** Retrieves a single webhook endpoint by ID. */
97
+ async get(id) {
98
+ validateWebhookId(id);
99
+ const raw = await this.http.get(`/v1/webhooks/${id}`);
100
+ return mapEndpoint(raw);
101
+ }
102
+ /** Updates a webhook endpoint's URL, subscribed event types, or active status. */
103
+ async update(id, options) {
104
+ validateWebhookId(id);
105
+ validateWebhookUpdateOptions(options);
106
+ const body = {};
107
+ if (options.url !== undefined) {
108
+ body['url'] = options.url;
109
+ }
110
+ if (options.eventTypes !== undefined) {
111
+ body['event_types'] = options.eventTypes;
112
+ }
113
+ if (options.isActive !== undefined) {
114
+ body['is_active'] = options.isActive;
115
+ }
116
+ const raw = await this.http.patch(`/v1/webhooks/${id}`, body);
117
+ return mapEndpoint(raw);
118
+ }
119
+ /** Deletes a webhook endpoint and all associated deliveries. */
120
+ async delete(id) {
121
+ validateWebhookId(id);
122
+ return this.http.delete(`/v1/webhooks/${id}`);
123
+ }
124
+ /** Sends a synthetic test event to the specified endpoint to verify connectivity. */
125
+ async test(id) {
126
+ validateWebhookId(id);
127
+ const raw = await this.http.post(`/v1/webhooks/${id}/test`, {});
128
+ return {
129
+ endpointId: raw.endpoint_id,
130
+ eventId: raw.event_id,
131
+ fanoutCount: raw.fanout_count,
132
+ };
133
+ }
134
+ /** Rotates the webhook signing secret. The previous secret remains valid for 24 hours. */
135
+ async rotateSecret(id) {
136
+ validateWebhookId(id);
137
+ const raw = await this.http.post(`/v1/webhooks/${id}/rotate-secret`, {});
138
+ return {
139
+ id: raw.id,
140
+ secret: raw.secret,
141
+ secretPrefix: raw.secret_prefix,
142
+ secretSuffix: raw.secret_suffix,
143
+ rotatedAt: raw.rotated_at,
144
+ };
145
+ }
146
+ /** Lists delivery attempts for a webhook endpoint, optionally filtered by status. */
147
+ async listDeliveries(id, options = {}) {
148
+ validateWebhookId(id);
149
+ const query = {};
150
+ if (typeof options.limit === 'number') {
151
+ if (!Number.isInteger(options.limit) ||
152
+ options.limit < 1 ||
153
+ options.limit > 100) {
154
+ throw new index_js_1.ValidationError('limit must be an integer between 1 and 100', index_js_2.ErrorCodes.VALIDATION_OUT_OF_RANGE, { field: 'limit', value: options.limit });
155
+ }
156
+ query.limit = String(options.limit);
157
+ }
158
+ if (typeof options.offset === 'number') {
159
+ if (!Number.isInteger(options.offset) || options.offset < 0) {
160
+ throw new index_js_1.ValidationError('offset must be a non-negative integer', index_js_2.ErrorCodes.VALIDATION_OUT_OF_RANGE, { field: 'offset', value: options.offset });
161
+ }
162
+ query.offset = String(options.offset);
163
+ }
164
+ if (options.status) {
165
+ query.status = options.status;
166
+ }
167
+ const raw = await this.http.get(`/v1/webhooks/${id}/deliveries`, query);
168
+ return raw.map(mapDelivery);
169
+ }
170
+ }
171
+ exports.AdminWebhooksResource = AdminWebhooksResource;
@@ -6,7 +6,7 @@
6
6
  * and do not depend on internal monorepo packages.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.isValidStreamFrame = exports.ErrorCodes = exports.isValidISODate = exports.isApiKey = exports.isValidApiKey = exports.createUUID = exports.createISODate = exports.createApiKey = exports.createConversationId = exports.createMessageId = exports.createIntegrationId = exports.createUserId = exports.isValidUUID = exports.asBrandedString = void 0;
9
+ exports.isValidStreamFrame = exports.UNKNOWN_SENDER = exports.ErrorCodes = exports.isValidISODate = exports.isApiKey = exports.isValidApiKey = exports.createUUID = exports.createISODate = exports.createApiKey = exports.createConversationId = exports.createMessageId = exports.createIntegrationId = exports.createUserId = exports.isValidUUID = exports.asBrandedString = void 0;
10
10
  /**
11
11
  * Helper to cast a string to a branded type.
12
12
  *
@@ -290,6 +290,8 @@ exports.ErrorCodes = {
290
290
  SERVICE_UNAVAILABLE: 'SERVICE_UNAVAILABLE',
291
291
  REQUEST_TIMEOUT: 'REQUEST_TIMEOUT',
292
292
  };
293
+ /** Sentinel value for messages where the sender could not be resolved from the platform. */
294
+ exports.UNKNOWN_SENDER = 'unknown';
293
295
  /**
294
296
  * Valid stream frame types.
295
297
  */
@@ -152,6 +152,7 @@ class HttpClient {
152
152
  else if (auth.type === "partner") {
153
153
  headers["Authorization"] = `Bearer ${auth.clientSecret}`;
154
154
  headers["X-Auth-Type"] = "partner";
155
+ headers["X-Client-Id"] = auth.clientId;
155
156
  }
156
157
  return headers;
157
158
  }
@@ -3,9 +3,12 @@
3
3
  * Utility exports
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.generateMessageId = exports.StreamingClient = exports.HttpClient = void 0;
6
+ exports.verifyWebhookSignatureDetailed = exports.verifyWebhookSignature = exports.generateMessageId = exports.StreamingClient = exports.HttpClient = void 0;
7
7
  var fetch_js_1 = require("./fetch.js");
8
8
  Object.defineProperty(exports, "HttpClient", { enumerable: true, get: function () { return fetch_js_1.HttpClient; } });
9
9
  var streaming_js_1 = require("./streaming.js");
10
10
  Object.defineProperty(exports, "StreamingClient", { enumerable: true, get: function () { return streaming_js_1.StreamingClient; } });
11
11
  Object.defineProperty(exports, "generateMessageId", { enumerable: true, get: function () { return streaming_js_1.generateMessageId; } });
12
+ var webhooks_js_1 = require("./webhooks.js");
13
+ Object.defineProperty(exports, "verifyWebhookSignature", { enumerable: true, get: function () { return webhooks_js_1.verifyWebhookSignature; } });
14
+ Object.defineProperty(exports, "verifyWebhookSignatureDetailed", { enumerable: true, get: function () { return webhooks_js_1.verifyWebhookSignatureDetailed; } });
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.verifyWebhookSignature = exports.verifyWebhookSignatureDetailed = void 0;
4
+ const tslib_1 = require("tslib");
5
+ /// <reference types="node" />
6
+ const crypto_1 = tslib_1.__importDefault(require("crypto"));
7
+ const DEFAULT_TOLERANCE_SECONDS = 300;
8
+ function getHeader(headers, name) {
9
+ const lowerName = name.toLowerCase();
10
+ if (typeof Headers !== 'undefined' && headers instanceof Headers) {
11
+ return headers.get(lowerName);
12
+ }
13
+ const record = headers;
14
+ for (const [key, value] of Object.entries(record)) {
15
+ if (key.toLowerCase() !== lowerName) {
16
+ continue;
17
+ }
18
+ if (Array.isArray(value)) {
19
+ return value[0] ?? null;
20
+ }
21
+ return value ?? null;
22
+ }
23
+ return null;
24
+ }
25
+ function normalizeBody(body) {
26
+ if (typeof body === 'string') {
27
+ return Buffer.from(body, 'utf8');
28
+ }
29
+ if (body instanceof Uint8Array) {
30
+ return Buffer.from(body);
31
+ }
32
+ return Buffer.from(body);
33
+ }
34
+ function extractSignatures(signatureHeader) {
35
+ const candidates = signatureHeader
36
+ .split(/\s+/)
37
+ .map((segment) => segment.trim())
38
+ .filter(Boolean);
39
+ const signatures = [];
40
+ for (const candidate of candidates) {
41
+ if (candidate.startsWith('v1,')) {
42
+ signatures.push(candidate.slice(3));
43
+ continue;
44
+ }
45
+ if (candidate.startsWith('v1=')) {
46
+ signatures.push(candidate.slice(3));
47
+ continue;
48
+ }
49
+ // Allow bare values to be tolerant of intermediary header transformations.
50
+ signatures.push(candidate);
51
+ }
52
+ return signatures.filter((signature) => signature.length > 0);
53
+ }
54
+ function expectedSignature(params) {
55
+ const hmac = crypto_1.default.createHmac('sha256', params.secret);
56
+ hmac.update(`${params.webhookId}.${params.timestamp}.`, 'utf8');
57
+ hmac.update(params.body);
58
+ return hmac.digest();
59
+ }
60
+ function safeCompare(left, right) {
61
+ if (left.length !== right.length) {
62
+ return false;
63
+ }
64
+ return crypto_1.default.timingSafeEqual(left, right);
65
+ }
66
+ /**
67
+ * Verifies Attrove webhook signatures using HMAC-SHA256 (`v1`).
68
+ * Returns a structured result with a reason on failure for easier debugging.
69
+ *
70
+ * Uses the raw secret string (including `whsec_` prefix) as the HMAC key.
71
+ * This matches the Attrove server's signing behaviour but differs from the
72
+ * Standard Webhooks reference implementation which base64-decodes the secret.
73
+ * Always use this SDK function (not a generic Standard Webhooks library) to verify.
74
+ *
75
+ * Signing input: `${webhook-id}.${webhook-timestamp}.${rawBody}`
76
+ *
77
+ * @remarks Node.js only — requires `crypto` and `Buffer` from Node.js built-ins.
78
+ */
79
+ function verifyWebhookSignatureDetailed(headers, rawBody, secret, options = {}) {
80
+ const webhookId = getHeader(headers, 'webhook-id');
81
+ const timestamp = getHeader(headers, 'webhook-timestamp');
82
+ const signatureHeader = getHeader(headers, 'webhook-signature');
83
+ if (!webhookId || !timestamp || !signatureHeader) {
84
+ return { valid: false, reason: 'missing_headers' };
85
+ }
86
+ if (!secret) {
87
+ return { valid: false, reason: 'missing_secret' };
88
+ }
89
+ const timestampSeconds = Number(timestamp);
90
+ if (!Number.isFinite(timestampSeconds)) {
91
+ return { valid: false, reason: 'invalid_timestamp' };
92
+ }
93
+ const now = options.currentTimestamp ?? Math.floor(Date.now() / 1000);
94
+ const tolerance = options.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;
95
+ if (Math.abs(now - timestampSeconds) > tolerance) {
96
+ return { valid: false, reason: 'timestamp_expired' };
97
+ }
98
+ const bodyBuffer = normalizeBody(rawBody);
99
+ const expected = expectedSignature({
100
+ secret,
101
+ webhookId,
102
+ timestamp,
103
+ body: bodyBuffer,
104
+ });
105
+ const providedSignatures = extractSignatures(signatureHeader);
106
+ const matched = providedSignatures.some((provided) => {
107
+ let decoded;
108
+ try {
109
+ decoded = Buffer.from(provided, 'base64');
110
+ }
111
+ catch {
112
+ return false;
113
+ }
114
+ return safeCompare(decoded, expected);
115
+ });
116
+ if (matched) {
117
+ return { valid: true };
118
+ }
119
+ return { valid: false, reason: 'no_matching_signature' };
120
+ }
121
+ exports.verifyWebhookSignatureDetailed = verifyWebhookSignatureDetailed;
122
+ /**
123
+ * Verifies Attrove webhook signatures using HMAC-SHA256 (`v1`).
124
+ * Convenience wrapper that returns a boolean instead of a detailed result.
125
+ *
126
+ * @remarks Node.js only — requires `crypto` and `Buffer` from Node.js built-ins.
127
+ */
128
+ function verifyWebhookSignature(headers, rawBody, secret, options = {}) {
129
+ return verifyWebhookSignatureDetailed(headers, rawBody, secret, options)
130
+ .valid;
131
+ }
132
+ exports.verifyWebhookSignature = verifyWebhookSignature;
@@ -7,6 +7,7 @@
7
7
  import { HttpClient } from "./utils/fetch.js";
8
8
  import { AttroveAdminConfig, CreateUserOptions, CreateUserResponse, CreateTokenResponse } from "./types/index.js";
9
9
  import { AdminSettingsResource } from "./resources/settings.js";
10
+ import { AdminWebhooksResource } from "./resources/webhooks.js";
10
11
  /**
11
12
  * Users admin resource for managing users.
12
13
  *
@@ -110,6 +111,10 @@ export declare class AttroveAdmin {
110
111
  * Settings resource for managing organization sync configuration.
111
112
  */
112
113
  readonly settings: AdminSettingsResource;
114
+ /**
115
+ * Webhooks resource for managing outbound webhook endpoints.
116
+ */
117
+ readonly webhooks: AdminWebhooksResource;
113
118
  /**
114
119
  * Create a new admin client.
115
120
  *
@@ -1 +1 @@
1
- {"version":3,"file":"admin-client.d.ts","sourceRoot":"","sources":["../../../../packages/sdk/src/admin-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EAEpB,MAAM,kBAAkB,CAAC;AAM1B,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAkDhE;;;;;GAKG;AACH,qBAAa,kBAAkB;IAE3B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBADR,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,MAAM;IAGnC;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACG,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAsBrE;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACG,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;CAqBvE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAa;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CACe;IAEtC;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,kBAAkB,CAAC;IAEnC;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,CAAC;IAEzC;;;;;;;;;;;;;;OAcG;gBACS,MAAM,EAAE,kBAAkB;CA0BvC"}
1
+ {"version":3,"file":"admin-client.d.ts","sourceRoot":"","sources":["../../../../packages/sdk/src/admin-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EAEpB,MAAM,kBAAkB,CAAC;AAM1B,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAkDhE;;;;;GAKG;AACH,qBAAa,kBAAkB;IAE3B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBADR,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,MAAM;IAGnC;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACG,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAsBrE;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACG,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;CAqBvE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAa;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CACe;IAEtC;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,kBAAkB,CAAC;IAEnC;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,CAAC;IAEzC;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,CAAC;IAEzC;;;;;;;;;;;;;;OAcG;gBACS,MAAM,EAAE,kBAAkB;CA2BvC"}
@@ -10,6 +10,7 @@ import { ErrorCodes } from "./types/index.js";
10
10
  import { isValidUUID, } from "./types/index.js";
11
11
  import { DEFAULT_BASE_URL, DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES, } from "./constants.js";
12
12
  import { AdminSettingsResource } from "./resources/settings.js";
13
+ import { AdminWebhooksResource } from "./resources/webhooks.js";
13
14
  /**
14
15
  * Validate the admin client configuration.
15
16
  *
@@ -193,6 +194,7 @@ export class AttroveAdmin {
193
194
  // Initialize resources
194
195
  this.users = new AdminUsersResource(this.http, this.config.clientId);
195
196
  this.settings = new AdminSettingsResource(this.http, this.config.clientId);
197
+ this.webhooks = new AdminWebhooksResource(this.http, this.config.clientId);
196
198
  }
197
199
  }
198
200
  //# sourceMappingURL=admin-client.js.map