linkedapi 1.0.2__tar.gz → 1.1.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 (90) hide show
  1. {linkedapi-1.0.2 → linkedapi-1.1.0}/.gitignore +1 -0
  2. {linkedapi-1.0.2 → linkedapi-1.1.0}/PKG-INFO +1 -1
  3. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/__init__.py +5 -1
  4. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/admin/__init__.py +2 -0
  5. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/admin/accounts.py +23 -0
  6. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/admin/admin.py +3 -1
  7. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/admin/subscription.py +0 -18
  8. linkedapi-1.1.0/linkedapi/admin/webhooks.py +82 -0
  9. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/errors.py +4 -0
  10. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/types/__init__.py +46 -10
  11. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/types/admin/__init__.py +8 -10
  12. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/types/admin/accounts.py +22 -0
  13. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/types/admin/subscription.py +0 -21
  14. linkedapi-1.1.0/linkedapi/types/webhooks.py +105 -0
  15. linkedapi-1.1.0/linkedapi/webhooks/__init__.py +3 -0
  16. linkedapi-1.1.0/linkedapi/webhooks/parse.py +48 -0
  17. {linkedapi-1.0.2 → linkedapi-1.1.0}/pyproject.toml +1 -1
  18. {linkedapi-1.0.2 → linkedapi-1.1.0}/tests/test_admin.py +9 -6
  19. {linkedapi-1.0.2 → linkedapi-1.1.0}/tests/test_errors.py +2 -0
  20. {linkedapi-1.0.2 → linkedapi-1.1.0}/.github/workflows/publish.yaml +0 -0
  21. {linkedapi-1.0.2 → linkedapi-1.1.0}/LICENSE +0 -0
  22. {linkedapi-1.0.2 → linkedapi-1.1.0}/README.md +0 -0
  23. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/admin/http_client.py +0 -0
  24. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/admin/limits.py +0 -0
  25. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/client.py +0 -0
  26. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/config.py +0 -0
  27. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/core/__init__.py +0 -0
  28. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/core/operation.py +0 -0
  29. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/core/polling.py +0 -0
  30. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/http/__init__.py +0 -0
  31. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/http/base.py +0 -0
  32. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/http/linked_api_http_client.py +0 -0
  33. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/mappers/__init__.py +0 -0
  34. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/mappers/array.py +0 -0
  35. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/mappers/base.py +0 -0
  36. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/mappers/simple.py +0 -0
  37. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/mappers/then.py +0 -0
  38. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/mappers/void.py +0 -0
  39. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/__init__.py +0 -0
  40. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/check_connection_status.py +0 -0
  41. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/comment_on_post.py +0 -0
  42. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/create_post.py +0 -0
  43. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/custom_workflow.py +0 -0
  44. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/fetch_company.py +0 -0
  45. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/fetch_job.py +0 -0
  46. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/fetch_person.py +0 -0
  47. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/fetch_post.py +0 -0
  48. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/nv_fetch_company.py +0 -0
  49. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/nv_fetch_person.py +0 -0
  50. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/nv_search_companies.py +0 -0
  51. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/nv_search_people.py +0 -0
  52. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/nv_send_message.py +0 -0
  53. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/nv_sync_conversation.py +0 -0
  54. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/react_to_post.py +0 -0
  55. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/remove_connection.py +0 -0
  56. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/retrieve_connections.py +0 -0
  57. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/retrieve_pending_requests.py +0 -0
  58. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/retrieve_performance.py +0 -0
  59. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/retrieve_ssi.py +0 -0
  60. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/search_companies.py +0 -0
  61. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/search_jobs.py +0 -0
  62. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/search_people.py +0 -0
  63. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/send_connection_request.py +0 -0
  64. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/send_message.py +0 -0
  65. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/sync_conversation.py +0 -0
  66. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/operations/withdraw_connection_request.py +0 -0
  67. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/py.typed +0 -0
  68. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/types/account.py +0 -0
  69. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/types/admin/config.py +0 -0
  70. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/types/admin/limits.py +0 -0
  71. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/types/base.py +0 -0
  72. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/types/company.py +0 -0
  73. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/types/connection.py +0 -0
  74. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/types/jobs.py +0 -0
  75. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/types/message.py +0 -0
  76. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/types/params.py +0 -0
  77. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/types/person.py +0 -0
  78. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/types/post.py +0 -0
  79. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/types/responses.py +0 -0
  80. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/types/search_companies.py +0 -0
  81. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/types/search_people.py +0 -0
  82. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/types/statistics.py +0 -0
  83. {linkedapi-1.0.2 → linkedapi-1.1.0}/linkedapi/types/workflow.py +0 -0
  84. {linkedapi-1.0.2 → linkedapi-1.1.0}/tests/conftest.py +0 -0
  85. {linkedapi-1.0.2 → linkedapi-1.1.0}/tests/test_direct_methods.py +0 -0
  86. {linkedapi-1.0.2 → linkedapi-1.1.0}/tests/test_http_client.py +0 -0
  87. {linkedapi-1.0.2 → linkedapi-1.1.0}/tests/test_mappers.py +0 -0
  88. {linkedapi-1.0.2 → linkedapi-1.1.0}/tests/test_models.py +0 -0
  89. {linkedapi-1.0.2 → linkedapi-1.1.0}/tests/test_operations.py +0 -0
  90. {linkedapi-1.0.2 → linkedapi-1.1.0}/tests/test_polling.py +0 -0
