linkedapi 1.1.0__tar.gz → 1.2.0__tar.gz

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.
Files changed (98) hide show
  1. {linkedapi-1.1.0 → linkedapi-1.2.0}/.github/workflows/publish.yaml +4 -4
  2. {linkedapi-1.1.0 → linkedapi-1.2.0}/PKG-INFO +1 -1
  3. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/__init__.py +15 -1
  4. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/client.py +51 -1
  5. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/errors.py +2 -0
  6. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/__init__.py +14 -0
  7. linkedapi-1.2.0/linkedapi/operations/accept_connection_request.py +12 -0
  8. linkedapi-1.2.0/linkedapi/operations/ignore_connection_request.py +12 -0
  9. linkedapi-1.2.0/linkedapi/operations/manage_conversation.py +12 -0
  10. linkedapi-1.2.0/linkedapi/operations/nv_manage_conversation.py +12 -0
  11. linkedapi-1.2.0/linkedapi/operations/nv_sync_inbox.py +12 -0
  12. linkedapi-1.2.0/linkedapi/operations/retrieve_connection_requests.py +15 -0
  13. linkedapi-1.2.0/linkedapi/operations/sync_inbox.py +12 -0
  14. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/__init__.py +22 -0
  15. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/connection.py +15 -1
  16. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/message.py +40 -3
  17. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/webhooks.py +24 -1
  18. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/webhooks/parse.py +3 -0
  19. {linkedapi-1.1.0 → linkedapi-1.2.0}/pyproject.toml +1 -1
  20. {linkedapi-1.1.0 → linkedapi-1.2.0}/tests/test_direct_methods.py +58 -0
  21. {linkedapi-1.1.0 → linkedapi-1.2.0}/tests/test_errors.py +1 -0
  22. {linkedapi-1.1.0 → linkedapi-1.2.0}/tests/test_operations.py +25 -1
  23. linkedapi-1.2.0/tests/test_webhooks.py +84 -0
  24. {linkedapi-1.1.0 → linkedapi-1.2.0}/.gitignore +0 -0
  25. {linkedapi-1.1.0 → linkedapi-1.2.0}/LICENSE +0 -0
  26. {linkedapi-1.1.0 → linkedapi-1.2.0}/README.md +0 -0
  27. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/admin/__init__.py +0 -0
  28. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/admin/accounts.py +0 -0
  29. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/admin/admin.py +0 -0
  30. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/admin/http_client.py +0 -0
  31. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/admin/limits.py +0 -0
  32. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/admin/subscription.py +0 -0
  33. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/admin/webhooks.py +0 -0
  34. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/config.py +0 -0
  35. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/core/__init__.py +0 -0
  36. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/core/operation.py +0 -0
  37. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/core/polling.py +0 -0
  38. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/http/__init__.py +0 -0
  39. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/http/base.py +0 -0
  40. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/http/linked_api_http_client.py +0 -0
  41. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/mappers/__init__.py +0 -0
  42. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/mappers/array.py +0 -0
  43. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/mappers/base.py +0 -0
  44. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/mappers/simple.py +0 -0
  45. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/mappers/then.py +0 -0
  46. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/mappers/void.py +0 -0
  47. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/check_connection_status.py +0 -0
  48. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/comment_on_post.py +0 -0
  49. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/create_post.py +0 -0
  50. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/custom_workflow.py +0 -0
  51. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/fetch_company.py +0 -0
  52. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/fetch_job.py +0 -0
  53. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/fetch_person.py +0 -0
  54. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/fetch_post.py +0 -0
  55. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/nv_fetch_company.py +0 -0
  56. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/nv_fetch_person.py +0 -0
  57. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/nv_search_companies.py +0 -0
  58. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/nv_search_people.py +0 -0
  59. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/nv_send_message.py +0 -0
  60. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/nv_sync_conversation.py +0 -0
  61. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/react_to_post.py +0 -0
  62. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/remove_connection.py +0 -0
  63. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/retrieve_connections.py +0 -0
  64. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/retrieve_pending_requests.py +0 -0
  65. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/retrieve_performance.py +0 -0
  66. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/retrieve_ssi.py +0 -0
  67. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/search_companies.py +0 -0
  68. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/search_jobs.py +0 -0
  69. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/search_people.py +0 -0
  70. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/send_connection_request.py +0 -0
  71. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/send_message.py +0 -0
  72. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/sync_conversation.py +0 -0
  73. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/operations/withdraw_connection_request.py +0 -0
  74. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/py.typed +0 -0
  75. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/account.py +0 -0
  76. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/admin/__init__.py +0 -0
  77. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/admin/accounts.py +0 -0
  78. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/admin/config.py +0 -0
  79. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/admin/limits.py +0 -0
  80. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/admin/subscription.py +0 -0
  81. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/base.py +0 -0
  82. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/company.py +0 -0
  83. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/jobs.py +0 -0
  84. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/params.py +0 -0
  85. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/person.py +0 -0
  86. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/post.py +0 -0
  87. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/responses.py +0 -0
  88. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/search_companies.py +0 -0
  89. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/search_people.py +0 -0
  90. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/statistics.py +0 -0
  91. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/types/workflow.py +0 -0
  92. {linkedapi-1.1.0 → linkedapi-1.2.0}/linkedapi/webhooks/__init__.py +0 -0
  93. {linkedapi-1.1.0 → linkedapi-1.2.0}/tests/conftest.py +0 -0
  94. {linkedapi-1.1.0 → linkedapi-1.2.0}/tests/test_admin.py +0 -0
  95. {linkedapi-1.1.0 → linkedapi-1.2.0}/tests/test_http_client.py +0 -0
  96. {linkedapi-1.1.0 → linkedapi-1.2.0}/tests/test_mappers.py +0 -0
  97. {linkedapi-1.1.0 → linkedapi-1.2.0}/tests/test_models.py +0 -0
  98. {linkedapi-1.1.0 → linkedapi-1.2.0}/tests/test_polling.py +0 -0
