@awell-health/awell-extensions 2.1.205 → 2.1.206

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.
@@ -332,6 +332,13 @@ export declare const actions: {
332
332
  description: string;
333
333
  required: true;
334
334
  };
335
+ signingSecret: {
336
+ key: string;
337
+ label: string;
338
+ obfuscated: true;
339
+ description: string;
340
+ required: false;
341
+ };
335
342
  }, "call_id">;
336
343
  sendCall: import("@awell-health/extensions-core").Action<{
337
344
  phoneNumber: {
@@ -659,6 +666,13 @@ export declare const actions: {
659
666
  description: string;
660
667
  required: true;
661
668
  };
669
+ signingSecret: {
670
+ key: string;
671
+ label: string;
672
+ obfuscated: true;
673
+ description: string;
674
+ required: false;
675
+ };
662
676
  }, "call_id">;
663
677
  getCallDetails: import("@awell-health/extensions-core").Action<{
664
678
  callId: {
@@ -676,6 +690,13 @@ export declare const actions: {
676
690
  description: string;
677
691
  required: true;
678
692
  };
693
+ signingSecret: {
694
+ key: string;
695
+ label: string;
696
+ obfuscated: true;
697
+ description: string;
698
+ required: false;
699
+ };
679
700
  }, "metadata" | "status" | "completed" | "from" | "to" | "summary" | "analysis" | "transcripts" | "analysisSchema" | "callData" | "callLength" | "startedAt" | "endAt" | "concatenatedTranscript" | "answeredBy" | "errorMessage">;
680
701
  stopActiveCall: import("@awell-health/extensions-core").Action<{
681
702
  callId: {
@@ -693,6 +714,13 @@ export declare const actions: {
693
714
  description: string;
694
715
  required: true;
695
716
  };
717
+ signingSecret: {
718
+ key: string;
719
+ label: string;
720
+ obfuscated: true;
721
+ description: string;
722
+ required: false;
723
+ };
696
724
  }, "message" | "status">;
697
725
  sendSms: import("@awell-health/extensions-core").Action<{
698
726
  userNumber: {
@@ -796,6 +824,13 @@ export declare const actions: {
796
824
  description: string;
797
825
  required: true;
798
826
  };
827
+ signingSecret: {
828
+ key: string;
829
+ label: string;
830
+ obfuscated: true;
831
+ description: string;
832
+ required: false;
833
+ };
799
834
  }, "conversationId" | "smsResponse">;
800
835
  createSmsConversation: import("@awell-health/extensions-core").Action<{
801
836
  userNumber: {
@@ -883,5 +918,12 @@ export declare const actions: {
883
918
  description: string;
884
919
  required: true;
885
920
  };
921
+ signingSecret: {
922
+ key: string;
923
+ label: string;
924
+ obfuscated: true;
925
+ description: string;
926
+ required: false;
927
+ };
886
928
  }, "conversationId" | "createResponse">;
887
929
  };
@@ -7,7 +7,15 @@ export declare const settings: {
7
7
  description: string;
8
8
  required: true;
9
9
  };
10
+ signingSecret: {
11
+ key: string;
12
+ label: string;
13
+ obfuscated: true;
14
+ description: string;
15
+ required: false;
16
+ };
10
17
  };
11
18
  export declare const SettingsValidationSchema: z.ZodObject<{
12
19
  apiKey: z.ZodString;
20
+ signingSecret: z.ZodOptional<z.ZodString>;
13
21
  }, z.core.$strip>;
@@ -10,8 +10,16 @@ exports.settings = {
10
10
  description: '',
11
11
  required: true,
12
12
  },
13
+ signingSecret: {
14
+ key: 'signingSecret',
15
+ label: 'Webhook signing secret',
16
+ obfuscated: true,
17
+ description: 'The webhook signing secret from the Bland dev portal (Account Settings, Keys tab, "Replace Secret"). Bland signs each webhook with an HMAC-SHA256 of the body using it, sent in the `X-Webhook-Signature` header. When set, webhooks that are not signed with it are rejected. When left empty, incoming webhooks are not verified.',
18
+ required: false,
19
+ },
13
20
  };
14
21
  exports.SettingsValidationSchema = zod_1.z.object({
15
22
  apiKey: zod_1.z.string().min(1),
23
+ signingSecret: zod_1.z.string().optional(),
16
24
  });
17
25
  //# sourceMappingURL=settings.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"settings.js","sourceRoot":"","sources":["../../../extensions/bland/settings.ts"],"names":[],"mappings":";;;AACA,6BAAqC;AAExB,QAAA,QAAQ,GAAG;IACtB,MAAM,EAAE;QACN,GAAG,EAAE,QAAQ;QACb,KAAK,EAAE,eAAe;QACtB,UAAU,EAAE,IAAI;QAChB,WAAW,EAAE,EAAE;QACf,QAAQ,EAAE,IAAI;KACf;CACgC,CAAA;AAEtB,QAAA,wBAAwB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC/C,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACuB,CAAC,CAAA"}
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../../../extensions/bland/settings.ts"],"names":[],"mappings":";;;AACA,6BAAqC;AAExB,QAAA,QAAQ,GAAG;IACtB,MAAM,EAAE;QACN,GAAG,EAAE,QAAQ;QACb,KAAK,EAAE,eAAe;QACtB,UAAU,EAAE,IAAI;QAChB,WAAW,EAAE,EAAE;QACf,QAAQ,EAAE,IAAI;KACf;IACD,aAAa,EAAE;QACb,GAAG,EAAE,eAAe;QACpB,KAAK,EAAE,wBAAwB;QAC/B,UAAU,EAAE,IAAI;QAChB,WAAW,EACT,qUAAqU;QACvU,QAAQ,EAAE,KAAK;KAChB;CACgC,CAAA;AAEtB,QAAA,wBAAwB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC/C,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACzB,aAAa,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACY,CAAC,CAAA"}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.callCompleted = void 0;
4
4
  const lodash_1 = require("lodash");
5
+ const verifyWebhookSignature_1 = require("../verifyWebhookSignature");
5
6
  const dataPoints = {
6
7
  callId: {
7
8
  key: 'callId',
@@ -33,15 +34,30 @@ exports.callCompleted = {
33
34
  dataPoints,
34
35
  onEvent: async ({ payload: { payload, rawBody, headers, settings }, onSuccess, onError, }) => {
35
36
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
37
+ if (!(0, verifyWebhookSignature_1.isWebhookRequestAuthorized)({
38
+ signingSecret: settings.signingSecret,
39
+ rawBody,
40
+ payload,
41
+ headers,
42
+ })) {
43
+ await onError({
44
+ response: {
45
+ statusCode: 401,
46
+ message: 'Invalid or missing X-Webhook-Signature header',
47
+ },
48
+ });
49
+ return;
50
+ }
36
51
  const callId = payload === null || payload === void 0 ? void 0 : payload.call_id;
37
52
  const awellPatientId = (_c = (_b = (_a = payload === null || payload === void 0 ? void 0 : payload.variables) === null || _a === void 0 ? void 0 : _a.metadata) === null || _b === void 0 ? void 0 : _b.awell_patient_id) !== null && _c !== void 0 ? _c : (_d = payload === null || payload === void 0 ? void 0 : payload.metadata) === null || _d === void 0 ? void 0 : _d.awell_patient_id;
38
53
  if ((0, lodash_1.isNil)(callId)) {
39
- return await onError({
54
+ await onError({
40
55
  response: {
41
56
  statusCode: 400,
42
57
  message: 'Missing call_id in payload',
43
58
  },
44
59
  });
60
+ return;
45
61
  }
46
62
  await onSuccess({
47
63
  data_points: {
@@ -1 +1 @@
1
- {"version":3,"file":"callCompleted.js","sourceRoot":"","sources":["../../../../../extensions/bland/webhooks/CallCompleted/callCompleted.ts"],"names":[],"mappings":";;;AAAA,mCAA8B;AAQ9B,MAAM,UAAU,GAAG;IACjB,MAAM,EAAE;QACN,GAAG,EAAE,QAAQ;QACb,SAAS,EAAE,QAAQ;KACpB;IACD,SAAS,EAAE;QACT,GAAG,EAAE,WAAW;QAChB,SAAS,EAAE,SAAS;KACrB;IACD,MAAM,EAAE;QACN,GAAG,EAAE,QAAQ;QACb,SAAS,EAAE,QAAQ;KACpB;IACD,UAAU,EAAE;QACV,GAAG,EAAE,YAAY;QACjB,SAAS,EAAE,QAAQ;KACpB;IACD,YAAY,EAAE;QACZ,GAAG,EAAE,cAAc;QACnB,SAAS,EAAE,QAAQ;KACpB;IACD,UAAU,EAAE;QACV,GAAG,EAAE,YAAY;QACjB,SAAS,EAAE,MAAM;KAClB;CAC4C,CAAA;AAElC,QAAA,aAAa,GAItB;IACF,GAAG,EAAE,eAAe;IACpB,UAAU;IACV,OAAO,EAAE,KAAK,EAAE,EACd,OAAO,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAChD,SAAS,EACT,OAAO,GACR,EAAE,EAAE;;QACH,MAAM,MAAM,GAAG,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,OAAO,CAAA;QAC/B,MAAM,cAAc,GAClB,MAAA,MAAA,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,SAAS,0CAAE,QAAQ,0CAAE,gBAAgB,mCAC9C,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,QAAQ,0CAAE,gBAAgB,CAAA;QAErC,IAAI,IAAA,cAAK,EAAC,MAAM,CAAC,EAAE,CAAC;YAClB,OAAO,MAAM,OAAO,CAAC;gBACnB,QAAQ,EAAE;oBACR,UAAU,EAAE,GAAG;oBACf,OAAO,EAAE,4BAA4B;iBACtC;aACF,CAAC,CAAA;QACJ,CAAC;QAED,MAAM,SAAS,CAAC;YACd,WAAW,EAAE;gBACX,MAAM;gBACN,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;gBACnC;;;mBAGG;gBACH,SAAS,EAAE,MAAA,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,SAAS,0CAAE,QAAQ,EAAE,mCAAI,EAAE;gBAC/C;;;;;;;;mBAQG;gBACH,MAAM,EAAE,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,mCAAI,EAAE;gBAC7B;;;;;;mBAMG;gBACH,UAAU,EAAE,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,WAAW,mCAAI,EAAE;gBACtC;;mBAEG;gBACH,YAAY,EAAE,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,aAAa,mCAAI,EAAE;aAC3C;YACD,GAAG,CAAC,cAAc,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,CAAC;SACpE,CAAC,CAAA;IACJ,CAAC;CACF,CAAA"}
1
+ {"version":3,"file":"callCompleted.js","sourceRoot":"","sources":["../../../../../extensions/bland/webhooks/CallCompleted/callCompleted.ts"],"names":[],"mappings":";;;AAAA,mCAA8B;AAM9B,sEAAsE;AAGtE,MAAM,UAAU,GAAG;IACjB,MAAM,EAAE;QACN,GAAG,EAAE,QAAQ;QACb,SAAS,EAAE,QAAQ;KACpB;IACD,SAAS,EAAE;QACT,GAAG,EAAE,WAAW;QAChB,SAAS,EAAE,SAAS;KACrB;IACD,MAAM,EAAE;QACN,GAAG,EAAE,QAAQ;QACb,SAAS,EAAE,QAAQ;KACpB;IACD,UAAU,EAAE;QACV,GAAG,EAAE,YAAY;QACjB,SAAS,EAAE,QAAQ;KACpB;IACD,YAAY,EAAE;QACZ,GAAG,EAAE,cAAc;QACnB,SAAS,EAAE,QAAQ;KACpB;IACD,UAAU,EAAE;QACV,GAAG,EAAE,YAAY;QACjB,SAAS,EAAE,MAAM;KAClB;CAC4C,CAAA;AAElC,QAAA,aAAa,GAItB;IACF,GAAG,EAAE,eAAe;IACpB,UAAU;IACV,OAAO,EAAE,KAAK,EAAE,EACd,OAAO,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAChD,SAAS,EACT,OAAO,GACR,EAAE,EAAE;;QACH,IACE,CAAC,IAAA,mDAA0B,EAAC;YAC1B,aAAa,EAAE,QAAQ,CAAC,aAAa;YACrC,OAAO;YACP,OAAO;YACP,OAAO;SACR,CAAC,EACF,CAAC;YACD,MAAM,OAAO,CAAC;gBACZ,QAAQ,EAAE;oBACR,UAAU,EAAE,GAAG;oBACf,OAAO,EAAE,+CAA+C;iBACzD;aACF,CAAC,CAAA;YACF,OAAM;QACR,CAAC;QAED,MAAM,MAAM,GAAG,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,OAAO,CAAA;QAC/B,MAAM,cAAc,GAClB,MAAA,MAAA,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,SAAS,0CAAE,QAAQ,0CAAE,gBAAgB,mCAC9C,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,QAAQ,0CAAE,gBAAgB,CAAA;QAErC,IAAI,IAAA,cAAK,EAAC,MAAM,CAAC,EAAE,CAAC;YAClB,MAAM,OAAO,CAAC;gBACZ,QAAQ,EAAE;oBACR,UAAU,EAAE,GAAG;oBACf,OAAO,EAAE,4BAA4B;iBACtC;aACF,CAAC,CAAA;YACF,OAAM;QACR,CAAC;QAED,MAAM,SAAS,CAAC;YACd,WAAW,EAAE;gBACX,MAAM;gBACN,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;gBACnC;;;mBAGG;gBACH,SAAS,EAAE,MAAA,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,SAAS,0CAAE,QAAQ,EAAE,mCAAI,EAAE;gBAC/C;;;;;;;;mBAQG;gBACH,MAAM,EAAE,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,mCAAI,EAAE;gBAC7B;;;;;;mBAMG;gBACH,UAAU,EAAE,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,WAAW,mCAAI,EAAE;gBACtC;;mBAEG;gBACH,YAAY,EAAE,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,aAAa,mCAAI,EAAE;aAC3C;YACD,GAAG,CAAC,cAAc,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,CAAC;SACpE,CAAC,CAAA;IACJ,CAAC;CACF,CAAA"}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.completedBlandText = void 0;
4
4
  const lodash_1 = require("lodash");
5
+ const verifyWebhookSignature_1 = require("../verifyWebhookSignature");
5
6
  const dataPoints = {
6
7
  conversationId: {
7
8
  key: 'conversationId',
@@ -48,8 +49,22 @@ exports.completedBlandText = {
48
49
  key: 'completedBlandText',
49
50
  description: 'Triggered when a Bland SMS conversation ends (status webhook with channel "sms").',
50
51
  dataPoints,
51
- onEvent: async ({ payload: { payload }, onSuccess, onError }) => {
52
+ onEvent: async ({ payload: { payload, rawBody, headers, settings }, onSuccess, onError, }) => {
52
53
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
54
+ if (!(0, verifyWebhookSignature_1.isWebhookRequestAuthorized)({
55
+ signingSecret: settings.signingSecret,
56
+ rawBody,
57
+ payload,
58
+ headers,
59
+ })) {
60
+ await onError({
61
+ response: {
62
+ statusCode: 401,
63
+ message: 'Invalid or missing X-Webhook-Signature header',
64
+ },
65
+ });
66
+ return;
67
+ }
53
68
  const conversationId = payload === null || payload === void 0 ? void 0 : payload.conversation_id;
54
69
  const awellPatientId = (_b = (_a = payload === null || payload === void 0 ? void 0 : payload.metadata) === null || _a === void 0 ? void 0 : _a.awell_patient_id) !== null && _b !== void 0 ? _b : (_d = (_c = payload === null || payload === void 0 ? void 0 : payload.variables) === null || _c === void 0 ? void 0 : _c.metadata) === null || _d === void 0 ? void 0 : _d.awell_patient_id;
55
70
  if ((0, lodash_1.isNil)(conversationId)) {
@@ -1 +1 @@
1
- {"version":3,"file":"completedBlandText.js","sourceRoot":"","sources":["../../../../../extensions/bland/webhooks/CompletedBlandText/completedBlandText.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"completedBlandText.js","sourceRoot":"","sources":["../../../../../extensions/bland/webhooks/CompletedBlandText/completedBlandText.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,13 @@ export declare const webhooks: (import("@awell-health/extensions-core").Webhook<
6
6
  description: string;
7
7
  required: true;
8
8
  };
9
+ signingSecret: {
10
+ key: string;
11
+ label: string;
12
+ obfuscated: true;
13
+ description: string;
14
+ required: false;
15
+ };
9
16
  }> | import("@awell-health/extensions-core").Webhook<"status" | "reason" | "pathwayId" | "variables" | "userNumber" | "agentNumber" | "conversationId" | "messageCount" | "transcript" | "conversationObject", import("./CompletedBlandText/types").CompletedBlandTextWebhookPayload, {
10
17
  apiKey: {
11
18
  key: string;
@@ -14,4 +21,11 @@ export declare const webhooks: (import("@awell-health/extensions-core").Webhook<
14
21
  description: string;
15
22
  required: true;
16
23
  };
24
+ signingSecret: {
25
+ key: string;
26
+ label: string;
27
+ obfuscated: true;
28
+ description: string;
29
+ required: false;
30
+ };
17
31
  }>)[];
@@ -0,0 +1,26 @@
1
+ import { type IncomingHttpHeaders } from 'http';
2
+ /**
3
+ * The header Bland delivers the webhook signature in.
4
+ * https://docs.bland.ai/tutorials/webhook-signing
5
+ */
6
+ export declare const BLAND_SIGNATURE_HEADER = "x-webhook-signature";
7
+ /**
8
+ * Verifies a Bland webhook request.
9
+ *
10
+ * Bland signs each webhook with an HMAC-SHA256 of the body using the account's
11
+ * webhook secret, hex-encoded in `X-Webhook-Signature`. Bland's own example
12
+ * verifies against `JSON.stringify(req.body)`, the re-serialized body, so a
13
+ * signature over either the raw bytes or that re-serialization is accepted:
14
+ * both need the secret to produce. The docs describe no timestamp, so a
15
+ * captured request can be replayed; the signature proves who sent it, not when.
16
+ * https://docs.bland.ai/tutorials/webhook-signing
17
+ *
18
+ * @returns `true` if the request is authorized: either no signing secret is
19
+ * configured (verification disabled) or the signature is present and valid.
20
+ */
21
+ export declare const isWebhookRequestAuthorized: ({ signingSecret, rawBody, payload, headers, }: {
22
+ signingSecret: string | undefined;
23
+ rawBody: Buffer;
24
+ payload: unknown;
25
+ headers: IncomingHttpHeaders;
26
+ }) => boolean;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isWebhookRequestAuthorized = exports.BLAND_SIGNATURE_HEADER = void 0;
4
+ const crypto_1 = require("crypto");
5
+ const lodash_1 = require("lodash");
6
+ /**
7
+ * The header Bland delivers the webhook signature in.
8
+ * https://docs.bland.ai/tutorials/webhook-signing
9
+ */
10
+ exports.BLAND_SIGNATURE_HEADER = 'x-webhook-signature';
11
+ const hmacHex = (secret, data) => (0, crypto_1.createHmac)('sha256', secret).update(data).digest('hex');
12
+ /** Constant-time comparison; `timingSafeEqual` throws on different lengths. */
13
+ const sameHex = (expected, received) => {
14
+ const a = Buffer.from(expected.toLowerCase());
15
+ const b = Buffer.from(received.trim().toLowerCase());
16
+ return a.length === b.length && (0, crypto_1.timingSafeEqual)(a, b);
17
+ };
18
+ /**
19
+ * Verifies a Bland webhook request.
20
+ *
21
+ * Bland signs each webhook with an HMAC-SHA256 of the body using the account's
22
+ * webhook secret, hex-encoded in `X-Webhook-Signature`. Bland's own example
23
+ * verifies against `JSON.stringify(req.body)`, the re-serialized body, so a
24
+ * signature over either the raw bytes or that re-serialization is accepted:
25
+ * both need the secret to produce. The docs describe no timestamp, so a
26
+ * captured request can be replayed; the signature proves who sent it, not when.
27
+ * https://docs.bland.ai/tutorials/webhook-signing
28
+ *
29
+ * @returns `true` if the request is authorized: either no signing secret is
30
+ * configured (verification disabled) or the signature is present and valid.
31
+ */
32
+ const isWebhookRequestAuthorized = ({ signingSecret, rawBody, payload, headers, }) => {
33
+ const secret = signingSecret === null || signingSecret === void 0 ? void 0 : signingSecret.trim();
34
+ // When no signing secret is configured, signature verification is disabled.
35
+ if ((0, lodash_1.isNil)(secret) || secret.length === 0) {
36
+ return true;
37
+ }
38
+ const header = headers[exports.BLAND_SIGNATURE_HEADER];
39
+ const signature = Array.isArray(header) ? header[0] : header;
40
+ if ((0, lodash_1.isNil)(signature) || signature.length === 0) {
41
+ return false;
42
+ }
43
+ if (sameHex(hmacHex(secret, rawBody), signature)) {
44
+ return true;
45
+ }
46
+ return (payload !== undefined &&
47
+ sameHex(hmacHex(secret, JSON.stringify(payload)), signature));
48
+ };
49
+ exports.isWebhookRequestAuthorized = isWebhookRequestAuthorized;
50
+ //# sourceMappingURL=verifyWebhookSignature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifyWebhookSignature.js","sourceRoot":"","sources":["../../../../extensions/bland/webhooks/verifyWebhookSignature.ts"],"names":[],"mappings":";;;AAAA,mCAAoD;AAEpD,mCAA8B;AAE9B;;;GAGG;AACU,QAAA,sBAAsB,GAAG,qBAAqB,CAAA;AAE3D,MAAM,OAAO,GAAG,CAAC,MAAc,EAAE,IAAqB,EAAU,EAAE,CAChE,IAAA,mBAAU,EAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAEzD,+EAA+E;AAC/E,MAAM,OAAO,GAAG,CAAC,QAAgB,EAAE,QAAgB,EAAW,EAAE;IAC9D,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAA;IAC7C,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAA;IACpD,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,IAAA,wBAAe,EAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AACvD,CAAC,CAAA;AAED;;;;;;;;;;;;;GAaG;AACI,MAAM,0BAA0B,GAAG,CAAC,EACzC,aAAa,EACb,OAAO,EACP,OAAO,EACP,OAAO,GAMR,EAAW,EAAE;IACZ,MAAM,MAAM,GAAG,aAAa,aAAb,aAAa,uBAAb,aAAa,CAAE,IAAI,EAAE,CAAA;IACpC,4EAA4E;IAC5E,IAAI,IAAA,cAAK,EAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzC,OAAO,IAAI,CAAA;IACb,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,8BAAsB,CAAC,CAAA;IAC9C,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;IAC5D,IAAI,IAAA,cAAK,EAAC,SAAS,CAAC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/C,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC;QACjD,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,CACL,OAAO,KAAK,SAAS;QACrB,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,SAAS,CAAC,CAC7D,CAAA;AACH,CAAC,CAAA;AA9BY,QAAA,0BAA0B,8BA8BtC"}
@@ -20,8 +20,8 @@
20
20
  "changelog": "# CHANGELOG"
21
21
  },
22
22
  "bland": {
23
- "readme": "---\ntitle: Bland.ai\ndescription: To do\n---\n\n# Bland.ai\n\nWith Bland.ai you can build, test, and deploy AI phone calling agents. \n\n## Extension settings\n\nTo set up this extension, you will need to provide an API key for Bland.\n\n## Actions\n\n### Send call (with Pathway)\n\nInitiates an AI phone call using a simple prompt or a conversational pathway agent built on the Bland platform. This action is asynchronous—it triggers a call request in Bland, which queues the call. Note that the action only confirms that Bland has received the request; it does not wait for the call to complete or handle any callbacks from Bland.\n\nAwell lets you specify both the data you want to send to Bland and the structured data you’d like to receive in return. This is achieved by defining the request data for outgoing information and an analysis schema for the structured data you’ll get back.\n\n#### Request data\n\nAny JSON you put in here will be visible to the AI agent during the call - and can also be referenced with Prompt Variables.\n\nFor example, let’s say in the action you want to programmatically set the name of the person you’re calling. You could set request data to:\n\n```json\n{\n \"name\": \"John Doe\",\n}\n```\n\n[Also see Bland documentation.](https://docs.bland.ai/api-v1/post/calls)\n\n#### Analysis schema\n\nDefine a JSON schema for how you want to get information about the call - information like email addresses, names, appointment times or any other type of custom data.\n\nFor example, if you wanted to retrieve this information from the call:\n\n```json\n{\n \"email_address\": \"email\",\n \"first_name\": \"string\",\n \"last_name\": \"string\",\n \"wants_to_book_appointment\": \"boolean\",\n \"appointment_time\": \"YYYY-MM-DD HH:MM:SS\"\n}\n```\n\nWhen retrieving the call details, Bland will return data about the call in the following format:\n\n```json\n{\n \"analysis\": {\n \"email_address\": \"johndoe@gmail.com\",\n \"first_name\": \"John\",\n \"last_name\": \"Doe\",\n \"wants_to_book_appointment\": true,\n \"appointment_time\": \"2024-01-01 12:00:00\"\n }\n}\n```\n\n[Also see Bland documentation.](https://docs.bland.ai/api-v1/post/calls)\n\n### Get call details\n\nRetrieve the details of a call based on the provider call ID.",
24
- "changelog": "# CHANGELOG\n\n## Unreleased\n\n- Added `sendSms` action: send an SMS immediately via Bland `POST /v1/sms/send`. The agent message is optional; when omitted, the pathway configured on the agent number generates the message.\n- Added `createSmsConversation` action: seed an SMS conversation positioned at pathway state via Bland `POST /v1/sms/create` without sending a message.\n- Added optional timeout fields to `sendSms` (`time_out`, `timeout_message`, `warning_time`, `warning_message`): per-conversation overrides for Bland's silence-timeout flow.\n- Added `completedBlandText` webhook: triggered when a Bland SMS conversation ends. Maps conversation data (status, reason, transcript, pathway variables) to data points and resolves the patient via the `awell_patient_id` echoed back in metadata.\n"
23
+ "readme": "---\ntitle: Bland.ai\ndescription: To do\n---\n\n# Bland.ai\n\nWith Bland.ai you can build, test, and deploy AI phone calling agents. \n\n## Extension settings\n\nTo set up this extension, you will need to provide an API key for Bland.\n\nOptionally, add the **Webhook signing secret** to have Awell verify that webhooks really come from Bland. Create it in the Bland dev portal under Account Settings, Keys tab, \"Replace Secret\" (it is shown only once). Bland then signs each webhook with an HMAC-SHA256 of the body using the secret and sends the hex digest in the `X-Webhook-Signature` header. Awell recomputes it and rejects a webhook with no signature or a wrong one with a `401`. When the setting is empty, webhooks are not verified.\n\nThe signature proves who sent a webhook, not when: Bland's documentation describes no timestamp, so a captured request could be sent again. See [Bland's webhook signing guide](https://docs.bland.ai/tutorials/webhook-signing).\n\n## Actions\n\n### Send call (with Pathway)\n\nInitiates an AI phone call using a simple prompt or a conversational pathway agent built on the Bland platform. This action is asynchronous—it triggers a call request in Bland, which queues the call. Note that the action only confirms that Bland has received the request; it does not wait for the call to complete or handle any callbacks from Bland.\n\nAwell lets you specify both the data you want to send to Bland and the structured data you’d like to receive in return. This is achieved by defining the request data for outgoing information and an analysis schema for the structured data you’ll get back.\n\n#### Request data\n\nAny JSON you put in here will be visible to the AI agent during the call - and can also be referenced with Prompt Variables.\n\nFor example, let’s say in the action you want to programmatically set the name of the person you’re calling. You could set request data to:\n\n```json\n{\n \"name\": \"John Doe\",\n}\n```\n\n[Also see Bland documentation.](https://docs.bland.ai/api-v1/post/calls)\n\n#### Analysis schema\n\nDefine a JSON schema for how you want to get information about the call - information like email addresses, names, appointment times or any other type of custom data.\n\nFor example, if you wanted to retrieve this information from the call:\n\n```json\n{\n \"email_address\": \"email\",\n \"first_name\": \"string\",\n \"last_name\": \"string\",\n \"wants_to_book_appointment\": \"boolean\",\n \"appointment_time\": \"YYYY-MM-DD HH:MM:SS\"\n}\n```\n\nWhen retrieving the call details, Bland will return data about the call in the following format:\n\n```json\n{\n \"analysis\": {\n \"email_address\": \"johndoe@gmail.com\",\n \"first_name\": \"John\",\n \"last_name\": \"Doe\",\n \"wants_to_book_appointment\": true,\n \"appointment_time\": \"2024-01-01 12:00:00\"\n }\n}\n```\n\n[Also see Bland documentation.](https://docs.bland.ai/api-v1/post/calls)\n\n### Get call details\n\nRetrieve the details of a call based on the provider call ID.",
24
+ "changelog": "# CHANGELOG\n\n## Unreleased\n\n- Added the optional **Webhook signing secret** setting. When set, the `callCompleted` and `completedBlandText` webhooks verify Bland's `X-Webhook-Signature` header (an HMAC-SHA256 of the body, hex-encoded) and reject a request with no signature or a wrong one with a `401`. When empty, nothing changes: webhooks are not verified, as before.\n- Added `sendSms` action: send an SMS immediately via Bland `POST /v1/sms/send`. The agent message is optional; when omitted, the pathway configured on the agent number generates the message.\n- Added `createSmsConversation` action: seed an SMS conversation positioned at pathway state via Bland `POST /v1/sms/create` without sending a message.\n- Added optional timeout fields to `sendSms` (`time_out`, `timeout_message`, `warning_time`, `warning_message`): per-conversation overrides for Bland's silence-timeout flow.\n- Added `completedBlandText` webhook: triggered when a Bland SMS conversation ends. Maps conversation data (status, reason, transcript, pathway variables) to data points and resolves the patient via the `awell_patient_id` echoed back in metadata.\n"
25
25
  },
26
26
  "braze": {
27
27
  "readme": "---\ntitle: Braze\ndescription: Braze is a customer engagement platform that helps you communicate with your patients.\n---\n\n# Braze\n\nBraze is a comprehensive customer engagement platform that specializes in helping businesses connect with their users through personalized messaging channels.\n\n## Extension settings\n\nAn api key and the REST Endpoint URL are needed to be provided in order for the extension to authenticate with Braze's API. \n\nAn app ID can also be specified via the extension settings or the action field. Actions requiring an app ID to be provided will throw an error if no app ID is provided via the extension settings or the action field. If both are provided, the app ID from the action field will be used.\n\nList of URLs can be found [here](https://www.braze.com/docs/api/basics/#endpoints) - make sure it's the REST Endpoint URL. Documentation on API keys can be found [here](https://www.braze.com/docs/api/basics/#about-rest-api-keys).\n\n## Supported Actins\n\n### Send SMS\n\nSend an immediate SMS message to a specified user\n\nRequired permissions: `messages.send`\n\n### Schedule SMS\n\nSchedule an SMS message to be sent at a specific time\n\nRequired permissions: `messages.schedule.create`\n\n### Send Email\n\nSend an immediate email to a specified user\n\nRequired permissions: `messages.send`\n\n### Schedule Email\n\nSchedule an email to be sent at a specific time\n\nRequired permissions: `messages.schedule.create`\n\n### Send Email Using Template\n\nSend an email using a pre-defined Braze template\n\nRequired permissions: `messages.send`\n\n### Send campaign\n\nSend a campaign message.\n\nRequired permissions: `campaigns.trigger.send`\n",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awell-health/awell-extensions",
3
- "version": "2.1.205",
3
+ "version": "2.1.206",
4
4
  "packageManager": "yarn@4.5.3",
5
5
  "main": "dist/src/index.js",
6
6
  "repository": {