diff --git a/docs/docs.json b/docs/docs.json index f0d3df154a..3ddd80a15d 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -45,7 +45,8 @@ "features/custom-instructions", "features/direct-import", "features/async-client", - "features/memory-export" + "features/memory-export", + "features/webhook" ] } ] diff --git a/docs/features/webhook.mdx b/docs/features/webhook.mdx new file mode 100644 index 0000000000..6adac8c599 --- /dev/null +++ b/docs/features/webhook.mdx @@ -0,0 +1,223 @@ +--- +title: Webhooks +description: 'Configure and manage webhooks to receive real-time notifications about memory events' +icon: "webhook" +iconType: "solid" +--- + +## Overview + +Webhooks allow you to receive real-time notifications when memory events occur in your Mem0 project. Webhooks are configured at the project level, meaning each webhook is associated with a specific project and will only receive events from that project. You can configure webhooks to send HTTP POST requests to your specified URLs whenever memories are created, updated, or deleted. + +## Managing Webhooks + +### Get Webhooks + +Retrieve all webhooks configured for your project. By default, it uses the project_id from your client configuration, but you can optionally specify a different project: + + + +```python Python +from mem0 import MemoryClient + +client = MemoryClient(api_key="your-api-key") + +# Get webhooks for the default project +webhooks = client.get_webhooks() + +# Or specify a different project +webhooks = client.get_webhooks(project_id="proj_123") +print(webhooks) +``` + +```javascript JavaScript +const { MemoryClient } = require('mem0ai'); +const client = new MemoryClient('your-api-key'); + +// Get webhooks for the default project +const webhooks = await client.getWebhooks(); + +// Or specify a different project +const webhooks = await client.getWebhooks("proj_123"); +console.log(webhooks); +``` + +```json Output +[ + { + 'id': 6, + 'url': 'https://mem0.ai', + 'name': 'mem0', + 'owner': 'john', + 'project': 'default-project', + 'secret': '3254dde069fe2490216dba7ca4e0f3595619a28e5909ba80d45caed7577f1040', + 'is_active': True, + 'created_at': '2025-02-18T22:59:56.804993-08:00', + 'updated_at': '2025-02-18T23:06:41.479361-08:00' + } +] + +``` + + + +### Create Webhook + +Create a new webhook for your project. The webhook will only receive events from the specified project: + + + +```python Python +# Create a webhook in the default project +webhook = client.create_webhook( + url="https://your-app.com/webhook", + name="Memory Logger" +) + +# Or create in a specific project +webhook = client.create_webhook( + url="https://your-app.com/webhook", + name="Memory Logger", + project_id="proj_123" +) +print(webhook) +``` + +```javascript JavaScript +// Create a webhook in the default project +const webhook = await client.createWebhook({ + url: "https://your-app.com/webhook", + name: "Memory Logger" +}); + +// Or create in a specific project +const webhook = await client.createWebhook({ + url: "https://your-app.com/webhook", + name: "Memory Logger", + projectId: "proj_123" +}); +console.log(webhook); +``` + +```json Output +{ + 'id': 1, + 'name': 'Memory Logger', + 'url': 'https://your-app.com/webhook', + 'project': 'default-project', + 'secret': '3254dde069fe2490216dba7ca4e0f3595619a28e5909ba80d45caed7577f1040', + 'is_active': True, + 'created_at': '2025-02-18T22:59:56.804993-08:00', + 'updated_at': '2025-02-18T23:06:41.479361-08:00' +} +``` + + + +### Update Webhook + +Modify an existing webhook's configuration. Remember that webhooks can only be updated within their associated project: + + + +```python Python +# Update webhook in default project +updated_webhook = client.update_webhook( + webhook_id=1, + name="Updated Logger", + url="https://your-app.com/new-webhook" +) + +# Or update in a specific project +updated_webhook = client.update_webhook( + webhook_id=1, + name="Updated Logger", + url="https://your-app.com/new-webhook", + project_id="proj_123" +) +print(updated_webhook) +``` + +```javascript JavaScript +// Update webhook in default project +const updatedWebhook = await client.updateWebhook(1, { + name: "Updated Logger", + url: "https://your-app.com/new-webhook" +}); + +// Or update in a specific project +const updatedWebhook = await client.updateWebhook(1, { + name: "Updated Logger", + url: "https://your-app.com/new-webhook", + projectId: "proj_123" +}); +console.log(updatedWebhook); +``` + +```json Output +{ + "message": "Webhook updated successfully" +} +``` + + + +### Delete Webhook + +Remove a webhook configuration from a project: + + + +```python Python +# Delete webhook from default project +response = client.delete_webhook(webhook_id=1) + +# Or delete from a specific project +response = client.delete_webhook(webhook_id=1, project_id="proj_123") +print(response) +``` + +```javascript JavaScript +// Delete webhook from default project +const response = await client.deleteWebhook(1); + +// Or delete from a specific project +const response = await client.deleteWebhook(1, "proj_123"); +console.log(response); +``` + +```json Output +{ + "message": "Webhook deleted successfully" +} +``` + + + +## Webhook Payload + +When a memory event occurs in your project, Mem0 sends a POST request to your webhook URL with the following payload structure: + +```json +{ + "id": "a1b2c3d4-e5f6-4g7h-8i9j-k0l1m2n3o4p5", + "data": { + "memory": "Name is Alex" + }, + "event": "ADD" +} +``` + +## Best Practices + +1. **Implement Retry Logic**: Your webhook endpoint should be able to handle temporary failures and implement appropriate retry mechanisms. + +2. **Verify Webhook Source**: Implement security measures to verify that webhook requests are coming from Mem0. + +3. **Process Events Asynchronously**: Handle webhook events asynchronously to prevent timeouts and ensure reliable processing. + +4. **Monitor Webhook Health**: Regularly check your webhook logs to ensure proper functionality and handle any delivery failures. + +If you have any questions, please feel free to reach out to us using one of the following methods: + + \ No newline at end of file diff --git a/mem0/client/main.py b/mem0/client/main.py index 154db809f1..312e58e281 100644 --- a/mem0/client/main.py +++ b/mem0/client/main.py @@ -524,6 +524,112 @@ def chat(self): """ raise NotImplementedError("Chat is not implemented yet") + @api_error_handler + def get_webhooks(self, project_id: Optional[str] = None) -> Dict[str, Any]: + """Get webhooks configuration for the project. + + Args: + project_id: The ID of the project to get webhooks for. + + Returns: + Dictionary containing webhook details. + + Raises: + APIError: If the API request fails. + ValueError: If project_id is not set. + """ + + project_id = project_id or self.project_id + + if not project_id: + raise ValueError("project_id must be set to access webhooks") + + response = self.client.get(f"api/v1/webhooks/{project_id}/webhook/") + response.raise_for_status() + capture_client_event("client.get_webhook", self) + return response.json() + + @api_error_handler + def create_webhook(self, url: str, name: str, project_id: Optional[str] = None) -> Dict[str, Any]: + """Create a webhook for the current project. + + Args: + url: The URL to send the webhook to. + name: The name of the webhook. + + Returns: + Dictionary containing the created webhook details. + + Raises: + APIError: If the API request fails. + ValueError: If project_id is not set. + """ + project_id = project_id or self.project_id + + if not project_id: + raise ValueError("project_id must be set to create webhook") + + payload = {"url": url, "name": name} + response = self.client.post(f"api/v1/webhooks/{project_id}/webhook/", json=payload) + response.raise_for_status() + capture_client_event("client.create_webhook", self) + return response.json() + + @api_error_handler + def update_webhook( + self, webhook_id: int, name: Optional[str] = None, url: Optional[str] = None, project_id: Optional[str] = None + ) -> Dict[str, Any]: + """Update a webhook configuration. + + Args: + webhook_id: ID of the webhook to update + name: Optional new name for the webhook + url: Optional new URL for the webhook + project_id: The ID of the project to update the webhook for. + + Returns: + Dictionary containing the updated webhook details. + + Raises: + APIError: If the API request fails. + ValueError: If project_id is not set. + """ + project_id = project_id or self.project_id + + if not project_id: + raise ValueError("project_id must be set to update webhook") + + payload = {k: v for k, v in {"name": name, "url": url}.items() if v is not None} + response = self.client.put(f"api/v1/webhooks/{project_id}/webhook/{webhook_id}/", json=payload) + response.raise_for_status() + capture_client_event("client.update_webhook", self, {"webhook_id": webhook_id}) + return response.json() + + @api_error_handler + def delete_webhook(self, webhook_id: int, project_id: Optional[str] = None) -> Dict[str, str]: + """Delete a webhook configuration. + + Args: + webhook_id: ID of the webhook to delete + project_id: The ID of the project to delete the webhook for. + + Returns: + Dictionary containing success message. + + Raises: + APIError: If the API request fails. + ValueError: If project_id is not set. + """ + project_id = project_id or self.project_id + + if not project_id: + raise ValueError("project_id must be set to delete webhook") + + response = self.client.delete(f"api/v1/webhooks/{project_id}/webhook/{webhook_id}/") + response.raise_for_status() + capture_client_event("client.delete_webhook", self, {"webhook_id": webhook_id}) + return response.json() + def _prepare_payload( self, messages: Union[str, List[Dict[str, str]], None], kwargs: Dict[str, Any] ) -> Dict[str, Any]: @@ -867,3 +973,58 @@ async def update_project( async def chat(self): raise NotImplementedError("Chat is not implemented yet") + + @api_error_handler + async def get_webhooks(self, project_id: Optional[str] = None) -> Dict[str, Any]: + project_id = project_id or self.sync_client.project_id + + if not project_id: + raise ValueError("project_id must be set to access webhooks") + + response = await self.async_client.get( + f"api/v1/webhooks/{project_id}/webhook/", + ) + response.raise_for_status() + capture_client_event("async_client.get_webhook", self.sync_client) + return response.json() + + @api_error_handler + async def create_webhook(self, url: str, name: str, project_id: Optional[str] = None) -> Dict[str, Any]: + project_id = project_id or self.sync_client.project_id + + if not project_id: + raise ValueError("project_id must be set to create webhook") + + response = await self.async_client.post( + f"api/v1/webhooks/{project_id}/webhook/", json={"url": url, "name": name} + ) + response.raise_for_status() + capture_client_event("async_client.create_webhook", self.sync_client) + return response.json() + + @api_error_handler + async def update_webhook( + self, webhook_id: int, name: Optional[str] = None, url: Optional[str] = None, project_id: Optional[str] = None + ) -> Dict[str, Any]: + project_id = project_id or self.sync_client.project_id + + if not project_id: + raise ValueError("project_id must be set to update webhook") + + payload = {k: v for k, v in {"name": name, "url": url}.items() if v is not None} + response = await self.async_client.put(f"api/v1/webhooks/{project_id}/webhook/{webhook_id}/", json=payload) + response.raise_for_status() + capture_client_event("async_client.update_webhook", self.sync_client, {"webhook_id": webhook_id}) + return response.json() + + @api_error_handler + async def delete_webhook(self, webhook_id: int, project_id: Optional[str] = None) -> Dict[str, str]: + project_id = project_id or self.sync_client.project_id + + if not project_id: + raise ValueError("project_id must be set to delete webhook") + + response = await self.async_client.delete(f"api/v1/webhooks/{project_id}/webhook/{webhook_id}/") + response.raise_for_status() + capture_client_event("async_client.delete_webhook", self.sync_client, {"webhook_id": webhook_id}) + return response.json() diff --git a/pyproject.toml b/pyproject.toml index db4bc9aa2b..a6e277b6ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "mem0ai" -version = "0.1.51" +version = "0.1.52" description = "Long-term memory for AI Agents" authors = ["Mem0 "] exclude = [