@@ -6,4 +6,5 @@ dist/
6
6
  .ruff_cache/
7
7
  .pytest_cache/
8
8
  .venv/
9
+ uv.lock
9
10
  build/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: linkedapi
3
- Version: 1.0.2
3
+ Version: 1.1.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
@@ -3,6 +3,7 @@ from linkedapi.admin import (
3
3
  AdminHttpClient,
4
4
  AdminLimits,
5
5
  AdminSubscription,
6
+ AdminWebhooks,
6
7
  LinkedApiAdmin,
7
8
  )
8
9
  from linkedapi.client import LinkedApi
@@ -64,8 +65,9 @@ from linkedapi.operations import (
64
65
  )
65
66
  from linkedapi.types import * # noqa: F403
66
67
  from linkedapi.types import __all__ as _types_all
68
+ from linkedapi.webhooks import parse_webhook_event
67
69
 
68
- __version__ = "1.0.2"
70
+ __version__ = "1.1.0"
69
71
  PredefinedOperation = Operation
70
72
 
71
73
  __all__ = [
@@ -74,6 +76,7 @@ __all__ = [
74
76
  "AdminHttpClient",
75
77
  "AdminLimits",
76
78
  "AdminSubscription",
79
+ "AdminWebhooks",
77
80
  "ActionConfig",
78
81
  "ArrayWorkflowMapper",
79
82
  "BaseMapper",
@@ -129,5 +132,6 @@ __all__ = [
129
132
  "VoidWorkflowMapper",
130
133
  "WithdrawConnectionRequest",
131
134
  "__version__",
135
+ "parse_webhook_event",
132
136
  "poll_workflow_result",
133
137
  ]
@@ -3,11 +3,13 @@ from linkedapi.admin.admin import LinkedApiAdmin
3
3
  from linkedapi.admin.http_client import AdminHttpClient
4
4
  from linkedapi.admin.limits import AdminLimits
5
5
  from linkedapi.admin.subscription import AdminSubscription
6
+ from linkedapi.admin.webhooks import AdminWebhooks
6
7
 
7
8
  __all__ = [
8
9
  "AdminAccounts",
9
10
  "AdminHttpClient",
10
11
  "AdminLimits",
11
12
  "AdminSubscription",
13
+ "AdminWebhooks",
12
14
  "LinkedApiAdmin",
13
15
  ]
@@ -11,10 +11,14 @@ from linkedapi.types.admin import (
11
11
  CancelConnectionSessionParams,
12
12
  ConnectionSessionResult,
13
13
  CreateConnectionSessionResult,
14
+ CreateReconnectionSessionParams,
15
+ CreateReconnectionSessionResult,
14
16
  DisconnectParams,
15
17
  GetConnectionSessionParams,
16
18
  RegenerateTokenParams,
17
19
  RegenerateTokenResult,
20
+ ReparseAccountInfoParams,
21
+ ReparseAccountInfoResult,
18
22
  )
19
23
 
20
24
  TModel = TypeVar("TModel", bound=BaseModel)
@@ -30,6 +34,14 @@ class AdminAccounts:
30
34
  def disconnect(self, params: DisconnectParams) -> None:
31
35
  self._post_void("/admin/accounts.disconnect", "Failed to disconnect account", params)
32
36
 
37
+ def reparse_account_info(self, params: ReparseAccountInfoParams) -> ReparseAccountInfoResult:
38
+ return self._post_result(
39
+ "/admin/accounts.reparseAccountInfo",
40
+ ReparseAccountInfoResult,
41
+ "Failed to reparse account info",
42
+ params,
43
+ )
44
+
33
45
  def regenerate_identification_token(
34
46
  self,
35
47
  params: RegenerateTokenParams,
@@ -48,6 +60,17 @@ class AdminAccounts:
48
60
  "Failed to create connection session",
49
61
  )
50
62
 
63
+ def create_reconnection_session(
64
+ self,
65
+ params: CreateReconnectionSessionParams,
66
+ ) -> CreateReconnectionSessionResult:
67
+ return self._post_result(
68
+ "/admin/accounts.createReconnectionSession",
69
+ CreateReconnectionSessionResult,
70
+ "Failed to create reconnection session",
71
+ params,
72
+ )
73
+
51
74
  def get_connection_session(
52
75
  self,
53
76
  params: GetConnectionSessionParams,
@@ -6,12 +6,13 @@ from linkedapi.admin.accounts import AdminAccounts
6
6
  from linkedapi.admin.http_client import AdminHttpClient
7
7
  from linkedapi.admin.limits import AdminLimits
8
8
  from linkedapi.admin.subscription import AdminSubscription
9
+ from linkedapi.admin.webhooks import AdminWebhooks
9
10
  from linkedapi.http import HttpClient
10
11
  from linkedapi.types.admin import AdminConfig
11
12
 
12
13
 
13
14
  class LinkedApiAdmin:
14
- """Admin SDK for Linked API subscription, account, and limit management."""
15
+ """Admin SDK for Linked API subscription, account, limit, and webhook management."""
15
16
 
16
17
  def __init__(self, config: AdminConfig | HttpClient[Any]) -> None:
17
18
  http_client = (
@@ -20,3 +21,4 @@ class LinkedApiAdmin:
20
21
  self.subscription = AdminSubscription(http_client)
21
22
  self.accounts = AdminAccounts(http_client)
22
23
  self.limits = AdminLimits(http_client)
24
+ self.webhooks = AdminWebhooks(http_client)
@@ -7,11 +7,8 @@ from pydantic import BaseModel
7
7
  from linkedapi.errors import LinkedApiError
8
8
  from linkedapi.http import HttpClient
9
9
  from linkedapi.types.admin import (
10
- BillingLinkResult,
11
- CancelResult,
12
10
  SetSeatsParams,
13
11
  SetSeatsResult,
14
- SubscriptionPricingResult,
15
12
  SubscriptionSeatsResult,
16
13
  SubscriptionStatus,
17
14
  )
@@ -33,26 +30,11 @@ class AdminSubscription:
33
30
  "/admin/subscription.getSeats", SubscriptionSeatsResult, "Failed to get seats"
34
31
  )
35
32
 
36
- def get_pricing(self) -> SubscriptionPricingResult:
37
- return self._post_result(
38
- "/admin/subscription.getPricing", SubscriptionPricingResult, "Failed to get pricing"
39
- )
40
-
41
33
  def set_seats(self, params: SetSeatsParams) -> SetSeatsResult:
42
34
  return self._post_result(
43
35
  "/admin/subscription.setSeats", SetSeatsResult, "Failed to set seats", params
44
36
  )
45
37
 
46
- def get_billing_link(self) -> BillingLinkResult:
47
- return self._post_result(
48
- "/admin/subscription.getBillingLink", BillingLinkResult, "Failed to get billing link"
49
- )
50
-
51
- def cancel(self) -> CancelResult:
52
- return self._post_result(
53
- "/admin/subscription.cancel", CancelResult, "Failed to cancel subscription"
54
- )
55
-
56
38
  def _post_result(
57
39
  self,
58
40
  path: str,
@@ -0,0 +1,82 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from linkedapi.errors import LinkedApiError
6
+ from linkedapi.http import HttpClient
7
+ from linkedapi.types.webhooks import (
8
+ DeleteWebhookParams,
9
+ ReplayWebhookDeliveryParams,
10
+ SetWebhookParams,
11
+ SetWebhookPayloadModeParams,
12
+ WebhookDelivery,
13
+ WebhookSubscription,
14
+ )
15
+
16
+
17
+ class AdminWebhooks:
18
+ """Manage the client's registered outbound webhook and inspect recent deliveries.
19
+
20
+ A client may hold at most one active webhook. It receives every event Linked API emits
21
+ (workflow lifecycle + account status changes); filter by the event ``type`` on your receiver.
22
+ """
23
+
24
+ def __init__(self, http_client: HttpClient[Any]) -> None:
25
+ self.http_client = http_client
26
+
27
+ def set(self, params: SetWebhookParams) -> WebhookSubscription:
28
+ result = self._post_result("/admin/webhook.set", "Failed to set webhook", params)
29
+ return WebhookSubscription.model_validate(result["webhook"])
30
+
31
+ def get(self) -> list[WebhookSubscription]:
32
+ result = self._post_result("/admin/webhook.get", "Failed to get webhooks")
33
+ return [WebhookSubscription.model_validate(item) for item in result["webhooks"]]
34
+
35
+ def set_payload_mode(self, params: SetWebhookPayloadModeParams) -> WebhookSubscription:
36
+ result = self._post_result(
37
+ "/admin/webhook.setPayloadMode",
38
+ "Failed to set webhook payload mode",
39
+ params,
40
+ )
41
+ return WebhookSubscription.model_validate(result["webhook"])
42
+
43
+ def delete(self, params: DeleteWebhookParams) -> None:
44
+ self._post_void("/admin/webhook.delete", "Failed to delete webhook", params)
45
+
46
+ def deliveries(self) -> list[WebhookDelivery]:
47
+ result = self._post_result("/admin/webhook.deliveries", "Failed to get webhook deliveries")
48
+ return [WebhookDelivery.model_validate(item) for item in result["deliveries"]]
49
+
50
+ def replay_delivery(self, params: ReplayWebhookDeliveryParams) -> None:
51
+ self._post_void(
52
+ "/admin/webhook.replayDelivery",
53
+ "Failed to replay webhook delivery",
54
+ params,
55
+ )
56
+
57
+ def send_test(self) -> None:
58
+ self._post_void("/admin/webhook.sendTest", "Failed to send test webhook")
59
+
60
+ def _post_result(
61
+ self,
62
+ path: str,
63
+ default_message: str,
64
+ params: Any | None = None,
65
+ ) -> dict[str, Any]:
66
+ response = self.http_client.post(path, params)
67
+ if response.success and response.result is not None:
68
+ result: dict[str, Any] = response.result
69
+ return result
70
+ raise LinkedApiError(
71
+ response.error.type if response.error else "httpError",
72
+ response.error.message if response.error else default_message,
73
+ )
74
+
75
+ def _post_void(self, path: str, default_message: str, params: Any | None = None) -> None:
76
+ response = self.http_client.post(path, params)
77
+ if response.success:
78
+ return
79
+ raise LinkedApiError(
80
+ response.error.type if response.error else "httpError",
81
+ response.error.message if response.error else default_message,
82
+ )
@@ -9,6 +9,8 @@ LinkedApiActionErrorType = Literal[
9
9
  "alreadyPending",
10
10
  "alreadyConnected",
11
11
  "emailRequired",
12
+ "noteTooLong",
13
+ "noteLimitExceeded",
12
14
  "requestNotAllowed",
13
15
  "notPending",
14
16
  "retrievingNotAllowed",
@@ -29,6 +31,8 @@ LINKED_API_ACTION_ERROR_TYPES: tuple[str, ...] = (
29
31
  "alreadyPending",
30
32
  "alreadyConnected",
31
33
  "emailRequired",
34
+ "noteTooLong",
35
+ "noteLimitExceeded",
32
36
  "requestNotAllowed",
33
37
  "notPending",
34
38
  "retrievingNotAllowed",
@@ -4,15 +4,14 @@ from linkedapi.types.admin import (
4
4
  AdminAccount,
5
5
  AdminAccountStatus,
6
6
  AdminConfig,
7
- BillingLinkResult,
8
7
  BillingPeriod,
9
8
  CancelConnectionSessionParams,
10
- CancelResult,
11
9
  ConnectionSession,
12
10
  ConnectionSessionResult,
13
11
  ConnectionSessionStatus,
14
12
  CreateConnectionSessionResult,
15
- Currency,
13
+ CreateReconnectionSessionParams,
14
+ CreateReconnectionSessionResult,
16
15
  DeleteLimitEntry,
17
16
  DeleteLimitsParams,
18
17
  DisconnectParams,
@@ -28,6 +27,8 @@ from linkedapi.types.admin import (
28
27
  PendingConnectionSession,
29
28
  RegenerateTokenParams,
30
29
  RegenerateTokenResult,
30
+ ReparseAccountInfoParams,
31
+ ReparseAccountInfoResult,
31
32
  ResetLimitsParams,
32
33
  SeatType,
33
34
  SetLimitEntry,
@@ -35,8 +36,6 @@ from linkedapi.types.admin import (
35
36
  SetSeatsParams,
36
37
  SetSeatsResult,
37
38
  SetSeatsStatus,
38
- SubscriptionPricingResult,
39
- SubscriptionProduct,
40
39
  SubscriptionSeat,
41
40
  SubscriptionSeatsResult,
42
41
  SubscriptionStatus,
@@ -179,6 +178,26 @@ from linkedapi.types.statistics import (
179
178
  RetrievePerformanceResult,
180
179
  RetrieveSSIResult,
181
180
  )
181
+ from linkedapi.types.webhooks import (
182
+ AccountWebhookEvent,
183
+ AccountWebhookEventData,
184
+ AccountWebhookStatus,
185
+ DeleteWebhookParams,
186
+ ReplayWebhookDeliveryParams,
187
+ SetWebhookParams,
188
+ SetWebhookPayloadModeParams,
189
+ WebhookDelivery,
190
+ WebhookDeliveryStatus,
191
+ WebhookEvent,
192
+ WebhookEventType,
193
+ WebhookPayloadMode,
194
+ WebhookSubscription,
195
+ WebhookTestEvent,
196
+ WebhookTestEventData,
197
+ WorkflowWebhookEvent,
198
+ WorkflowWebhookEventData,
199
+ WorkflowWebhookStatus,
200
+ )
182
201
  from linkedapi.types.workflow import (
183
202
  LinkedApiActionError,
184
203
  WorkflowCancelResponse,
@@ -195,6 +214,9 @@ from linkedapi.types.workflow import (
195
214
 
196
215
  __all__ = [
197
216
  "AccountInfo",
217
+ "AccountWebhookEvent",
218
+ "AccountWebhookEventData",
219
+ "AccountWebhookStatus",
198
220
  "AccountsResult",
199
221
  "AdminAccount",
200
222
  "AdminAccountStatus",
@@ -210,10 +232,8 @@ __all__ = [
210
232
  "BaseFetchPersonParamsWide",
211
233
  "BaseFetchPostParams",
212
234
  "BaseFetchPostParamsWide",
213
- "BillingLinkResult",
214
235
  "BillingPeriod",
215
236
  "CancelConnectionSessionParams",
216
- "CancelResult",
217
237
  "CheckConnectionStatusParams",
218
238
  "CheckConnectionStatusResult",
219
239
  "Comment",
@@ -231,9 +251,11 @@ __all__ = [
231
251
  "CreatePostAttachment",
232
252
  "CreatePostParams",
233
253
  "CreatePostResult",
234
- "Currency",
254
+ "CreateReconnectionSessionParams",
255
+ "CreateReconnectionSessionResult",
235
256
  "DeleteLimitEntry",
236
257
  "DeleteLimitsParams",
258
+ "DeleteWebhookParams",
237
259
  "DisconnectParams",
238
260
  "EmploymentType",
239
261
  "FetchCompanyParams",
@@ -315,6 +337,9 @@ __all__ = [
315
337
  "RegenerateTokenParams",
316
338
  "RegenerateTokenResult",
317
339
  "RemoveConnectionParams",
340
+ "ReparseAccountInfoParams",
341
+ "ReparseAccountInfoResult",
342
+ "ReplayWebhookDeliveryParams",
318
343
  "ResetLimitsParams",
319
344
  "RetrieveConnectionsFilter",
320
345
  "RetrieveConnectionsParams",
@@ -341,17 +366,25 @@ __all__ = [
341
366
  "SetSeatsParams",
342
367
  "SetSeatsResult",
343
368
  "SetSeatsStatus",
369
+ "SetWebhookParams",
370
+ "SetWebhookPayloadModeParams",
344
371
  "StCompanyDm",
345
372
  "StCompanyEmployee",
346
373
  "StCompanyEmployeesFilter",
347
374
  "StCompanyEmployeesRetrievalConfig",
348
- "SubscriptionPricingResult",
349
- "SubscriptionProduct",
350
375
  "SubscriptionSeat",
351
376
  "SubscriptionSeatsResult",
352
377
  "SubscriptionStatus",
353
378
  "SubscriptionStatusValue",
354
379
  "SyncConversationParams",
380
+ "WebhookDelivery",
381
+ "WebhookDeliveryStatus",
382
+ "WebhookEvent",
383
+ "WebhookEventType",
384
+ "WebhookPayloadMode",
385
+ "WebhookSubscription",
386
+ "WebhookTestEvent",
387
+ "WebhookTestEventData",
355
388
  "WithdrawConnectionRequestParams",
356
389
  "WorkflowCancelResponse",
357
390
  "WorkflowCompletion",
@@ -363,6 +396,9 @@ __all__ = [
363
396
  "WorkflowResponse",
364
397
  "WorkflowStartedResponse",
365
398
  "WorkflowStatus",
399
+ "WorkflowWebhookEvent",
400
+ "WorkflowWebhookEventData",
401
+ "WorkflowWebhookStatus",
366
402
  "YearsOfExperience",
367
403
  "dump_model_by_name",
368
404
  "serialize_model",
@@ -7,11 +7,15 @@ from linkedapi.types.admin.accounts import (
7
7
  ConnectionSessionResult,
8
8
  ConnectionSessionStatus,
9
9
  CreateConnectionSessionResult,
10
+ CreateReconnectionSessionParams,
11
+ CreateReconnectionSessionResult,
10
12
  DisconnectParams,
11
13
  GetConnectionSessionParams,
12
14
  PendingConnectionSession,
13
15
  RegenerateTokenParams,
14
16
  RegenerateTokenResult,
17
+ ReparseAccountInfoParams,
18
+ ReparseAccountInfoResult,
15
19
  )
16
20
  from linkedapi.types.admin.config import AdminConfig
17
21
  from linkedapi.types.admin.limits import (
@@ -30,16 +34,11 @@ from linkedapi.types.admin.limits import (
30
34
  SetLimitsParams,
31
35
  )
32
36
  from linkedapi.types.admin.subscription import (
33
- BillingLinkResult,
34
37
  BillingPeriod,
35
- CancelResult,
36
- Currency,
37
38
  SeatType,
38
39
  SetSeatsParams,
39
40
  SetSeatsResult,
40
41
  SetSeatsStatus,
41
- SubscriptionPricingResult,
42
- SubscriptionProduct,
43
42
  SubscriptionSeat,
44
43
  SubscriptionSeatsResult,
45
44
  SubscriptionStatus,
@@ -51,15 +50,14 @@ __all__ = [
51
50
  "AdminAccount",
52
51
  "AdminAccountStatus",
53
52
  "AdminConfig",
54
- "BillingLinkResult",
55
53
  "BillingPeriod",
56
54
  "CancelConnectionSessionParams",
57
- "CancelResult",
58
55
  "ConnectionSession",
59
56
  "ConnectionSessionResult",
60
57
  "ConnectionSessionStatus",
61
58
  "CreateConnectionSessionResult",
62
- "Currency",
59
+ "CreateReconnectionSessionParams",
60
+ "CreateReconnectionSessionResult",
63
61
  "DeleteLimitEntry",
64
62
  "DeleteLimitsParams",
65
63
  "DisconnectParams",
@@ -75,6 +73,8 @@ __all__ = [
75
73
  "PendingConnectionSession",
76
74
  "RegenerateTokenParams",
77
75
  "RegenerateTokenResult",
76
+ "ReparseAccountInfoParams",
77
+ "ReparseAccountInfoResult",
78
78
  "ResetLimitsParams",
79
79
  "SeatType",
80
80
  "SetLimitEntry",
@@ -82,8 +82,6 @@ __all__ = [
82
82
  "SetSeatsParams",
83
83
  "SetSeatsResult",
84
84
  "SetSeatsStatus",
85
- "SubscriptionPricingResult",
86
- "SubscriptionProduct",
87
85
  "SubscriptionSeat",
88
86
  "SubscriptionSeatsResult",
89
87
  "SubscriptionStatus",
@@ -20,10 +20,15 @@ ConnectionSessionStatus = Literal[
20
20
  class AdminAccount(LinkedApiModel):
21
21
  id: str | None = None
22
22
  name: str | None = None
23
+ url: str | None = None
24
+ avatar_url: str | None = None
25
+ headline: str | None = None
23
26
  country_code: str | None = None
24
27
  identification_token: str | None = None
25
28
  status: AdminAccountStatus | None = None
26
29
  connected_at: str | None = None
30
+ reconnection_session_id: str | None = None
31
+ reconnection_link: str | None = None
27
32
 
28
33
 
29
34
  class PendingConnectionSession(LinkedApiModel):
@@ -40,6 +45,14 @@ class DisconnectParams(LinkedApiModel):
40
45
  account_id: str
41
46
 
42
47
 
48
+ class ReparseAccountInfoParams(LinkedApiModel):
49
+ account_id: str
50
+
51
+
52
+ class ReparseAccountInfoResult(LinkedApiModel):
53
+ workflow_id: str | None = None
54
+
55
+
43
56
  class RegenerateTokenParams(LinkedApiModel):
44
57
  account_id: str
45
58
 
@@ -53,6 +66,15 @@ class CreateConnectionSessionResult(LinkedApiModel):
53
66
  connection_link: str | None = None
54
67
 
55
68
 
69
+ class CreateReconnectionSessionParams(LinkedApiModel):
70
+ account_id: str
71
+
72
+
73
+ class CreateReconnectionSessionResult(LinkedApiModel):
74
+ reconnection_session_id: str | None = None
75
+ reconnection_link: str | None = None
76
+
77
+
56
78
  class GetConnectionSessionParams(LinkedApiModel):
57
79
  session_id: str
58
80
 
@@ -7,7 +7,6 @@ from linkedapi.types.base import LinkedApiModel
7
7
  SubscriptionStatusValue = Literal["active", "trialing", "past_due", "canceled"]
8
8
  SeatType = Literal["core", "plus"]
9
9
  BillingPeriod = Literal["month", "year"]
10
- Currency = Literal["usd", "eur"]
11
10
  SetSeatsStatus = Literal["complete", "processing"]
12
11
 
13
12
 
@@ -23,14 +22,6 @@ class SubscriptionSeat(LinkedApiModel):
23
22
  billing_period: BillingPeriod | None = None
24
23
 
25
24
 
26
- class SubscriptionProduct(LinkedApiModel):
27
- id: str | None = None
28
- seat_type: SeatType | None = None
29
- billing_period: BillingPeriod | None = None
30
- unit_price: int | None = None
31
- currency: Currency | None = None
32
-
33
-
34
25
  class SetSeatsParams(LinkedApiModel):
35
26
  quantity: int
36
27
  seat_type: SeatType
@@ -42,17 +33,5 @@ class SetSeatsResult(LinkedApiModel):
42
33
  payment_link: str | None = None
43
34
 
44
35
 
45
- class BillingLinkResult(LinkedApiModel):
46
- stripe_link: str | None = None
47
-
48
-
49
- class CancelResult(LinkedApiModel):
50
- cancel_at_date: str | None = None
51
-
52
-
53
36
  class SubscriptionSeatsResult(LinkedApiModel):
54
37
  seats: list[SubscriptionSeat] | None = None
55
-
56
-
57
- class SubscriptionPricingResult(LinkedApiModel):
58
- products: list[SubscriptionProduct] | None = None
@@ -0,0 +1,105 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Literal
4
+
5
+ from linkedapi.types.base import LinkedApiModel
6
+
7
+ WebhookPayloadMode = Literal["thin", "fat"]
8
+ WebhookEventType = Literal[
9
+ "workflow.created",
10
+ "workflow.started",
11
+ "workflow.completed",
12
+ "account.active",
13
+ "account.reconnectionRequired",
14
+ "account.frozen",
15
+ "account.deleted",
16
+ "webhook.test",
17
+ ]
18
+ WebhookDeliveryStatus = Literal["pending", "delivering", "success", "failed"]
19
+ WorkflowWebhookStatus = Literal["pending", "running", "completed", "failed"]
20
+ AccountWebhookStatus = Literal["active", "reconnection_required", "frozen", "deleted"]
21
+
22
+
23
+ class WebhookSubscription(LinkedApiModel):
24
+ id: str | None = None
25
+ url: str | None = None
26
+ payload_mode: WebhookPayloadMode | None = None
27
+ is_active: bool | None = None
28
+ created_at: str | None = None
29
+
30
+
31
+ class WebhookDelivery(LinkedApiModel):
32
+ id: str | None = None
33
+ event_type: WebhookEventType | None = None
34
+ event_id: str | None = None
35
+ status: WebhookDeliveryStatus | None = None
36
+ attempts: int | None = None
37
+ response_status_code: int | None = None
38
+ last_error: str | None = None
39
+ created_at: str | None = None
40
+ updated_at: str | None = None
41
+
42
+
43
+ class SetWebhookParams(LinkedApiModel):
44
+ url: str
45
+ payload_mode: WebhookPayloadMode | None = None
46
+
47
+
48
+ class SetWebhookPayloadModeParams(LinkedApiModel):
49
+ id: str
50
+ payload_mode: WebhookPayloadMode
51
+
52
+
53
+ class DeleteWebhookParams(LinkedApiModel):
54
+ id: str
55
+
56
+
57
+ class ReplayWebhookDeliveryParams(LinkedApiModel):
58
+ delivery_id: str
59
+
60
+
61
+ class WorkflowWebhookEventData(LinkedApiModel):
62
+ workflow_id: str | None = None
63
+ account_id: str | None = None
64
+ status: WorkflowWebhookStatus | None = None
65
+ # Present only on workflow.completed delivered in `fat` payload mode; in `thin` mode fetch the
66
+ # result via the workflow API by workflow_id.
67
+ result: Any | None = None
68
+
69
+
70
+ class WorkflowWebhookEvent(LinkedApiModel):
71
+ id: str
72
+ type: Literal["workflow.created", "workflow.started", "workflow.completed"]
73
+ created_at: str | None = None
74
+ data: WorkflowWebhookEventData
75
+
76
+
77
+ class AccountWebhookEventData(LinkedApiModel):
78
+ account_id: str | None = None
79
+ status: AccountWebhookStatus | None = None
80
+
81
+
82
+ class AccountWebhookEvent(LinkedApiModel):
83
+ id: str
84
+ type: Literal[
85
+ "account.active",
86
+ "account.reconnectionRequired",
87
+ "account.frozen",
88
+ "account.deleted",
89
+ ]
90
+ created_at: str | None = None
91
+ data: AccountWebhookEventData
92
+
93
+
94
+ class WebhookTestEventData(LinkedApiModel):
95
+ message: str | None = None
96
+
97
+
98
+ class WebhookTestEvent(LinkedApiModel):
99
+ id: str
100
+ type: Literal["webhook.test"]
101
+ created_at: str | None = None
102
+ data: WebhookTestEventData
103
+
104
+
105
+ WebhookEvent = WorkflowWebhookEvent | AccountWebhookEvent | WebhookTestEvent
@@ -0,0 +1,3 @@
1
+ from linkedapi.webhooks.parse import parse_webhook_event
2
+
3
+ __all__ = ["parse_webhook_event"]
@@ -0,0 +1,48 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+
5
+ from linkedapi.types.webhooks import (
6
+ AccountWebhookEvent,
7
+ WebhookEvent,
8
+ WebhookTestEvent,
9
+ WorkflowWebhookEvent,
10
+ )
11
+
12
+
13
+ def parse_webhook_event(raw_body: str | bytes) -> WebhookEvent:
14
+ """Parse a raw webhook request body into a typed Linked API event.
15
+
16
+ Pass the raw HTTP body (``str`` or ``bytes``) exactly as received, then branch on the
17
+ returned event's ``type``.
18
+
19
+ Raises:
20
+ ValueError: when the body is not valid JSON or is missing the ``id`` / ``type`` fields,
21
+ or carries an unknown event type.
22
+ """
23
+ text = raw_body.decode("utf-8") if isinstance(raw_body, (bytes, bytearray)) else raw_body
24
+
25
+ try:
26
+ parsed = json.loads(text)
27
+ except (ValueError, TypeError) as error:
28
+ msg = "Invalid webhook payload: body is not valid JSON"
29
+ raise ValueError(msg) from error
30
+
31
+ if (
32
+ not isinstance(parsed, dict)
33
+ or not isinstance(parsed.get("id"), str)
34
+ or not isinstance(parsed.get("type"), str)
35
+ ):
36
+ msg = 'Invalid webhook payload: missing "id" or "type"'
37
+ raise ValueError(msg)
38
+
39
+ event_type: str = parsed["type"]
40
+ if event_type.startswith("workflow."):
41
+ return WorkflowWebhookEvent.model_validate(parsed)
42
+ if event_type.startswith("account."):
43
+ return AccountWebhookEvent.model_validate(parsed)
44
+ if event_type == "webhook.test":
45
+ return WebhookTestEvent.model_validate(parsed)
46
+
47
+ msg = f"Unknown webhook event type: {event_type}"
48
+ raise ValueError(msg)
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "linkedapi"
7
- version = "1.0.2"
7
+ version = "1.1.0"
8
8
  description = "Official synchronous Python SDK for Linked API."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -8,6 +8,7 @@ from linkedapi import (
8
8
  AdminConfig,
9
9
  AdminHttpClient,
10
10
  CancelConnectionSessionParams,
11
+ CreateReconnectionSessionParams,
11
12
  DeleteLimitEntry,
12
13
  DeleteLimitsParams,
13
14
  DisconnectParams,
@@ -16,6 +17,7 @@ from linkedapi import (
16
17
  GetLimitsUsageParams,
17
18
  LinkedApiAdmin,
18
19
  RegenerateTokenParams,
20
+ ReparseAccountInfoParams,
19
21
  ResetLimitsParams,
20
22
  SetLimitEntry,
21
23
  SetLimitsParams,
@@ -59,14 +61,16 @@ def test_admin_methods_use_node_paths(requests_mock: requests_mock.Mocker) -> No
59
61
  {"status": "active", "eligibleForTrial": False, "cancelAtPeriodEnd": False},
60
62
  ),
61
63
  ("/admin/subscription.getSeats", {"seats": []}),
62
- ("/admin/subscription.getPricing", {"products": []}),
63
64
  ("/admin/subscription.setSeats", {"status": "complete"}),
64
- ("/admin/subscription.getBillingLink", {"stripeLink": "https://stripe.test"}),
65
- ("/admin/subscription.cancel", {"cancelAtDate": "2026-01-01"}),
66
65
  ("/admin/accounts.getAll", {"accounts": [], "pendingConnectionSessions": []}),
67
66
  ("/admin/accounts.disconnect", None),
67
+ ("/admin/accounts.reparseAccountInfo", {"workflowId": "workflow-1"}),
68
68
  ("/admin/accounts.regenerateIdentificationToken", {"token": "new"}),
69
69
  ("/admin/accounts.createConnectionSession", {"sessionId": "s1", "connectionLink": "u"}),
70
+ (
71
+ "/admin/accounts.createReconnectionSession",
72
+ {"reconnectionSessionId": "s2", "reconnectionLink": "u2"},
73
+ ),
70
74
  (
71
75
  "/admin/accounts.getConnectionSession",
72
76
  {"session": {"sessionId": "s1", "status": "pending", "type": "connect"}},
@@ -87,16 +91,15 @@ def test_admin_methods_use_node_paths(requests_mock: requests_mock.Mocker) -> No
87
91
 
88
92
  admin.subscription.get_status()
89
93
  admin.subscription.get_seats()
90
- admin.subscription.get_pricing()
91
94
  admin.subscription.set_seats(
92
95
  SetSeatsParams(quantity=1, seat_type="core", billing_period="month")
93
96
  )
94
- admin.subscription.get_billing_link()
95
- admin.subscription.cancel()
96
97
  admin.accounts.get_all()
97
98
  admin.accounts.disconnect(DisconnectParams(account_id="a1"))
99
+ admin.accounts.reparse_account_info(ReparseAccountInfoParams(account_id="a1"))
98
100
  admin.accounts.regenerate_identification_token(RegenerateTokenParams(account_id="a1"))
99
101
  admin.accounts.create_connection_session()
102
+ admin.accounts.create_reconnection_session(CreateReconnectionSessionParams(account_id="a1"))
100
103
  admin.accounts.get_connection_session(GetConnectionSessionParams(session_id="s1"))
101
104
  admin.accounts.cancel_connection_session(CancelConnectionSessionParams(session_id="s1"))
102
105
  admin.limits.get_defaults()
@@ -35,6 +35,8 @@ def test_error_type_sets_match_node_contract() -> None:
35
35
  "alreadyPending",
36
36
  "alreadyConnected",
37
37
  "emailRequired",
38
+ "noteTooLong",
39
+ "noteLimitExceeded",
38
40
  "requestNotAllowed",
39
41
  "notPending",
40
42
  "retrievingNotAllowed",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes