@curviate/sdk 0.18.0 → 0.19.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/CHANGELOG.md CHANGED
@@ -9,6 +9,79 @@ Versioning: semantic — minor for additive changes, patch for bug fixes; no sta
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [0.19.0] - 2026-08-03
13
+
14
+ ### Fixed
15
+
16
+ - **`constructEvent` now parses real webhook deliveries. It previously rejected
17
+ every single one.** The Curviate platform sends the event name in `event`;
18
+ the SDK required a `type` field that no delivery has ever contained, so
19
+ verification threw `WebhookSignatureError` on 100% of genuine webhooks. The
20
+ HMAC path was never at fault (a tampered body was correctly rejected as
21
+ `invalid_signature`); the parse step was. If webhook verification has never
22
+ worked for you, this is why, and this release fixes it.
23
+
24
+ - **A body problem no longer reports a header problem.** A payload whose
25
+ signature verifies but whose body cannot be parsed used to fail with
26
+ `reason: "malformed_header"`, pointing at the two things that had just been
27
+ proven correct: the header and the signing secret. Integrators read that and
28
+ rotated a secret that was never wrong. Those cases now report the new
29
+ `reason: "malformed_payload"`, and the message no longer mentions the header,
30
+ the signature, or the secret.
31
+
32
+ ### Changed
33
+
34
+ - **BREAKING (type-level), and it breaks no working code.** The `CurviateEvent`
35
+ discriminant is now `event`, not `type`:
36
+
37
+ ```diff
38
+ - if (event.type === "message.received") { ... }
39
+ + if (event.event === "message.received") { ... }
40
+ ```
41
+
42
+ Read the label carefully before deferring the upgrade: **there is no working
43
+ consumer of `event.type` to break.** `constructEvent` has never once returned
44
+ successfully for a real delivery, so no `event.type` branch has ever executed
45
+ in production. Any handler you have written behind it is unreachable code
46
+ today. `event.type` is now a compile error precisely so `tsc` points you at
47
+ every site that needs the one-word edit; nothing changes at runtime for code
48
+ that was previously working, because there was none.
49
+
50
+ `WebhookSignatureError["reason"]` gains the `"malformed_payload"` member. An
51
+ exhaustive `switch` over `reason` will need the new arm.
52
+
53
+ ### Added
54
+
55
+ - **`CurviateEventEnvelope`**: the per-delivery metadata now carried on every
56
+ event. `event.id` is the `wdl_…` delivery id (de-duplicate retries on it),
57
+ `event.webhook_id` is the registration, `event.delivered_at` is the attempt
58
+ timestamp. These are typed optional and are **not** required by the parser:
59
+ requiring an envelope field is what caused this defect, so nothing beyond the
60
+ discriminant can fail a verification again.
61
+
62
+ - Forward compatibility: a payload carrying `type` instead of `event` is
63
+ accepted and normalized onto `event`, so a future platform-side change to the
64
+ envelope cannot break verification a second time. `type` is never required.
65
+
66
+ - `test/webhooks.dispatcherEnvelope.test.ts` and
67
+ `test/fixtures/webhook-delivery.capture.json`: the regression test replays
68
+ bytes and a signature header **captured off the wire from the real dispatch
69
+ worker**, rather than constructing its own payload. The pre-existing suite was
70
+ green throughout this defect because it built its own `{ type: … }` fixtures;
71
+ a suite that constructs its own input cannot detect an emitter/parser
72
+ divergence, and that is what let this ship.
73
+
74
+ ## [0.18.1] — 2026-07-18
75
+
76
+ ### Changed
77
+
78
+ - **`companies.sendMessage(identifier, chatId, body)`** now takes the normal `2-…`
79
+ chat id that `companies.chats()` / `chat()` / `searchChats()` return; the endpoint
80
+ resolves the page mailbox internally from `identifier`. The previous send-ready
81
+ `COMPANY_` chat-id requirement is gone: pass the id straight from the company chat
82
+ reads. Method signature and response shape are unchanged (documentation/behavior
83
+ only).
84
+
12
85
  ## [0.18.0] — 2026-07-17
13
86
 
14
87
  ### Added
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # @curviate/sdk
2
2
 
3
- The official TypeScript SDK for the [Curviate API](https://docs.curviate.com) — agent-native
3
+ The official TypeScript SDK for the [Curviate API](https://docs.curviate.com). Agent-native
4
4
  LinkedIn infrastructure for AI engineers and agent builders.
5
5
 
6
- > **Status:** `0.16.0` — pre-1.0. Full v2 API parity; the surface is public but not yet stability-promised.
6
+ > **Status:** pre-1.0. Full v2 API parity; the surface is public but not yet stability-promised.
7
7
 
8
8
  ---
9
9
 
@@ -17,7 +17,7 @@ Requires Node 18+. Works in Cloudflare Workers and Vercel Edge.
17
17
 
18
18
  ---
19
19
 
20
- ## Auth — construct the client
20
+ ## Auth: construct the client
21
21
 
22
22
  Get your API key from the Curviate dashboard. Store it as an environment variable.
23
23
 
@@ -37,7 +37,7 @@ const curviate = new Curviate({
37
37
 
38
38
  ## Account-scoped accessor
39
39
 
40
- Every LinkedIn operation (messages, member profiles, invites, posts) is tied to a **managed account** — a LinkedIn session you have connected via the connect flow (`curviate.auth.intent()`). The `curviate.account(id)` accessor fixes the `account_id` on every call so you do not have to thread it manually:
40
+ Every LinkedIn operation (messages, member profiles, invites, posts) is tied to a **managed account**, a LinkedIn session you have connected via the connect flow (`curviate.auth.intent()`). The `curviate.account(id)` accessor fixes the `account_id` on every call so you do not have to thread it manually:
41
41
 
42
42
  ```ts
43
43
  // Root-level: tenant-wide operations (accounts, auth, webhooks)
@@ -56,7 +56,7 @@ const profile = await acc.users.get("some-user-id"); // someone else's
56
56
 
57
57
  ## First end-to-end call
58
58
 
59
- This snippet lists your connected accounts, picks the first one, and sends a message — something an agent might do to automate outreach:
59
+ This snippet lists your connected accounts, picks the first one, and sends a message, something an agent might do to automate outreach:
60
60
 
61
61
  ```ts
62
62
  import { Curviate, isCurviateError } from "@curviate/sdk";
@@ -78,7 +78,7 @@ async function sendFirstMessage() {
78
78
  // 3. Send a message to the first chat
79
79
  const chat = chats[0]!;
80
80
  await acc.messaging.sendMessage(chat.id ?? "", {
81
- text: "Hi — following up from our conversation.",
81
+ text: "Hi, following up from our conversation.",
82
82
  });
83
83
 
84
84
  console.log("Message sent.");
@@ -114,7 +114,7 @@ try {
114
114
  break;
115
115
  default:
116
116
  if (err.retryLikelyToSucceed) {
117
- // Safe to retry — server-side transient error
117
+ // Safe to retry: server-side transient error
118
118
  await retry();
119
119
  }
120
120
  }
@@ -127,7 +127,7 @@ All 34 error codes are documented in the [API reference](https://docs.curviate.c
127
127
 
128
128
  ## Cursor pagination
129
129
 
130
- Resources that return lists support cursor pagination. `curviate.paginate()` is an async iterator that follows the `cursor` field automatically — pull items one at a time without managing cursors:
130
+ Resources that return lists support cursor pagination. `curviate.paginate()` is an async iterator that follows the `cursor` field automatically, so you pull items one at a time without managing cursors:
131
131
 
132
132
  ```ts
133
133
  // Iterate over every chat across all pages
@@ -163,27 +163,28 @@ app.post("/webhook", express.raw({ type: "application/json" }), async (req, res)
163
163
  event = await constructEvent(req.body, sig, secret);
164
164
  } catch (err) {
165
165
  if (err instanceof WebhookSignatureError) {
166
- console.warn("Bad webhook:", err.reason); // 'invalid_signature' | 'replay_detected' | 'malformed_header'
166
+ // 'invalid_signature' | 'replay_detected' | 'malformed_header' | 'malformed_payload'
167
+ console.warn("Bad webhook:", err.reason);
167
168
  return res.sendStatus(400);
168
169
  }
169
170
  throw err;
170
171
  }
171
172
 
172
- switch (event.type) {
173
+ switch (event.event) {
173
174
  case "message.received":
174
- // event.data is MessagePayload — account_id, message_id, etc.
175
+ // event.data is MessagePayload: account_id, message_id, and so on.
175
176
  handleNewMessage(event.data);
176
177
  break;
177
178
  case "account.connected":
178
179
  handleAccountConnected(event.data);
179
180
  break;
180
- // 22 more event types — all in the CurviateEvent union
181
+ // 22 more event types, all in the CurviateEvent union.
181
182
  }
182
183
 
183
184
  res.sendStatus(200);
184
185
  });
185
186
 
186
- // Hono / Vercel Edge — always await (Web Crypto is async)
187
+ // Hono / Vercel Edge. Always await; Web Crypto is async.
187
188
  app.post("/webhook", async (c) => {
188
189
  const rawBody = await c.req.text();
189
190
  const event = await constructEvent(rawBody, c.req.header("curviate-signature")!, secret);
@@ -191,16 +192,28 @@ app.post("/webhook", async (c) => {
191
192
  });
192
193
  ```
193
194
 
194
- `constructEvent` always returns a `Promise<CurviateEvent>` — always `await` it.
195
+ `constructEvent` always returns a `Promise<CurviateEvent>`. Always `await` it.
195
196
 
196
- `WebhookSignatureError` is NOT a `CurviateError` — narrow with `instanceof WebhookSignatureError`.
197
+ Each event also carries its delivery metadata: `event.id` (a `wdl_` delivery id you
198
+ can de-duplicate retries on), `event.webhook_id`, and `event.delivered_at`.
199
+
200
+ `WebhookSignatureError` is NOT a `CurviateError`. Narrow with `instanceof WebhookSignatureError`.
201
+ Its `reason` tells you where to look: `malformed_header` means the signature header could not be
202
+ read, `invalid_signature` means the HMAC did not match (check the secret, and check that you passed
203
+ the raw request bytes), `replay_detected` means the event is outside the replay window, and
204
+ `malformed_payload` means the signature was **valid** but the body was not a Curviate event, so
205
+ your secret and header are both fine.
206
+
207
+ > **Upgrading from 0.18.x or earlier?** The event discriminant is `event.event`, not `event.type`.
208
+ > See the 0.19.0 entry in [CHANGELOG.md](./CHANGELOG.md); the change is a one-word edit and it
209
+ > breaks no working code, because `constructEvent` never returned successfully before 0.19.0.
197
210
 
198
211
  ---
199
212
 
200
213
  ## Links
201
214
 
202
215
  - API reference: https://docs.curviate.com
203
- - Issues: https://github.com/curviate/sdk/issues
216
+ - Issues: https://github.com/curviate/curviate-sdk/issues
204
217
  - Changelog: [CHANGELOG.md](./CHANGELOG.md)
205
218
 
206
219
  ## License
package/dist/index.d.ts CHANGED
@@ -28709,9 +28709,10 @@ declare class JobsResource {
28709
28709
  * busier inbox.
28710
28710
  *
28711
28711
  * `sendMessage()` is the inbox's one write: reply to an existing
28712
- * conversation AS THE PAGE. It requires the send-ready `COMPANY_` chat id
28713
- * from `inboxes.listChats()`, not the `2-…` id the reads above return.
28714
- * Reply-only, same admin requirement as the reads.
28712
+ * conversation AS THE PAGE. It takes the same `2-…` chat id that
28713
+ * `chats()` / `searchChats()` return; the endpoint resolves the page
28714
+ * mailbox internally from `identifier`. Reply-only, same admin
28715
+ * requirement as the reads.
28715
28716
  */
28716
28717
 
28717
28718
  type CompanyProfile = paths["/v1/{account_id}/companies/{identifier}"]["get"]["responses"]["200"]["content"]["application/json"];
@@ -28866,11 +28867,10 @@ declare class CompaniesResource {
28866
28867
  * Reply to a company-inbox conversation, AS THE PAGE.
28867
28868
  * `POST /v1/{account_id}/companies/{identifier}/chats/{chat_id}/messages`
28868
28869
  *
28869
- * `chatId` must be the send-ready `COMPANY_` chat id, not the `2-…` id
28870
- * `chats()`/`chat()`/`searchChats()` return. Get the `COMPANY_` id from
28871
- * `inboxes.listChats()` (`GET /v1/{account_id}/inboxes/{inbox_id}/chats`).
28872
- * A `2-…` or personal chat id is rejected with a guiding 400 that names
28873
- * the required form and its source.
28870
+ * `chatId` is the `2-…` conversation id that `chats()` / `chat()` /
28871
+ * `searchChats()` return for this page. The endpoint resolves the page
28872
+ * mailbox internally from `identifier`, so no special send-ready id is
28873
+ * needed: pass the id straight from the company chat reads.
28874
28874
  *
28875
28875
  * Reply-only: this method can only answer an existing conversation, it
28876
28876
  * cannot start a new one on the page's behalf. The connected account
@@ -28886,13 +28886,9 @@ declare class CompaniesResource {
28886
28886
  *
28887
28887
  * @example
28888
28888
  * const acc = curviate.account("acc_YOUR_ACCOUNT_ID");
28889
- * const { items: inboxes } = await acc.inboxes.list({ kind: "company" });
28890
- * const pageInbox = inboxes[0]!; // e.g. id "COMPANY_83734124_PRIMARY"
28891
- *
28892
- * const { items: chats } = await acc.inboxes.listChats(pageInbox.id);
28893
- * const chatId = chats[0]!.id as string; // e.g. "COMPANY_83734124_2-YTQ3ODU3Njgt"
28889
+ * const { items: chats } = await acc.companies.chats("112013061");
28894
28890
  *
28895
- * const result = await acc.companies.sendMessage("112013061", chatId, {
28891
+ * const result = await acc.companies.sendMessage("112013061", chats[0]!.id, {
28896
28892
  * text: "Thanks for reaching out, happy to help!",
28897
28893
  * });
28898
28894
  * console.log(result.sent_as); // { kind: "company", company_id: "112013061", name: "RedHire" }
@@ -29215,6 +29211,20 @@ declare function isCurviateError(err: unknown): err is CurviateError;
29215
29211
  * Extends `Error`, NOT `CurviateError` — callers can narrow with
29216
29212
  * `instanceof WebhookSignatureError` independently of CurviateError.
29217
29213
  *
29214
+ * The `reason` codes split by where the problem actually is, so the code you
29215
+ * read points at the thing you need to look at:
29216
+ *
29217
+ * - `malformed_header` - the `Curviate-Signature` header itself is unusable
29218
+ * (missing `t=` or `v1=`, non-numeric timestamp). Check how you are reading
29219
+ * the header.
29220
+ * - `invalid_signature` - the header parsed, but the HMAC does not match.
29221
+ * Check the signing secret, and check that you passed the raw request bytes
29222
+ * rather than a re-serialized object.
29223
+ * - `replay_detected` - signature valid, timestamp outside the replay window.
29224
+ * - `malformed_payload` - **signature valid**, but the body is not a Curviate
29225
+ * event. Nothing is wrong with your secret or your header; look at what is
29226
+ * POSTing to the endpoint.
29227
+ *
29218
29228
  * @example
29219
29229
  * try {
29220
29230
  * const event = await constructEvent(rawBody, header, secret);
@@ -29227,7 +29237,7 @@ declare function isCurviateError(err: unknown): err is CurviateError;
29227
29237
  declare class WebhookSignatureError extends Error {
29228
29238
  readonly name = "WebhookSignatureError";
29229
29239
  /** Structured reason code for the verification failure. */
29230
- readonly reason: "invalid_signature" | "replay_detected" | "malformed_header";
29240
+ readonly reason: "invalid_signature" | "replay_detected" | "malformed_header" | "malformed_payload";
29231
29241
  constructor(reason: WebhookSignatureError["reason"], message: string);
29232
29242
  }
29233
29243
  /**
@@ -29252,9 +29262,34 @@ interface AccountPayload {
29252
29262
  account_id: string;
29253
29263
  [key: string]: unknown;
29254
29264
  }
29265
+ /**
29266
+ * Delivery metadata that accompanies every Curviate webhook event.
29267
+ *
29268
+ * These fields are present on every delivery the platform sends, but
29269
+ * `constructEvent` does **not** require them: the parser insists only on the
29270
+ * discriminant. That is deliberate. Requiring an envelope field is exactly how
29271
+ * verification broke before (the parser demanded a field the emitter did not
29272
+ * send, and rejected every genuine delivery), so anything the union does not
29273
+ * strictly need to narrow is optional here and cannot fail a verification.
29274
+ */
29275
+ interface CurviateEventEnvelope {
29276
+ /** Unique id for this delivery attempt, e.g. `wdl_01J...`. Use it to de-duplicate retries. */
29277
+ id?: string;
29278
+ /** Id of the webhook registration this delivery belongs to, e.g. `wh_01J...`. */
29279
+ webhook_id?: string;
29280
+ /** ISO-8601 timestamp of the delivery attempt. */
29281
+ delivered_at?: string;
29282
+ }
29255
29283
  /**
29256
29284
  * The complete discriminated union of all 24 canonical, create-subscribable
29257
- * Curviate webhook events. The `type` field is the discriminant.
29285
+ * Curviate webhook events. The **`event`** field is the discriminant, matching
29286
+ * the field the platform actually sends on the wire.
29287
+ *
29288
+ * > Migrating from 0.18.x and earlier: the discriminant was `type`, a field no
29289
+ * > Curviate delivery has ever contained, so `constructEvent` threw on every
29290
+ * > real webhook and no `event.type` branch could ever run. Switch your checks
29291
+ * > to `event.event === '...'`. `event.type` is now a compile error, which is
29292
+ * > intentional: it points at every site that needs the one-word change.
29258
29293
  *
29259
29294
  * Re-keyed for the v2 catalogue (was 19). Renamed/removed vs. the prior set:
29260
29295
  * `account.stopped`, `account.sync_started`, `account.sync_complete`,
@@ -29269,83 +29304,83 @@ interface AccountPayload {
29269
29304
  *
29270
29305
  * @example
29271
29306
  * const event = await constructEvent(rawBody, header, secret);
29272
- * if (event.type === 'message.received') {
29307
+ * if (event.event === 'message.received') {
29273
29308
  * // event.data is MessagePayload
29274
29309
  * }
29275
29310
  */
29276
- type CurviateEvent = {
29277
- type: "message.received";
29311
+ type CurviateEvent = CurviateEventEnvelope & ({
29312
+ event: "message.received";
29278
29313
  data: MessagePayload;
29279
29314
  } | {
29280
- type: "message.delivered";
29315
+ event: "message.delivered";
29281
29316
  data: MessagePayload;
29282
29317
  } | {
29283
- type: "message.read";
29318
+ event: "message.read";
29284
29319
  data: MessagePayload;
29285
29320
  } | {
29286
- type: "message.edited";
29321
+ event: "message.edited";
29287
29322
  data: MessagePayload;
29288
29323
  } | {
29289
- type: "message.deleted";
29324
+ event: "message.deleted";
29290
29325
  data: MessagePayload;
29291
29326
  } | {
29292
- type: "message.reaction";
29327
+ event: "message.reaction";
29293
29328
  data: MessagePayload;
29294
29329
  } | {
29295
- type: "chat.updated";
29330
+ event: "chat.updated";
29296
29331
  data: MessagePayload;
29297
29332
  } | {
29298
- type: "chat.deleted";
29333
+ event: "chat.deleted";
29299
29334
  data: MessagePayload;
29300
29335
  } | {
29301
- type: "connection.accepted";
29336
+ event: "connection.accepted";
29302
29337
  data: ConnectionPayload;
29303
29338
  } | {
29304
- type: "connection.new";
29339
+ event: "connection.new";
29305
29340
  data: ConnectionPayload;
29306
29341
  } | {
29307
- type: "account.created";
29342
+ event: "account.created";
29308
29343
  data: AccountPayload;
29309
29344
  } | {
29310
- type: "account.connected";
29345
+ event: "account.connected";
29311
29346
  data: AccountPayload;
29312
29347
  } | {
29313
- type: "account.synced";
29348
+ event: "account.synced";
29314
29349
  data: AccountPayload;
29315
29350
  } | {
29316
- type: "account.reconnected";
29351
+ event: "account.reconnected";
29317
29352
  data: AccountPayload;
29318
29353
  } | {
29319
- type: "account.reconnect_needed";
29354
+ event: "account.reconnect_needed";
29320
29355
  data: AccountPayload;
29321
29356
  } | {
29322
- type: "account.creation_failed";
29357
+ event: "account.creation_failed";
29323
29358
  data: AccountPayload;
29324
29359
  } | {
29325
- type: "account.disconnected";
29360
+ event: "account.disconnected";
29326
29361
  data: AccountPayload;
29327
29362
  } | {
29328
- type: "account.error";
29363
+ event: "account.error";
29329
29364
  data: AccountPayload;
29330
29365
  } | {
29331
- type: "account.paused";
29366
+ event: "account.paused";
29332
29367
  data: AccountPayload;
29333
29368
  } | {
29334
- type: "account.connecting";
29369
+ event: "account.connecting";
29335
29370
  data: AccountPayload;
29336
29371
  } | {
29337
- type: "account.permission_revoked";
29372
+ event: "account.permission_revoked";
29338
29373
  data: AccountPayload;
29339
29374
  } | {
29340
- type: "account.initial_sync.running";
29375
+ event: "account.initial_sync.running";
29341
29376
  data: AccountPayload;
29342
29377
  } | {
29343
- type: "account.initial_sync.completed";
29378
+ event: "account.initial_sync.completed";
29344
29379
  data: AccountPayload;
29345
29380
  } | {
29346
- type: "account.initial_sync.failed";
29381
+ event: "account.initial_sync.failed";
29347
29382
  data: AccountPayload;
29348
- };
29383
+ });
29349
29384
  /**
29350
29385
  * Options for {@link constructEvent}.
29351
29386
  */
@@ -29390,7 +29425,7 @@ interface ConstructEventOptions {
29390
29425
  * }
29391
29426
  * throw err;
29392
29427
  * }
29393
- * if (event.type === 'message.received') { ... }
29428
+ * if (event.event === 'message.received') { ... }
29394
29429
  * res.sendStatus(200);
29395
29430
  * });
29396
29431
  *
@@ -29404,4 +29439,4 @@ interface ConstructEventOptions {
29404
29439
  */
29405
29440
  declare function constructEvent(rawBody: string | Buffer, signatureHeader: string, secret: string, opts?: ConstructEventOptions): Promise<CurviateEvent>;
29406
29441
 
29407
- export { type AccountListPage, type AccountListParams, type AccountPayload, type AccountScopedNamespaces, type ConnectionPayload, type ConstructEventOptions, Curviate, type CurviateConfig, CurviateError, type CurviateErrorInit, type CurviateErrorJSON, type CurviateEvent, type ErrorCode, type MessagePayload, type RequiredTier, type ResolvedConfig, type ResourceNamespaces, type RetryHint, WebhookSignatureError, type components, constructEvent, isCurviateError, type operations, type paths };
29442
+ export { type AccountListPage, type AccountListParams, type AccountPayload, type AccountScopedNamespaces, type ConnectionPayload, type ConstructEventOptions, Curviate, type CurviateConfig, CurviateError, type CurviateErrorInit, type CurviateErrorJSON, type CurviateEvent, type CurviateEventEnvelope, type ErrorCode, type MessagePayload, type RequiredTier, type ResolvedConfig, type ResourceNamespaces, type RetryHint, WebhookSignatureError, type components, constructEvent, isCurviateError, type operations, type paths };
package/dist/index.js CHANGED
@@ -2375,11 +2375,10 @@ var CompaniesResource = class {
2375
2375
  * Reply to a company-inbox conversation, AS THE PAGE.
2376
2376
  * `POST /v1/{account_id}/companies/{identifier}/chats/{chat_id}/messages`
2377
2377
  *
2378
- * `chatId` must be the send-ready `COMPANY_` chat id, not the `2-…` id
2379
- * `chats()`/`chat()`/`searchChats()` return. Get the `COMPANY_` id from
2380
- * `inboxes.listChats()` (`GET /v1/{account_id}/inboxes/{inbox_id}/chats`).
2381
- * A `2-…` or personal chat id is rejected with a guiding 400 that names
2382
- * the required form and its source.
2378
+ * `chatId` is the `2-…` conversation id that `chats()` / `chat()` /
2379
+ * `searchChats()` return for this page. The endpoint resolves the page
2380
+ * mailbox internally from `identifier`, so no special send-ready id is
2381
+ * needed: pass the id straight from the company chat reads.
2383
2382
  *
2384
2383
  * Reply-only: this method can only answer an existing conversation, it
2385
2384
  * cannot start a new one on the page's behalf. The connected account
@@ -2395,13 +2394,9 @@ var CompaniesResource = class {
2395
2394
  *
2396
2395
  * @example
2397
2396
  * const acc = curviate.account("acc_YOUR_ACCOUNT_ID");
2398
- * const { items: inboxes } = await acc.inboxes.list({ kind: "company" });
2399
- * const pageInbox = inboxes[0]!; // e.g. id "COMPANY_83734124_PRIMARY"
2400
- *
2401
- * const { items: chats } = await acc.inboxes.listChats(pageInbox.id);
2402
- * const chatId = chats[0]!.id as string; // e.g. "COMPANY_83734124_2-YTQ3ODU3Njgt"
2397
+ * const { items: chats } = await acc.companies.chats("112013061");
2403
2398
  *
2404
- * const result = await acc.companies.sendMessage("112013061", chatId, {
2399
+ * const result = await acc.companies.sendMessage("112013061", chats[0]!.id, {
2405
2400
  * text: "Thanks for reaching out, happy to help!",
2406
2401
  * });
2407
2402
  * console.log(result.sent_as); // { kind: "company", company_id: "112013061", name: "RedHire" }
@@ -2689,12 +2684,27 @@ function verifyAndParse(computedHex, v1, timestamp, bodyStr, replayWindowSecs) {
2689
2684
  try {
2690
2685
  parsed = JSON.parse(bodyStr);
2691
2686
  } catch {
2692
- throw new WebhookSignatureError("malformed_header", "Webhook body is not valid JSON.");
2687
+ throw new WebhookSignatureError(
2688
+ "malformed_payload",
2689
+ "Webhook body is not valid JSON. Verification already succeeded, so the problem is the request body, not your webhook configuration."
2690
+ );
2693
2691
  }
2694
- if (typeof parsed !== "object" || parsed === null || typeof parsed["type"] !== "string") {
2695
- throw new WebhookSignatureError("malformed_header", 'Webhook payload missing "type" field.');
2692
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
2693
+ throw new WebhookSignatureError(
2694
+ "malformed_payload",
2695
+ "Webhook body is not a JSON object. Verification already succeeded, so the problem is the request body, not your webhook configuration."
2696
+ );
2697
+ }
2698
+ const record = parsed;
2699
+ const discriminant = typeof record["event"] === "string" ? record["event"] : typeof record["type"] === "string" ? record["type"] : void 0;
2700
+ if (discriminant === void 0) {
2701
+ throw new WebhookSignatureError(
2702
+ "malformed_payload",
2703
+ 'Webhook body has no "event" field, so it cannot be resolved to a Curviate event. Verification already succeeded, so the problem is the request body, not your webhook configuration.'
2704
+ );
2696
2705
  }
2697
- return parsed;
2706
+ record["event"] = discriminant;
2707
+ return record;
2698
2708
  }
2699
2709
  async function constructEvent(rawBody, signatureHeader, secret, opts) {
2700
2710
  const replayWindowSecs = opts?.replayWindowSecs ?? 300;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@curviate/sdk",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "private": false,
5
5
  "description": "TypeScript SDK for the Curviate API.",
6
6
  "license": "MIT",
@@ -8,10 +8,10 @@
8
8
  "homepage": "https://docs.curviate.com",
9
9
  "repository": {
10
10
  "type": "git",
11
- "url": "git+https://github.com/curviate/sdk.git"
11
+ "url": "git+https://github.com/curviate/curviate-sdk.git"
12
12
  },
13
13
  "bugs": {
14
- "url": "https://github.com/curviate/sdk/issues"
14
+ "url": "https://github.com/curviate/curviate-sdk/issues"
15
15
  },
16
16
  "publishConfig": {
17
17
  "access": "public"