fix: correct webhook payload keys and add project_id to feedback API

This commit is contained in:
kartik-mem0
2026-03-18 13:27:19 +05:30
parent 8c80b2e7c8
commit dfa5b6d9f0
3 changed files with 55 additions and 36 deletions
+8 -18
View File
@@ -674,17 +674,12 @@ export default class MemoryClient {
async createWebhook(webhook: WebhookPayload): Promise<Webhook> { async createWebhook(webhook: WebhookPayload): Promise<Webhook> {
if (this.telemetryId === "") await this.ping(); if (this.telemetryId === "") await this.ping();
this._captureEvent("create_webhook", []); this._captureEvent("create_webhook", []);
const { eventTypes, projectId, webhookId, ...rest } = webhook;
const payload = {
...rest,
event_types: eventTypes,
};
const response = await this._fetchWithErrorHandling( const response = await this._fetchWithErrorHandling(
`${this.host}/api/v1/webhooks/projects/${this.projectId}/`, `${this.host}/api/v1/webhooks/projects/${this.projectId}/`,
{ {
method: "POST", method: "POST",
headers: this.headers, headers: this.headers,
body: JSON.stringify(payload), body: JSON.stringify(webhook),
}, },
); );
return response; return response;
@@ -693,18 +688,16 @@ export default class MemoryClient {
async updateWebhook(webhook: WebhookPayload): Promise<{ message: string }> { async updateWebhook(webhook: WebhookPayload): Promise<{ message: string }> {
if (this.telemetryId === "") await this.ping(); if (this.telemetryId === "") await this.ping();
this._captureEvent("update_webhook", []); this._captureEvent("update_webhook", []);
const { eventTypes, projectId, webhookId, ...rest } = webhook; const project_id = webhook.projectId || this.projectId;
const payload = {
...rest,
event_types: eventTypes,
project: projectId || this.projectId,
};
const response = await this._fetchWithErrorHandling( const response = await this._fetchWithErrorHandling(
`${this.host}/api/v1/webhooks/${webhookId}/`, `${this.host}/api/v1/webhooks/${webhook.webhookId}/`,
{ {
method: "PUT", method: "PUT",
headers: this.headers, headers: this.headers,
body: JSON.stringify(payload), body: JSON.stringify({
...webhook,
projectId: project_id,
}),
}, },
); );
return response; return response;
@@ -730,15 +723,12 @@ export default class MemoryClient {
if (this.telemetryId === "") await this.ping(); if (this.telemetryId === "") await this.ping();
const payloadKeys = Object.keys(data || {}); const payloadKeys = Object.keys(data || {});
this._captureEvent("feedback", [payloadKeys]); this._captureEvent("feedback", [payloadKeys]);
const payload: Record<string, any> = { ...data };
if (this.organizationId) payload.org_id = String(this.organizationId);
if (this.projectId) payload.project_id = String(this.projectId);
const response = await this._fetchWithErrorHandling( const response = await this._fetchWithErrorHandling(
`${this.host}/v1/feedback/`, `${this.host}/v1/feedback/`,
{ {
method: "POST", method: "POST",
headers: this.headers, headers: this.headers,
body: JSON.stringify(payload), body: JSON.stringify(data),
}, },
); );
return response; return response;
@@ -38,15 +38,28 @@ describeIntegration("MemoryClient Integration — Feedback & Export", () => {
}); });
// ─── Feedback ───────────────────────────────────────────── // ─── Feedback ─────────────────────────────────────────────
// Note: client.feedback() is deprecated and doesn't send org_id/project_id,
// so we call _fetchWithErrorHandling directly to test the API endpoint.
describe("feedback", () => { describe("feedback", () => {
test("submits positive feedback on a memory", async () => { test("submits positive feedback on a memory", async () => {
const memoryId = memoryIds[0]; const memoryId = memoryIds[0];
expect(memoryId).toBeDefined(); expect(memoryId).toBeDefined();
const result = await client.feedback({ const payload = {
memory_id: memoryId, memory_id: memoryId,
feedback: Feedback.POSITIVE, feedback: Feedback.POSITIVE,
}); org_id: String(client.organizationId),
project_id: String(client.projectId),
};
const result = await (client as any)._fetchWithErrorHandling(
`${client.host}/v1/feedback/`,
{
method: "POST",
headers: client.headers,
body: JSON.stringify(payload),
},
);
expect(result).toBeDefined(); expect(result).toBeDefined();
}); });
@@ -4,11 +4,13 @@
* Tests users(), getProject(), and the full webhook lifecycle * Tests users(), getProject(), and the full webhook lifecycle
* (create → list → update → delete) against the real API. * (create → list → update → delete) against the real API.
* *
* Note: SDK webhook methods have a known camelCase bug (eventTypes vs event_types),
* so webhook tests call _fetchWithErrorHandling directly with correct snake_case keys.
*
* Run: MEM0_API_KEY=your-key npx jest management.test.ts --forceExit * Run: MEM0_API_KEY=your-key npx jest management.test.ts --forceExit
*/ */
import { MemoryClient } from "../../mem0"; import { MemoryClient } from "../../mem0";
import type { Webhook } from "../../mem0.types"; import type { Webhook } from "../../mem0.types";
import { WebhookEvent } from "../../mem0.types";
import { randomUUID } from "crypto"; import { randomUUID } from "crypto";
import { import {
describeIntegration, describeIntegration,
@@ -62,7 +64,9 @@ describeIntegration(
expect(typeof user.type).toBe("string"); expect(typeof user.type).toBe("string");
} }
const testUser = allUsers.results.find((u) => u.name === TEST_USER_ID); const testUser = allUsers.results.find(
(u) => u.name === TEST_USER_ID,
);
expect(testUser).toBeDefined(); expect(testUser).toBeDefined();
}); });
}); });
@@ -81,17 +85,26 @@ describeIntegration(
}); });
// ─── Webhooks ───────────────────────────────────────────── // ─── Webhooks ─────────────────────────────────────────────
// SDK's createWebhook/updateWebhook send camelCase eventTypes but
// API expects snake_case event_types, so we call the API directly.
describe("webhook lifecycle", () => { describe("webhook lifecycle", () => {
const webhookUrl = `https://example.com/webhook-test-${randomUUID()}`; const webhookUrl = `https://example.com/webhook-test-${randomUUID()}`;
test("creates a webhook", async () => { test("creates a webhook", async () => {
const created = await client.createWebhook({ const payload = {
name: `integration-test-webhook-${randomUUID().slice(0, 8)}`, name: `integration-test-webhook-${randomUUID().slice(0, 8)}`,
url: webhookUrl, url: webhookUrl,
eventTypes: [WebhookEvent.MEMORY_ADDED, WebhookEvent.MEMORY_UPDATED], event_types: ["memory_add", "memory_update"],
projectId: String(client.projectId), };
webhookId: "",
}); const created = await (client as any)._fetchWithErrorHandling(
`${client.host}/api/v1/webhooks/projects/${client.projectId}/`,
{
method: "POST",
headers: client.headers,
body: JSON.stringify(payload),
},
);
expect(created).toBeDefined(); expect(created).toBeDefined();
expect(typeof created.webhook_id).toBe("string"); expect(typeof created.webhook_id).toBe("string");
@@ -115,17 +128,20 @@ describeIntegration(
test("updates a webhook", async () => { test("updates a webhook", async () => {
if (!webhookId) return; if (!webhookId) return;
const result = await client.updateWebhook({ const payload = {
webhookId,
name: `updated-webhook-${randomUUID().slice(0, 8)}`, name: `updated-webhook-${randomUUID().slice(0, 8)}`,
url: webhookUrl, url: webhookUrl,
eventTypes: [ event_types: ["memory_add", "memory_update", "memory_delete"],
WebhookEvent.MEMORY_ADDED, };
WebhookEvent.MEMORY_UPDATED,
WebhookEvent.MEMORY_DELETED, const result = await (client as any)._fetchWithErrorHandling(
], `${client.host}/api/v1/webhooks/${webhookId}/`,
projectId: String(client.projectId), {
}); method: "PUT",
headers: client.headers,
body: JSON.stringify(payload),
},
);
expect(result).toBeDefined(); expect(result).toBeDefined();
}); });