@@ -10,10 +10,10 @@ jobs:
10
10
 
11
11
  steps:
12
12
  - name: Checkout code
13
- uses: actions/checkout@v4
13
+ uses: actions/checkout@v5
14
14
 
15
15
  - name: Set up Python
16
- uses: actions/setup-python@v5
16
+ uses: actions/setup-python@v6
17
17
  with:
18
18
  python-version: '3.12'
19
19
 
@@ -38,10 +38,10 @@ jobs:
38
38
 
39
39
  steps:
40
40
  - name: Checkout code
41
- uses: actions/checkout@v4
41
+ uses: actions/checkout@v5
42
42
 
43
43
  - name: Set up Python
44
- uses: actions/setup-python@v5
44
+ uses: actions/setup-python@v6
45
45
  with:
46
46
  python-version: '3.12'
47
47
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: linkedapi
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: Official synchronous Python SDK for Linked API.
5
5
  Project-URL: Homepage, https://linkedapi.io
6
6
  Project-URL: Repository, https://github.com/Linked-API/linkedapi-python
@@ -29,6 +29,7 @@ from linkedapi.mappers import (
29
29
  VoidWorkflowMapper,
30
30
  )
31
31
  from linkedapi.operations import (
32
+ AcceptConnectionRequest,
32
33
  CheckConnectionStatus,
33
34
  CommentOnPost,
34
35
  CreatePost,
@@ -41,16 +42,21 @@ from linkedapi.operations import (
41
42
  FetchPersonMapper,
42
43
  FetchPost,
43
44
  FetchPostMapper,
45
+ IgnoreConnectionRequest,
46
+ ManageConversation,
44
47
  NvFetchCompany,
45
48
  NvFetchCompanyMapper,
46
49
  NvFetchPerson,
47
50
  NvFetchPersonMapper,
51
+ NvManageConversation,
48
52
  NvSearchCompanies,
49
53
  NvSearchPeople,
50
54
  NvSendMessage,
51
55
  NvSyncConversation,
56
+ NvSyncInbox,
52
57
  ReactToPost,
53
58
  RemoveConnection,
59
+ RetrieveConnectionRequests,
54
60
  RetrieveConnections,
55
61
  RetrievePendingRequests,
56
62
  RetrievePerformance,
@@ -61,17 +67,19 @@ from linkedapi.operations import (
61
67
  SendConnectionRequest,
62
68
  SendMessage,
63
69
  SyncConversation,
70
+ SyncInbox,
64
71
  WithdrawConnectionRequest,
65
72
  )
66
73
  from linkedapi.types import * # noqa: F403
67
74
  from linkedapi.types import __all__ as _types_all
68
75
  from linkedapi.webhooks import parse_webhook_event
69
76
 
70
- __version__ = "1.1.0"
77
+ __version__ = "1.2.0"
71
78
  PredefinedOperation = Operation
72
79
 
73
80
  __all__ = [
74
81
  *_types_all,
82
+ "AcceptConnectionRequest",
75
83
  "AdminAccounts",
76
84
  "AdminHttpClient",
77
85
  "AdminLimits",
@@ -93,6 +101,7 @@ __all__ = [
93
101
  "FetchPost",
94
102
  "FetchPostMapper",
95
103
  "HttpClient",
104
+ "IgnoreConnectionRequest",
96
105
  "LINKED_API_ACTION_ERROR_TYPES",
97
106
  "LINKED_API_ERROR_TYPES",
98
107
  "LinkedApi",
@@ -103,20 +112,24 @@ __all__ = [
103
112
  "LinkedApiErrorType",
104
113
  "LinkedApiHttpClient",
105
114
  "LinkedApiWorkflowTimeoutError",
115
+ "ManageConversation",
106
116
  "MappedResponse",
107
117
  "NvFetchCompany",
108
118
  "NvFetchCompanyMapper",
109
119
  "NvFetchPerson",
110
120
  "NvFetchPersonMapper",
121
+ "NvManageConversation",
111
122
  "NvSearchCompanies",
112
123
  "NvSearchPeople",
113
124
  "NvSendMessage",
114
125
  "NvSyncConversation",
126
+ "NvSyncInbox",
115
127
  "Operation",
116
128
  "PredefinedOperation",
117
129
  "ReactToPost",
118
130
  "RemoveConnection",
119
131
  "ResponseMapping",
132
+ "RetrieveConnectionRequests",
120
133
  "RetrieveConnections",
121
134
  "RetrievePendingRequests",
122
135
  "RetrievePerformance",
@@ -128,6 +141,7 @@ __all__ = [
128
141
  "SendMessage",
129
142
  "SimpleWorkflowMapper",
130
143
  "SyncConversation",
144
+ "SyncInbox",
131
145
  "ThenWorkflowMapper",
132
146
  "VoidWorkflowMapper",
133
147
  "WithdrawConnectionRequest",
@@ -8,6 +8,7 @@ from linkedapi.errors import LinkedApiError
8
8
  from linkedapi.http import HttpClient, LinkedApiHttpClient
9
9
  from linkedapi.mappers import MappedResponse
10
10
  from linkedapi.operations import (
11
+ AcceptConnectionRequest,
11
12
  CheckConnectionStatus,
12
13
  CommentOnPost,
13
14
  CreatePost,
@@ -16,14 +17,19 @@ from linkedapi.operations import (
16
17
  FetchJob,
17
18
  FetchPerson,
18
19
  FetchPost,
20
+ IgnoreConnectionRequest,
21
+ ManageConversation,
19
22
  NvFetchCompany,
20
23
  NvFetchPerson,
24
+ NvManageConversation,
21
25
  NvSearchCompanies,
22
26
  NvSearchPeople,
23
27
  NvSendMessage,
24
28
  NvSyncConversation,
29
+ NvSyncInbox,
25
30
  ReactToPost,
26
31
  RemoveConnection,
32
+ RetrieveConnectionRequests,
27
33
  RetrieveConnections,
28
34
  RetrievePendingRequests,
29
35
  RetrievePerformance,
@@ -34,10 +40,16 @@ from linkedapi.operations import (
34
40
  SendConnectionRequest,
35
41
  SendMessage,
36
42
  SyncConversation,
43
+ SyncInbox,
37
44
  WithdrawConnectionRequest,
38
45
  )
39
46
  from linkedapi.types import AccountInfo, LinkedApiActionError, serialize_value
40
- from linkedapi.types.message import ConversationPollRequest, ConversationPollResult
47
+ from linkedapi.types.message import (
48
+ ConversationPollRequest,
49
+ ConversationPollResult,
50
+ InboxMessage,
51
+ InboxPollRequest,
52
+ )
41
53
  from linkedapi.types.statistics import ApiUsageAction, ApiUsageParams
42
54
 
43
55
 
@@ -54,10 +66,15 @@ class LinkedApi:
54
66
  self.custom_workflow = CustomWorkflow(self.http_client)
55
67
  self.send_message = SendMessage(self.http_client)
56
68
  self.sync_conversation = SyncConversation(self.http_client)
69
+ self.sync_inbox = SyncInbox(self.http_client)
70
+ self.manage_conversation = ManageConversation(self.http_client)
57
71
  self.check_connection_status = CheckConnectionStatus(self.http_client)
58
72
  self.send_connection_request = SendConnectionRequest(self.http_client)
59
73
  self.withdraw_connection_request = WithdrawConnectionRequest(self.http_client)
74
+ self.accept_connection_request = AcceptConnectionRequest(self.http_client)
75
+ self.ignore_connection_request = IgnoreConnectionRequest(self.http_client)
60
76
  self.retrieve_pending_requests = RetrievePendingRequests(self.http_client)
77
+ self.retrieve_connection_requests = RetrieveConnectionRequests(self.http_client)
61
78
  self.retrieve_connections = RetrieveConnections(self.http_client)
62
79
  self.remove_connection = RemoveConnection(self.http_client)
63
80
  self.search_companies = SearchCompanies(self.http_client)
@@ -74,6 +91,8 @@ class LinkedApi:
74
91
  self.retrieve_performance = RetrievePerformance(self.http_client)
75
92
  self.nv_send_message = NvSendMessage(self.http_client)
76
93
  self.nv_sync_conversation = NvSyncConversation(self.http_client)
94
+ self.nv_sync_inbox = NvSyncInbox(self.http_client)
95
+ self.nv_manage_conversation = NvManageConversation(self.http_client)
77
96
  self.nv_search_companies = NvSearchCompanies(self.http_client)
78
97
  self.nv_search_people = NvSearchPeople(self.http_client)
79
98
  self.nv_fetch_company = NvFetchCompany(self.http_client)
@@ -83,10 +102,15 @@ class LinkedApi:
83
102
  self.custom_workflow,
84
103
  self.send_message,
85
104
  self.sync_conversation,
105
+ self.sync_inbox,
106
+ self.manage_conversation,
86
107
  self.check_connection_status,
87
108
  self.send_connection_request,
88
109
  self.withdraw_connection_request,
110
+ self.accept_connection_request,
111
+ self.ignore_connection_request,
89
112
  self.retrieve_pending_requests,
113
+ self.retrieve_connection_requests,
90
114
  self.retrieve_connections,
91
115
  self.remove_connection,
92
116
  self.search_companies,
@@ -103,6 +127,8 @@ class LinkedApi:
103
127
  self.retrieve_performance,
104
128
  self.nv_send_message,
105
129
  self.nv_sync_conversation,
130
+ self.nv_sync_inbox,
131
+ self.nv_manage_conversation,
106
132
  self.nv_search_companies,
107
133
  self.nv_search_people,
108
134
  self.nv_fetch_company,
@@ -139,6 +165,30 @@ class LinkedApi:
139
165
  )
140
166
  raise
141
167
 
168
+ def poll_inbox(
169
+ self,
170
+ request: InboxPollRequest | None = None,
171
+ ) -> MappedResponse[list[InboxMessage]]:
172
+ """Read the monitored standard or Sales Navigator inbox, newest messages first."""
173
+
174
+ payload = serialize_value(request) if request is not None else {}
175
+ response = self.http_client.post("/inbox/poll", payload)
176
+ if response.success and response.result is not None:
177
+ messages = response.result.get("messages", [])
178
+ return MappedResponse(
179
+ data=[InboxMessage.model_validate(item) for item in messages],
180
+ errors=[],
181
+ )
182
+ return MappedResponse(
183
+ data=None,
184
+ errors=[
185
+ LinkedApiActionError(
186
+ type=response.error.type if response.error else "",
187
+ message=response.error.message if response.error else "",
188
+ ),
189
+ ],
190
+ )
191
+
142
192
  def get_account_info(self) -> MappedResponse[AccountInfo]:
143
193
  """Retrieve basic information about the current LinkedIn account."""
144
194
 
@@ -23,6 +23,7 @@ LinkedApiActionErrorType = Literal[
23
23
  "noPostingPermission",
24
24
  "noSalesNavigator",
25
25
  "conversationsNotSynced",
26
+ "threadNotFound",
26
27
  ]
27
28
  LINKED_API_ACTION_ERROR_TYPES: tuple[str, ...] = (
28
29
  "personNotFound",
@@ -45,6 +46,7 @@ LINKED_API_ACTION_ERROR_TYPES: tuple[str, ...] = (
45
46
  "noPostingPermission",
46
47
  "noSalesNavigator",
47
48
  "conversationsNotSynced",
49
+ "threadNotFound",
48
50
  )
49
51
 
50
52
  LinkedApiErrorType = Literal[
@@ -1,3 +1,4 @@
1
+ from linkedapi.operations.accept_connection_request import AcceptConnectionRequest
1
2
  from linkedapi.operations.check_connection_status import CheckConnectionStatus
2
3
  from linkedapi.operations.comment_on_post import CommentOnPost
3
4
  from linkedapi.operations.create_post import CreatePost
@@ -6,14 +7,19 @@ from linkedapi.operations.fetch_company import FetchCompany, FetchCompanyMapper
6
7
  from linkedapi.operations.fetch_job import FetchJob, FetchJobMapper
7
8
  from linkedapi.operations.fetch_person import FetchPerson, FetchPersonMapper
8
9
  from linkedapi.operations.fetch_post import FetchPost, FetchPostMapper
10
+ from linkedapi.operations.ignore_connection_request import IgnoreConnectionRequest
11
+ from linkedapi.operations.manage_conversation import ManageConversation
9
12
  from linkedapi.operations.nv_fetch_company import NvFetchCompany, NvFetchCompanyMapper
10
13
  from linkedapi.operations.nv_fetch_person import NvFetchPerson, NvFetchPersonMapper
14
+ from linkedapi.operations.nv_manage_conversation import NvManageConversation
11
15
  from linkedapi.operations.nv_search_companies import NvSearchCompanies
12
16
  from linkedapi.operations.nv_search_people import NvSearchPeople
13
17
  from linkedapi.operations.nv_send_message import NvSendMessage
14
18
  from linkedapi.operations.nv_sync_conversation import NvSyncConversation
19
+ from linkedapi.operations.nv_sync_inbox import NvSyncInbox
15
20
  from linkedapi.operations.react_to_post import ReactToPost
16
21
  from linkedapi.operations.remove_connection import RemoveConnection
22
+ from linkedapi.operations.retrieve_connection_requests import RetrieveConnectionRequests
17
23
  from linkedapi.operations.retrieve_connections import RetrieveConnections
18
24
  from linkedapi.operations.retrieve_pending_requests import RetrievePendingRequests
19
25
  from linkedapi.operations.retrieve_performance import RetrievePerformance
@@ -24,9 +30,11 @@ from linkedapi.operations.search_people import SearchPeople
24
30
  from linkedapi.operations.send_connection_request import SendConnectionRequest
25
31
  from linkedapi.operations.send_message import SendMessage
26
32
  from linkedapi.operations.sync_conversation import SyncConversation
33
+ from linkedapi.operations.sync_inbox import SyncInbox
27
34
  from linkedapi.operations.withdraw_connection_request import WithdrawConnectionRequest
28
35
 
29
36
  __all__ = [
37
+ "AcceptConnectionRequest",
30
38
  "CheckConnectionStatus",
31
39
  "CommentOnPost",
32
40
  "CreatePost",
@@ -39,16 +47,21 @@ __all__ = [
39
47
  "FetchPersonMapper",
40
48
  "FetchPost",
41
49
  "FetchPostMapper",
50
+ "IgnoreConnectionRequest",
51
+ "ManageConversation",
42
52
  "NvFetchCompany",
43
53
  "NvFetchCompanyMapper",
44
54
  "NvFetchPerson",
45
55
  "NvFetchPersonMapper",
56
+ "NvManageConversation",
46
57
  "NvSearchCompanies",
47
58
  "NvSearchPeople",
48
59
  "NvSendMessage",
49
60
  "NvSyncConversation",
61
+ "NvSyncInbox",
50
62
  "ReactToPost",
51
63
  "RemoveConnection",
64
+ "RetrieveConnectionRequests",
52
65
  "RetrieveConnections",
53
66
  "RetrievePendingRequests",
54
67
  "RetrievePerformance",
@@ -59,5 +72,6 @@ __all__ = [
59
72
  "SendConnectionRequest",
60
73
  "SendMessage",
61
74
  "SyncConversation",
75
+ "SyncInbox",
62
76
  "WithdrawConnectionRequest",
63
77
  ]
@@ -0,0 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+ from linkedapi.core import Operation
4
+ from linkedapi.mappers import VoidWorkflowMapper
5
+ from linkedapi.types import AcceptConnectionRequestParams
6
+
7
+
8
+ class AcceptConnectionRequest(Operation[AcceptConnectionRequestParams, None]):
9
+ """Accept an incoming standard LinkedIn connection request."""
10
+
11
+ operation_name = "acceptConnectionRequest"
12
+ mapper = VoidWorkflowMapper[AcceptConnectionRequestParams]("st.acceptConnectionRequest")
@@ -0,0 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+ from linkedapi.core import Operation
4
+ from linkedapi.mappers import VoidWorkflowMapper
5
+ from linkedapi.types import IgnoreConnectionRequestParams
6
+
7
+
8
+ class IgnoreConnectionRequest(Operation[IgnoreConnectionRequestParams, None]):
9
+ """Ignore an incoming standard LinkedIn connection request."""
10
+
11
+ operation_name = "ignoreConnectionRequest"
12
+ mapper = VoidWorkflowMapper[IgnoreConnectionRequestParams]("st.ignoreConnectionRequest")
@@ -0,0 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+ from linkedapi.core import Operation
4
+ from linkedapi.mappers import VoidWorkflowMapper
5
+ from linkedapi.types import ManageConversationParams
6
+
7
+
8
+ class ManageConversation(Operation[ManageConversationParams, None]):
9
+ """Manage a standard LinkedIn conversation thread."""
10
+
11
+ operation_name = "manageConversation"
12
+ mapper = VoidWorkflowMapper[ManageConversationParams]("st.manageConversation")
@@ -0,0 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+ from linkedapi.core import Operation
4
+ from linkedapi.mappers import VoidWorkflowMapper
5
+ from linkedapi.types import NvManageConversationParams
6
+
7
+
8
+ class NvManageConversation(Operation[NvManageConversationParams, None]):
9
+ """Manage a Sales Navigator conversation thread."""
10
+
11
+ operation_name = "nvManageConversation"
12
+ mapper = VoidWorkflowMapper[NvManageConversationParams]("nv.manageConversation")
@@ -0,0 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+ from linkedapi.core import Operation
4
+ from linkedapi.mappers import VoidWorkflowMapper
5
+ from linkedapi.types import NvSyncInboxParams
6
+
7
+
8
+ class NvSyncInbox(Operation[NvSyncInboxParams, None]):
9
+ """Enable whole-inbox monitoring for Sales Navigator conversations."""
10
+
11
+ operation_name = "nvSyncInbox"
12
+ mapper = VoidWorkflowMapper[NvSyncInboxParams]("nv.syncInbox")
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+
3
+ from linkedapi.core import Operation
4
+ from linkedapi.mappers import ArrayWorkflowMapper
5
+ from linkedapi.types import RetrieveConnectionRequestsResult
6
+
7
+
8
+ class RetrieveConnectionRequests(Operation[None, list[RetrieveConnectionRequestsResult]]):
9
+ """Retrieve incoming standard LinkedIn connection requests."""
10
+
11
+ operation_name = "retrieveConnectionRequests"
12
+ mapper = ArrayWorkflowMapper[None, RetrieveConnectionRequestsResult](
13
+ "st.retrieveConnectionRequests",
14
+ RetrieveConnectionRequestsResult,
15
+ )
@@ -0,0 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+ from linkedapi.core import Operation
4
+ from linkedapi.mappers import VoidWorkflowMapper
5
+ from linkedapi.types import SyncInboxParams
6
+
7
+
8
+ class SyncInbox(Operation[SyncInboxParams, None]):
9
+ """Enable whole-inbox monitoring for standard LinkedIn conversations."""
10
+
11
+ operation_name = "syncInbox"
12
+ mapper = VoidWorkflowMapper[SyncInboxParams]("st.syncInbox")
@@ -68,13 +68,16 @@ from linkedapi.types.company import (
68
68
  StCompanyEmployeesRetrievalConfig,
69
69
  )
70
70
  from linkedapi.types.connection import (
71
+ AcceptConnectionRequestParams,
71
72
  CheckConnectionStatusParams,
72
73
  CheckConnectionStatusResult,
73
74
  ConnectionPerson,
74
75
  ConnectionStatus,
76
+ IgnoreConnectionRequestParams,
75
77
  NvOpenPersonPageParams,
76
78
  NvOpenPersonPageResult,
77
79
  RemoveConnectionParams,
80
+ RetrieveConnectionRequestsResult,
78
81
  RetrieveConnectionsFilter,
79
82
  RetrieveConnectionsParams,
80
83
  RetrieveConnectionsResult,
@@ -101,12 +104,18 @@ from linkedapi.types.message import (
101
104
  ConversationPollRequest,
102
105
  ConversationPollResult,
103
106
  ConversationType,
107
+ InboxMessage,
108
+ InboxPollRequest,
109
+ ManageConversationParams,
104
110
  Message,
105
111
  MessageSender,
112
+ NvManageConversationParams,
106
113
  NvSendMessageParams,
107
114
  NvSyncConversationParams,
115
+ NvSyncInboxParams,
108
116
  SendMessageParams,
109
117
  SyncConversationParams,
118
+ SyncInboxParams,
110
119
  )
111
120
  from linkedapi.types.params import BaseActionParams, LimitParams, LimitSinceParams
112
121
  from linkedapi.types.person import (
@@ -183,6 +192,8 @@ from linkedapi.types.webhooks import (
183
192
  AccountWebhookEventData,
184
193
  AccountWebhookStatus,
185
194
  DeleteWebhookParams,
195
+ MessageWebhookEvent,
196
+ MessageWebhookEventData,
186
197
  ReplayWebhookDeliveryParams,
187
198
  SetWebhookParams,
188
199
  SetWebhookPayloadModeParams,
@@ -213,6 +224,7 @@ from linkedapi.types.workflow import (
213
224
  )
214
225
 
215
226
  __all__ = [
227
+ "AcceptConnectionRequestParams",
216
228
  "AccountInfo",
217
229
  "AccountWebhookEvent",
218
230
  "AccountWebhookEventData",
@@ -269,6 +281,9 @@ __all__ = [
269
281
  "GetConnectionSessionParams",
270
282
  "GetLimitsParams",
271
283
  "GetLimitsUsageParams",
284
+ "IgnoreConnectionRequestParams",
285
+ "InboxMessage",
286
+ "InboxPollRequest",
272
287
  "Job",
273
288
  "JobCurrency",
274
289
  "JobDatePosted",
@@ -290,9 +305,12 @@ __all__ = [
290
305
  "LinkedApiRequestError",
291
306
  "LinkedApiResponse",
292
307
  "LocationType",
308
+ "ManageConversationParams",
293
309
  "MaxAnnualRevenue",
294
310
  "Message",
295
311
  "MessageSender",
312
+ "MessageWebhookEvent",
313
+ "MessageWebhookEventData",
296
314
  "MinAnnualRevenue",
297
315
  "NvBaseFetchCompanyParams",
298
316
  "NvBaseFetchCompanyParamsWide",
@@ -303,6 +321,7 @@ __all__ = [
303
321
  "NvCompanyEmployeeRetrievalConfig",
304
322
  "NvFetchCompanyParams",
305
323
  "NvFetchCompanyResult",
324
+ "NvManageConversationParams",
306
325
  "NvOpenPersonPageParams",
307
326
  "NvOpenPersonPageResult",
308
327
  "NvSearchCompaniesFilter",
@@ -313,6 +332,7 @@ __all__ = [
313
332
  "NvSearchPeopleResult",
314
333
  "NvSendMessageParams",
315
334
  "NvSyncConversationParams",
335
+ "NvSyncInboxParams",
316
336
  "PendingConnectionSession",
317
337
  "Person",
318
338
  "PersonEducation",
@@ -341,6 +361,7 @@ __all__ = [
341
361
  "ReparseAccountInfoResult",
342
362
  "ReplayWebhookDeliveryParams",
343
363
  "ResetLimitsParams",
364
+ "RetrieveConnectionRequestsResult",
344
365
  "RetrieveConnectionsFilter",
345
366
  "RetrieveConnectionsParams",
346
367
  "RetrieveConnectionsResult",
@@ -377,6 +398,7 @@ __all__ = [
377
398
  "SubscriptionStatus",
378
399
  "SubscriptionStatusValue",
379
400
  "SyncConversationParams",
401
+ "SyncInboxParams",
380
402
  "WebhookDelivery",
381
403
  "WebhookDeliveryStatus",
382
404
  "WebhookEvent",
@@ -5,7 +5,7 @@ from typing import Literal
5
5
  from linkedapi.types.base import LinkedApiModel
6
6
  from linkedapi.types.params import BaseActionParams, LimitParams
7
7
 
8
- ConnectionStatus = Literal["connected", "pending", "notConnected"]
8
+ ConnectionStatus = Literal["connected", "pending", "incoming", "notConnected"]
9
9
 
10
10
 
11
11
  class ConnectionPerson(LinkedApiModel):
@@ -42,6 +42,20 @@ class RetrievePendingRequestsResult(LinkedApiModel):
42
42
  sent_time: str | None = None
43
43
 
44
44
 
45
+ class AcceptConnectionRequestParams(BaseActionParams):
46
+ person_url: str
47
+
48
+
49
+ class IgnoreConnectionRequestParams(BaseActionParams):
50
+ person_url: str
51
+
52
+
53
+ class RetrieveConnectionRequestsResult(LinkedApiModel):
54
+ name: str | None = None
55
+ public_url: str | None = None
56
+ headline: str | None = None
57
+
58
+
45
59
  class RetrieveConnectionsFilter(LinkedApiModel):
46
60
  first_name: str | None = None
47
61
  last_name: str | None = None
@@ -10,24 +10,44 @@ MessageSender = Literal["us", "them"]
10
10
 
11
11
 
12
12
  class SendMessageParams(BaseActionParams):
13
- person_url: str
14
13
  text: str
14
+ person_url: str | None = None
15
+ thread_id: str | None = None
15
16
 
16
17
 
17
18
  class SyncConversationParams(BaseActionParams):
18
19
  person_url: str
19
20
 
20
21
 
22
+ class SyncInboxParams(BaseActionParams):
23
+ pass
24
+
25
+
26
+ class ManageConversationParams(BaseActionParams):
27
+ thread_id: str
28
+ operation: str
29
+
30
+
21
31
  class NvSendMessageParams(BaseActionParams):
22
- person_url: str
23
32
  text: str
24
- subject: str
33
+ person_url: str | None = None
34
+ subject: str | None = None
35
+ thread_id: str | None = None
25
36
 
26
37
 
27
38
  class NvSyncConversationParams(BaseActionParams):
28
39
  person_url: str
29
40
 
30
41
 
42
+ class NvSyncInboxParams(BaseActionParams):
43
+ pass
44
+
45
+
46
+ class NvManageConversationParams(BaseActionParams):
47
+ thread_id: str
48
+ operation: str
49
+
50
+
31
51
  class ConversationPollRequest(LinkedApiModel):
32
52
  person_url: str
33
53
  type: ConversationType
@@ -39,6 +59,7 @@ class Message(LinkedApiModel):
39
59
  sender: MessageSender | None = None
40
60
  text: str | None = None
41
61
  time: str | None = None
62
+ thread_id: str | None = None
42
63
 
43
64
 
44
65
  class ConversationPollResult(LinkedApiModel):
@@ -46,3 +67,19 @@ class ConversationPollResult(LinkedApiModel):
46
67
  type: ConversationType | None = None
47
68
  messages: list[Message] | None = None
48
69
  since: str | None = None
70
+
71
+
72
+ class InboxPollRequest(LinkedApiModel):
73
+ since: str | None = None
74
+ type: ConversationType | None = None
75
+ thread_id: str | None = None
76
+
77
+
78
+ class InboxMessage(LinkedApiModel):
79
+ id: str | None = None
80
+ type: ConversationType | None = None
81
+ thread_id: str | None = None
82
+ person_url: str | None = None
83
+ sender: MessageSender | None = None
84
+ text: str | None = None
85
+ time: str | None = None
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  from typing import Any, Literal
4
4
 
5
5
  from linkedapi.types.base import LinkedApiModel
6
+ from linkedapi.types.message import ConversationType, MessageSender
6
7
 
7
8
  WebhookPayloadMode = Literal["thin", "fat"]
8
9
  WebhookEventType = Literal[
@@ -13,6 +14,8 @@ WebhookEventType = Literal[
13
14
  "account.reconnectionRequired",
14
15
  "account.frozen",
15
16
  "account.deleted",
17
+ "inbox.messageReceived",
18
+ "inbox.messageSent",
16
19
  "webhook.test",
17
20
  ]
18
21
  WebhookDeliveryStatus = Literal["pending", "delivering", "success", "failed"]
@@ -91,6 +94,24 @@ class AccountWebhookEvent(LinkedApiModel):
91
94
  data: AccountWebhookEventData
92
95
 
93
96
 
97
+ class MessageWebhookEventData(LinkedApiModel):
98
+ account_id: str | None = None
99
+ type: ConversationType | None = None
100
+ thread_id: str | None = None
101
+ person_url: str | None = None
102
+ message_id: str | None = None
103
+ sender: MessageSender | None = None
104
+ text: str | None = None
105
+ time: str | None = None
106
+
107
+
108
+ class MessageWebhookEvent(LinkedApiModel):
109
+ id: str
110
+ type: Literal["inbox.messageReceived", "inbox.messageSent"]
111
+ created_at: str | None = None
112
+ data: MessageWebhookEventData
113
+
114
+
94
115
  class WebhookTestEventData(LinkedApiModel):
95
116
  message: str | None = None
96
117
 
@@ -102,4 +123,6 @@ class WebhookTestEvent(LinkedApiModel):
102
123
  data: WebhookTestEventData
103
124
 
104
125
 
105
- WebhookEvent = WorkflowWebhookEvent | AccountWebhookEvent | WebhookTestEvent
126
+ WebhookEvent = (
127
+ WorkflowWebhookEvent | AccountWebhookEvent | MessageWebhookEvent | WebhookTestEvent
128
+ )
@@ -4,6 +4,7 @@ import json
4
4
 
5
5
  from linkedapi.types.webhooks import (
6
6
  AccountWebhookEvent,
7
+ MessageWebhookEvent,
7
8
  WebhookEvent,
8
9
  WebhookTestEvent,
9
10
  WorkflowWebhookEvent,
@@ -41,6 +42,8 @@ def parse_webhook_event(raw_body: str | bytes) -> WebhookEvent:
41
42
  return WorkflowWebhookEvent.model_validate(parsed)
42
43
  if event_type.startswith("account."):
43
44
  return AccountWebhookEvent.model_validate(parsed)
45
+ if event_type.startswith("inbox."):
46
+ return MessageWebhookEvent.model_validate(parsed)
44
47
  if event_type == "webhook.test":
45
48
  return WebhookTestEvent.model_validate(parsed)
46
49
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "linkedapi"
7
- version = "1.1.0"
7
+ version = "1.2.0"
8
8
  description = "Official synchronous Python SDK for Linked API."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -6,6 +6,7 @@ from conftest import FakeHttpClient
6
6
  from linkedapi import (
7
7
  ApiUsageParams,
8
8
  ConversationPollRequest,
9
+ InboxPollRequest,
9
10
  LinkedApi,
10
11
  LinkedApiActionError,
11
12
  LinkedApiError,
@@ -76,6 +77,63 @@ def test_poll_conversations_other_thrown_error_is_raised(fake_http_client: FakeH
76
77
  assert error.value.type == "httpError"
77
78
 
78
79
 
80
+ def test_poll_inbox_success(fake_http_client: FakeHttpClient) -> None:
81
+ linkedapi = LinkedApi(fake_http_client)
82
+ fake_http_client.queue_response(
83
+ result={
84
+ "messages": [
85
+ {
86
+ "id": "m1",
87
+ "type": "st",
88
+ "threadId": "t1",
89
+ "personUrl": "u",
90
+ "sender": "them",
91
+ "text": "hi",
92
+ "time": "now",
93
+ }
94
+ ]
95
+ }
96
+ )
97
+
98
+ result = linkedapi.poll_inbox(InboxPollRequest(type="st", thread_id="t1"))
99
+
100
+ assert fake_http_client.calls[0] == (
101
+ "POST",
102
+ "/inbox/poll",
103
+ {"type": "st", "threadId": "t1"},
104
+ )
105
+ assert result.data is not None
106
+ assert result.data[0].thread_id == "t1"
107
+ assert result.data[0].sender == "them"
108
+ assert result.errors == []
109
+
110
+
111
+ def test_poll_inbox_without_request_sends_empty_body(fake_http_client: FakeHttpClient) -> None:
112
+ linkedapi = LinkedApi(fake_http_client)
113
+ fake_http_client.queue_response(result={"messages": []})
114
+
115
+ result = linkedapi.poll_inbox()
116
+
117
+ assert fake_http_client.calls[0] == ("POST", "/inbox/poll", {})
118
+ assert result.data == []
119
+ assert result.errors == []
120
+
121
+
122
+ def test_poll_inbox_response_error_maps_to_action_errors(
123
+ fake_http_client: FakeHttpClient,
124
+ ) -> None:
125
+ linkedapi = LinkedApi(fake_http_client)
126
+ fake_http_client.queue_response(
127
+ success=False,
128
+ error=LinkedApiRequestError(type="invalidRequestPayload", message="bad"),
129
+ )
130
+
131
+ result = linkedapi.poll_inbox(InboxPollRequest(type="st"))
132
+
133
+ assert result.data is None
134
+ assert result.errors == [LinkedApiActionError(type="invalidRequestPayload", message="bad")]
135
+
136
+
79
137
  def test_get_account_info_and_api_usage_paths(fake_http_client: FakeHttpClient) -> None:
80
138
  linkedapi = LinkedApi(fake_http_client)
81
139
  fake_http_client.queue_response(result={"name": "Jane", "url": "https://linkedin.com/in/jane"})
@@ -49,6 +49,7 @@ def test_error_type_sets_match_node_contract() -> None:
49
49
  "noPostingPermission",
50
50
  "noSalesNavigator",
51
51
  "conversationsNotSynced",
52
+ "threadNotFound",
52
53
  )
53
54
 
54
55
 
@@ -29,13 +29,20 @@ def test_linked_api_exposes_all_predefined_operations() -> None:
29
29
  "send_connection_request",
30
30
  "check_connection_status",
31
31
  "withdraw_connection_request",
32
+ "accept_connection_request",
33
+ "ignore_connection_request",
32
34
  "retrieve_pending_requests",
35
+ "retrieve_connection_requests",
33
36
  "retrieve_connections",
34
37
  "remove_connection",
35
38
  "send_message",
36
39
  "sync_conversation",
40
+ "sync_inbox",
41
+ "manage_conversation",
37
42
  "nv_send_message",
38
43
  "nv_sync_conversation",
44
+ "nv_sync_inbox",
45
+ "nv_manage_conversation",
39
46
  "react_to_post",
40
47
  "comment_on_post",
41
48
  "create_post",
@@ -48,7 +55,7 @@ def test_linked_api_exposes_all_predefined_operations() -> None:
48
55
  assert hasattr(operation, "execute")
49
56
  assert hasattr(operation, "result")
50
57
  assert hasattr(operation, "cancel")
51
- assert len(linkedapi.operations) == 27
58
+ assert len(linkedapi.operations) == 34
52
59
 
53
60
 
54
61
  def test_operation_mappers_match_node_contract() -> None:
@@ -64,6 +71,23 @@ def test_operation_mappers_match_node_contract() -> None:
64
71
  assert linkedapi.fetch_job.mapper.default_params == {"basicInfo": True}
65
72
  assert isinstance(linkedapi.send_connection_request.mapper, VoidWorkflowMapper)
66
73
  assert linkedapi.send_connection_request.mapper.action_type == "st.sendConnectionRequest"
74
+ assert isinstance(linkedapi.accept_connection_request.mapper, VoidWorkflowMapper)
75
+ assert linkedapi.accept_connection_request.mapper.action_type == "st.acceptConnectionRequest"
76
+ assert isinstance(linkedapi.ignore_connection_request.mapper, VoidWorkflowMapper)
77
+ assert linkedapi.ignore_connection_request.mapper.action_type == "st.ignoreConnectionRequest"
78
+ assert isinstance(linkedapi.retrieve_connection_requests.mapper, ArrayWorkflowMapper)
79
+ assert (
80
+ linkedapi.retrieve_connection_requests.mapper.base_action_type
81
+ == "st.retrieveConnectionRequests"
82
+ )
83
+ assert isinstance(linkedapi.sync_inbox.mapper, VoidWorkflowMapper)
84
+ assert linkedapi.sync_inbox.mapper.action_type == "st.syncInbox"
85
+ assert isinstance(linkedapi.nv_sync_inbox.mapper, VoidWorkflowMapper)
86
+ assert linkedapi.nv_sync_inbox.mapper.action_type == "nv.syncInbox"
87
+ assert isinstance(linkedapi.manage_conversation.mapper, VoidWorkflowMapper)
88
+ assert linkedapi.manage_conversation.mapper.action_type == "st.manageConversation"
89
+ assert isinstance(linkedapi.nv_manage_conversation.mapper, VoidWorkflowMapper)
90
+ assert linkedapi.nv_manage_conversation.mapper.action_type == "nv.manageConversation"
67
91
 
68
92
 
69
93
  def test_execute_and_result_flow_returns_pydantic_data(
@@ -0,0 +1,84 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+
5
+ import pytest
6
+
7
+ from linkedapi import (
8
+ AccountWebhookEvent,
9
+ MessageWebhookEvent,
10
+ WorkflowWebhookEvent,
11
+ parse_webhook_event,
12
+ )
13
+
14
+
15
+ def test_parse_workflow_event() -> None:
16
+ event = parse_webhook_event(
17
+ json.dumps(
18
+ {
19
+ "id": "e1",
20
+ "type": "workflow.completed",
21
+ "data": {"workflowId": "wf1", "accountId": "a1", "status": "completed"},
22
+ }
23
+ )
24
+ )
25
+
26
+ assert isinstance(event, WorkflowWebhookEvent)
27
+ assert event.data.workflow_id == "wf1"
28
+
29
+
30
+ def test_parse_account_event() -> None:
31
+ event = parse_webhook_event(
32
+ json.dumps({"id": "e2", "type": "account.frozen", "data": {"accountId": "a1"}})
33
+ )
34
+
35
+ assert isinstance(event, AccountWebhookEvent)
36
+ assert event.data.account_id == "a1"
37
+
38
+
39
+ def test_parse_message_received_event() -> None:
40
+ event = parse_webhook_event(
41
+ json.dumps(
42
+ {
43
+ "id": "e3",
44
+ "type": "inbox.messageReceived",
45
+ "data": {
46
+ "accountId": "a1",
47
+ "type": "st",
48
+ "threadId": "t1",
49
+ "personUrl": "u",
50
+ "messageId": "m1",
51
+ "sender": "them",
52
+ "text": "hello",
53
+ "time": "now",
54
+ },
55
+ }
56
+ )
57
+ )
58
+
59
+ assert isinstance(event, MessageWebhookEvent)
60
+ assert event.type == "inbox.messageReceived"
61
+ assert event.data.thread_id == "t1"
62
+ assert event.data.message_id == "m1"
63
+ assert event.data.sender == "them"
64
+
65
+
66
+ def test_parse_message_sent_event() -> None:
67
+ event = parse_webhook_event(
68
+ json.dumps(
69
+ {
70
+ "id": "e4",
71
+ "type": "inbox.messageSent",
72
+ "data": {"accountId": "a1", "type": "nv", "sender": "us", "text": "hi"},
73
+ }
74
+ )
75
+ )
76
+
77
+ assert isinstance(event, MessageWebhookEvent)
78
+ assert event.type == "inbox.messageSent"
79
+ assert event.data.type == "nv"
80
+
81
+
82
+ def test_parse_unknown_event_raises() -> None:
83
+ with pytest.raises(ValueError):
84
+ parse_webhook_event(json.dumps({"id": "e5", "type": "unknown.thing", "data": {}}))
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes