From f0c224ebaa1c311b0c4f8a3fb24d6bf6c5d84aef Mon Sep 17 00:00:00 2001 From: Arthur Ceccotti Date: Wed, 8 Jul 2026 14:30:52 +0100 Subject: [PATCH 1/2] add customer support platform identifiers to start_conversation Lets callers link the customer being created to their record(s) in third-party support platforms (Intercom, Zendesk, Salesforce, Freshchat, Freshdesk), alongside customer_id. Co-Authored-By: Claude Sonnet 5 --- src/gradient_labs/_conversation_start.py | 53 ++++++++++++++- src/gradient_labs/client.py | 8 ++- tests/test_conversation_start.py | 85 ++++++++++++++++++++++++ 3 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 tests/test_conversation_start.py diff --git a/src/gradient_labs/_conversation_start.py b/src/gradient_labs/_conversation_start.py index b97270c..80a8934 100644 --- a/src/gradient_labs/_conversation_start.py +++ b/src/gradient_labs/_conversation_start.py @@ -1,5 +1,6 @@ -from typing import Optional, Dict, Any +from typing import Optional, Dict, Any, List from datetime import datetime +from enum import Enum from dataclasses import dataclass from dataclasses_json import dataclass_json @@ -8,6 +9,45 @@ from .conversation import ParticipantType, ConversationChannel, Conversation +class CustomerSupportPlatform(str, Enum): + """Identifies a third-party customer support platform that a customer + can be linked to via a CustomerSupportPlatformIdentifier.""" + + INTERCOM: str = "intercom" + ZENDESK: str = "zendesk" + SALESFORCE: str = "salesforce" + FRESHCHAT: str = "freshchat" + FRESHDESK: str = "freshdesk" + + +class CustomerSupportPlatformIdentifierType(str, Enum): + """Identifies the kind of identifier within a customer support platform, + for platforms that have more than one kind of identifier.""" + + INTERCOM_LEAD: str = "intercom_lead" + INTERCOM_USER: str = "intercom_user" + ZENDESK_CONVERSATION_USER: str = "zendesk_conversation_user" + ZENDESK_SUPPORT_USER: str = "zendesk_support_user" + SALESFORCE_CONTACT_ID: str = "salesforce_contact_id" + SALESFORCE_ACCOUNT_ID: str = "salesforce_account_id" + + +@dataclass_json +@dataclass(frozen=True) +class CustomerSupportPlatformIdentifier: + # support_platform identifies the third-party customer support platform + # this identifier belongs to. + support_platform: CustomerSupportPlatform + + # value is the customer's external identifier in that platform. + value: str + + # type optionally identifies which kind of identifier this is, for platforms + # that have more than one kind (e.g. Intercom leads vs. users). Omit for + # platforms that only have a single identifier kind (freshchat, freshdesk). + type: Optional[CustomerSupportPlatformIdentifierType] = None + + @dataclass_json @dataclass(frozen=True) class StartConversationParams: @@ -52,6 +92,13 @@ class StartConversationParams: # assigned to the specified traffic group, plus any procedures not assigned to any group. traffic_group_id: Optional[str] = None + # customer_support_platform_identifiers optionally links the customer to their + # record(s) in third-party customer support platforms (e.g. Intercom, Zendesk), + # alongside customer_id. + customer_support_platform_identifiers: Optional[ + List[CustomerSupportPlatformIdentifier] + ] = None + def start_conversation( *, client: HttpClient, params: StartConversationParams @@ -69,6 +116,10 @@ def start_conversation( body["conversation_token"] = params.conversation_token if params.traffic_group_id is not None: body["traffic_group_id"] = params.traffic_group_id + if params.customer_support_platform_identifiers is not None: + body["customer_support_platform_identifiers"] = [ + i.to_dict() for i in params.customer_support_platform_identifiers + ] rsp = client.post( path="conversations", diff --git a/src/gradient_labs/client.py b/src/gradient_labs/client.py index 7847a66..648b6b5 100644 --- a/src/gradient_labs/client.py +++ b/src/gradient_labs/client.py @@ -18,7 +18,13 @@ from ._conversation_rate import rate_conversation, RatingParams from ._conversation_resume import resume_conversation, ResumeParams from ._conversation_read import read_conversation, ReadParams -from ._conversation_start import start_conversation, StartConversationParams +from ._conversation_start import ( + start_conversation, + StartConversationParams, + CustomerSupportPlatform as CustomerSupportPlatform, + CustomerSupportPlatformIdentifier as CustomerSupportPlatformIdentifier, + CustomerSupportPlatformIdentifierType as CustomerSupportPlatformIdentifierType, +) from ._conversation_return_async_tool_result import ( return_async_tool_result, ReturnAsyncToolResultParams, diff --git a/tests/test_conversation_start.py b/tests/test_conversation_start.py new file mode 100644 index 0000000..ae85133 --- /dev/null +++ b/tests/test_conversation_start.py @@ -0,0 +1,85 @@ +from unittest.mock import MagicMock + +from gradient_labs import ( + Client, + Conversation, + ConversationChannel, + CustomerSupportPlatform, + CustomerSupportPlatformIdentifier, + CustomerSupportPlatformIdentifierType, + StartConversationParams, +) + + +def _client_returning(response: dict): + client = Client(api_key="test-key") + post = MagicMock(return_value=response) + client.http_client.post = post + return client, post + + +def _conversation_response() -> dict: + return { + "id": "conv-123", + "customer_id": "cust-456", + "channel": "web", + "status": "active", + "created": "2024-01-15T10:30:00", + "updated": "2024-01-15T10:30:00", + } + + +def test_start_conversation(): + client, post = _client_returning(_conversation_response()) + + conversation = client.start_conversation( + params=StartConversationParams( + id="conv-123", + customer_id="cust-456", + channel=ConversationChannel.LIVE_CHAT, + ) + ) + + _, kwargs = post.call_args + body = kwargs["body"] + assert kwargs["path"] == "conversations" + assert body["id"] == "conv-123" + assert body["customer_id"] == "cust-456" + assert body["channel"] == "web" + assert "customer_support_platform_identifiers" not in body + + assert isinstance(conversation, Conversation) + assert conversation.id == "conv-123" + + +def test_start_conversation_includes_customer_support_platform_identifiers(): + client, post = _client_returning(_conversation_response()) + + client.start_conversation( + params=StartConversationParams( + id="conv-123", + customer_id="cust-456", + channel=ConversationChannel.LIVE_CHAT, + customer_support_platform_identifiers=[ + CustomerSupportPlatformIdentifier( + support_platform=CustomerSupportPlatform.INTERCOM, + type=CustomerSupportPlatformIdentifierType.INTERCOM_USER, + value="6953e162a988d9ef0f73ef9b", + ), + CustomerSupportPlatformIdentifier( + support_platform=CustomerSupportPlatform.FRESHDESK, + value="fd-789", + ), + ], + ) + ) + + _, kwargs = post.call_args + body = kwargs["body"] + identifiers = body["customer_support_platform_identifiers"] + assert identifiers[0]["support_platform"] == "intercom" + assert identifiers[0]["type"] == "intercom_user" + assert identifiers[0]["value"] == "6953e162a988d9ef0f73ef9b" + assert identifiers[1]["support_platform"] == "freshdesk" + assert identifiers[1]["type"] is None + assert identifiers[1]["value"] == "fd-789" From 979d89cf1601396dd6a5c6e1a86a2fb32e219314 Mon Sep 17 00:00:00 2001 From: Arthur Ceccotti Date: Wed, 8 Jul 2026 14:46:58 +0100 Subject: [PATCH 2/2] Rename CustomerSupportPlatform to SupportPlatform Keeps the name generic since support platforms aren't inherently a customer-only concept and may be referenced from other contexts later. Co-Authored-By: Claude Sonnet 5 --- src/gradient_labs/_conversation_start.py | 4 ++-- src/gradient_labs/client.py | 2 +- tests/test_conversation_start.py | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/gradient_labs/_conversation_start.py b/src/gradient_labs/_conversation_start.py index 80a8934..aeddd10 100644 --- a/src/gradient_labs/_conversation_start.py +++ b/src/gradient_labs/_conversation_start.py @@ -9,7 +9,7 @@ from .conversation import ParticipantType, ConversationChannel, Conversation -class CustomerSupportPlatform(str, Enum): +class SupportPlatform(str, Enum): """Identifies a third-party customer support platform that a customer can be linked to via a CustomerSupportPlatformIdentifier.""" @@ -37,7 +37,7 @@ class CustomerSupportPlatformIdentifierType(str, Enum): class CustomerSupportPlatformIdentifier: # support_platform identifies the third-party customer support platform # this identifier belongs to. - support_platform: CustomerSupportPlatform + support_platform: SupportPlatform # value is the customer's external identifier in that platform. value: str diff --git a/src/gradient_labs/client.py b/src/gradient_labs/client.py index 648b6b5..020e449 100644 --- a/src/gradient_labs/client.py +++ b/src/gradient_labs/client.py @@ -21,7 +21,7 @@ from ._conversation_start import ( start_conversation, StartConversationParams, - CustomerSupportPlatform as CustomerSupportPlatform, + SupportPlatform as SupportPlatform, CustomerSupportPlatformIdentifier as CustomerSupportPlatformIdentifier, CustomerSupportPlatformIdentifierType as CustomerSupportPlatformIdentifierType, ) diff --git a/tests/test_conversation_start.py b/tests/test_conversation_start.py index ae85133..d6f10bd 100644 --- a/tests/test_conversation_start.py +++ b/tests/test_conversation_start.py @@ -4,10 +4,10 @@ Client, Conversation, ConversationChannel, - CustomerSupportPlatform, CustomerSupportPlatformIdentifier, CustomerSupportPlatformIdentifierType, StartConversationParams, + SupportPlatform, ) @@ -62,12 +62,12 @@ def test_start_conversation_includes_customer_support_platform_identifiers(): channel=ConversationChannel.LIVE_CHAT, customer_support_platform_identifiers=[ CustomerSupportPlatformIdentifier( - support_platform=CustomerSupportPlatform.INTERCOM, + support_platform=SupportPlatform.INTERCOM, type=CustomerSupportPlatformIdentifierType.INTERCOM_USER, value="6953e162a988d9ef0f73ef9b", ), CustomerSupportPlatformIdentifier( - support_platform=CustomerSupportPlatform.FRESHDESK, + support_platform=SupportPlatform.FRESHDESK, value="fd-789", ), ],