sendbee-api 1.7.2__tar.gz → 1.8.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 (55) hide show
  1. {sendbee_api-1.7.2/sendbee_api.egg-info → sendbee_api-1.8.0}/PKG-INFO +79 -3
  2. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/README.md +78 -2
  3. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/pyproject.toml +1 -1
  4. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/client.py +2 -1
  5. sendbee_api-1.8.0/sendbee_api/webhooks/__init__.py +0 -0
  6. sendbee_api-1.8.0/sendbee_api/webhooks/client.py +16 -0
  7. sendbee_api-1.8.0/sendbee_api/webhooks/models.py +22 -0
  8. sendbee_api-1.8.0/sendbee_api/webhooks/query_params.py +9 -0
  9. {sendbee_api-1.7.2 → sendbee_api-1.8.0/sendbee_api.egg-info}/PKG-INFO +79 -3
  10. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api.egg-info/SOURCES.txt +4 -0
  11. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/tests/test_endpoints_smoke.py +39 -1
  12. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/LICENSE +0 -0
  13. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/__init__.py +0 -0
  14. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/auth.py +0 -0
  15. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/automation/__init__.py +0 -0
  16. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/automation/client.py +0 -0
  17. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/automation/models.py +0 -0
  18. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/automation/query_params.py +0 -0
  19. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/bind.py +0 -0
  20. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/constants.py +0 -0
  21. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/contacts/__init__.py +0 -0
  22. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/contacts/client.py +0 -0
  23. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/contacts/models.py +0 -0
  24. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/contacts/query_params.py +0 -0
  25. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/conversations/__init__.py +0 -0
  26. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/conversations/client.py +0 -0
  27. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/conversations/models.py +0 -0
  28. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/conversations/query_params.py +0 -0
  29. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/debug.py +0 -0
  30. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/exceptions.py +0 -0
  31. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/fields.py +0 -0
  32. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/formatter.py +0 -0
  33. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/models.py +0 -0
  34. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/query_params.py +0 -0
  35. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/rate_limit/__init__.py +0 -0
  36. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/rate_limit/client.py +0 -0
  37. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/rate_limit/models.py +0 -0
  38. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/rate_limit/query_params.py +0 -0
  39. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/response.py +0 -0
  40. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/teams/__init__.py +0 -0
  41. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/teams/client.py +0 -0
  42. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/teams/models.py +0 -0
  43. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api/teams/query_params.py +0 -0
  44. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api.egg-info/dependency_links.txt +0 -0
  45. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api.egg-info/requires.txt +0 -0
  46. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/sendbee_api.egg-info/top_level.txt +0 -0
  47. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/setup.cfg +0 -0
  48. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/tests/test_auth.py +0 -0
  49. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/tests/test_bind_request.py +0 -0
  50. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/tests/test_client.py +0 -0
  51. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/tests/test_fields.py +0 -0
  52. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/tests/test_formatter.py +0 -0
  53. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/tests/test_models.py +0 -0
  54. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/tests/test_query_params.py +0 -0
  55. {sendbee_api-1.7.2 → sendbee_api-1.8.0}/tests/test_response.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sendbee_api
3
- Version: 1.7.2
3
+ Version: 1.8.0
4
4
  Summary: Python client SDK for Sendbee Public API
5
5
  Author-email: Sendbee ltd <info@sendbee.io>
6
6
  License: MIT
@@ -33,6 +33,10 @@ Requires-Dist: pytest; extra == "dev"
33
33
  Requires-Dist: responses>=0.23; extra == "dev"
34
34
  Dynamic: license-file
35
35
 
36
+ <p align="center">
37
+ <img src="docs/images/number-logo-tm.png" alt="AI Number">
38
+ </p>
39
+
36
40
  # AI Number Python API Client
37
41
 
38
42
  [![PyPI version](https://badge.fury.io/py/sendbee-api.svg)](https://badge.fury.io/py/sendbee-api)
@@ -80,6 +84,10 @@ Dynamic: license-file
80
84
  - [Managing chatbot (automated responses) status settings](#bot-on-off)
81
85
  - [Get chatbot (automated responses) status](#bot-status)
82
86
 
87
+ #### Webhooks
88
+
89
+ - [Subscribe to webhook](#subscribe-webhook)
90
+
83
91
  #### Mics
84
92
 
85
93
  - [Pagination](#pagination)
@@ -557,6 +565,59 @@ response.chatbot_active # True/False
557
565
 
558
566
  ```
559
567
 
568
+ ## Webhooks
569
+
570
+ ### <a href='#subscribe-webhook'>Subscribe to webhook</a>
571
+
572
+ Register a URL of yours and subscribe it to a webhook event, so we start sending
573
+ you requests when that event happens. This does the same thing as activating a
574
+ webhook URL in the Sendbee Dashboard.
575
+
576
+ ```python
577
+ webhook = api.subscribe_webhook(
578
+ url='https://example.com/sendbee/webhook',
579
+ event='message.received'
580
+ )
581
+
582
+ webhook.id # UUID of the webhook endpoint
583
+ webhook.url # the URL you registered
584
+ webhook.active # True/False
585
+ webhook.selected_webhooks # list of all events this URL is subscribed to
586
+ webhook.secret_key # key used to sign requests we send to your URL
587
+ webhook.created_at
588
+ ```
589
+
590
+ Both `url` and `event` are required. Calling it again for the same `url` is safe -
591
+ it will not create a duplicate, it just adds the new event to that URL (and
592
+ re-activates it if it was deactivated), so subscribe one event per call:
593
+
594
+ ```python
595
+ for event in ('message.received', 'message.sent'):
596
+ api.subscribe_webhook(url='https://example.com/sendbee/webhook', event=event)
597
+ ```
598
+
599
+ Available events:
600
+
601
+ | Event | Description |
602
+ | --- | --- |
603
+ | `contact.created` | A new contact was created |
604
+ | `contact.subscription_created` | A contact subscribed |
605
+ | `contact.subscription_updated` | A contact's subscription changed |
606
+ | `contact.unsubscribed` | A contact unsubscribed |
607
+ | `message.received` | A message was received from a contact |
608
+ | `message.sent` | A message was sent to a contact |
609
+ | `message.sent_status` | Delivery status of a sent message changed |
610
+ | `whatsapp.message.session.sent_status` | Delivery status of a session message changed |
611
+ | `whatsapp.message.template.sent_status` | Delivery status of a template message changed |
612
+ | `whatsapp.message.template.approval_status` | A message template was approved or rejected |
613
+ | `conversation.change_folder` | A conversation was moved to another folder |
614
+
615
+ An unknown event name returns a `SendbeeRequestApiException`. For the authoritative
616
+ and up to date list, see the [documentation](https://developer.ainumber.com/#webhooks).
617
+
618
+ Once subscribed, see [Authenticate webhook request](#authenticate-webhook-request)
619
+ for how to verify the requests we send you.
620
+
560
621
  ## Misc
561
622
 
562
623
  ### <a href='#pagination'>Pagination</a>
@@ -642,7 +703,7 @@ except SendbeeRequestApiException as e:
642
703
 
643
704
  ### <a href='#authenticate-webhook-request'>Authenticate webhook request</a>
644
705
 
645
- After activating your webhook URL in Sendbee Dashboard, we will start sending requests on that URL depending on which webhook type is linked with that webhook URL.
706
+ After activating your webhook URL in Sendbee Dashboard or with [Subscribe to webhook](#subscribe-webhook), we will start sending requests on that URL depending on which webhook type is linked with that webhook URL.
646
707
  Every request that we make will have authorization token in header, like this:
647
708
 
648
709
  ```
@@ -665,9 +726,24 @@ if not api.auth.check_auth_token(token):
665
726
  # error! authentication failed!
666
727
  ```
667
728
 
729
+ Requests to a webhook URL that has its own secret key also carry a second header,
730
+ `X-Auth-Token`, signed with that key instead of your account secret. You get the
731
+ key back as `webhook.secret_key` from [Subscribe to webhook](#subscribe-webhook).
732
+ Check it the same way, using `SendbeeAuth` directly:
733
+
734
+ ```python
735
+ from sendbee_api import SendbeeAuth
736
+
737
+ secret_key = '...' # webhook.secret_key, store it when you subscribe
738
+
739
+ token = '...' # taken from the 'X-Auth-Token' request header
740
+ if not SendbeeAuth(secret_key).check_auth_token(token):
741
+ # error! authentication failed!
742
+ ```
743
+
668
744
  ### <a href='#warnings'>Warnings</a>
669
745
 
670
- Sometimes APi returns a worning so you could be warned about something.
746
+ Sometimes APi returns a warning so you could be warned about something.
671
747
  The waning is displayed in standard output:
672
748
 
673
749
  ![Debugging](docs/images/warning.png)
@@ -1,3 +1,7 @@
1
+ <p align="center">
2
+ <img src="docs/images/number-logo-tm.png" alt="AI Number">
3
+ </p>
4
+
1
5
  # AI Number Python API Client
2
6
 
3
7
  [![PyPI version](https://badge.fury.io/py/sendbee-api.svg)](https://badge.fury.io/py/sendbee-api)
@@ -45,6 +49,10 @@
45
49
  - [Managing chatbot (automated responses) status settings](#bot-on-off)
46
50
  - [Get chatbot (automated responses) status](#bot-status)
47
51
 
52
+ #### Webhooks
53
+
54
+ - [Subscribe to webhook](#subscribe-webhook)
55
+
48
56
  #### Mics
49
57
 
50
58
  - [Pagination](#pagination)
@@ -522,6 +530,59 @@ response.chatbot_active # True/False
522
530
 
523
531
  ```
524
532
 
533
+ ## Webhooks
534
+
535
+ ### <a href='#subscribe-webhook'>Subscribe to webhook</a>
536
+
537
+ Register a URL of yours and subscribe it to a webhook event, so we start sending
538
+ you requests when that event happens. This does the same thing as activating a
539
+ webhook URL in the Sendbee Dashboard.
540
+
541
+ ```python
542
+ webhook = api.subscribe_webhook(
543
+ url='https://example.com/sendbee/webhook',
544
+ event='message.received'
545
+ )
546
+
547
+ webhook.id # UUID of the webhook endpoint
548
+ webhook.url # the URL you registered
549
+ webhook.active # True/False
550
+ webhook.selected_webhooks # list of all events this URL is subscribed to
551
+ webhook.secret_key # key used to sign requests we send to your URL
552
+ webhook.created_at
553
+ ```
554
+
555
+ Both `url` and `event` are required. Calling it again for the same `url` is safe -
556
+ it will not create a duplicate, it just adds the new event to that URL (and
557
+ re-activates it if it was deactivated), so subscribe one event per call:
558
+
559
+ ```python
560
+ for event in ('message.received', 'message.sent'):
561
+ api.subscribe_webhook(url='https://example.com/sendbee/webhook', event=event)
562
+ ```
563
+
564
+ Available events:
565
+
566
+ | Event | Description |
567
+ | --- | --- |
568
+ | `contact.created` | A new contact was created |
569
+ | `contact.subscription_created` | A contact subscribed |
570
+ | `contact.subscription_updated` | A contact's subscription changed |
571
+ | `contact.unsubscribed` | A contact unsubscribed |
572
+ | `message.received` | A message was received from a contact |
573
+ | `message.sent` | A message was sent to a contact |
574
+ | `message.sent_status` | Delivery status of a sent message changed |
575
+ | `whatsapp.message.session.sent_status` | Delivery status of a session message changed |
576
+ | `whatsapp.message.template.sent_status` | Delivery status of a template message changed |
577
+ | `whatsapp.message.template.approval_status` | A message template was approved or rejected |
578
+ | `conversation.change_folder` | A conversation was moved to another folder |
579
+
580
+ An unknown event name returns a `SendbeeRequestApiException`. For the authoritative
581
+ and up to date list, see the [documentation](https://developer.ainumber.com/#webhooks).
582
+
583
+ Once subscribed, see [Authenticate webhook request](#authenticate-webhook-request)
584
+ for how to verify the requests we send you.
585
+
525
586
  ## Misc
526
587
 
527
588
  ### <a href='#pagination'>Pagination</a>
@@ -607,7 +668,7 @@ except SendbeeRequestApiException as e:
607
668
 
608
669
  ### <a href='#authenticate-webhook-request'>Authenticate webhook request</a>
609
670
 
610
- After activating your webhook URL in Sendbee Dashboard, we will start sending requests on that URL depending on which webhook type is linked with that webhook URL.
671
+ After activating your webhook URL in Sendbee Dashboard or with [Subscribe to webhook](#subscribe-webhook), we will start sending requests on that URL depending on which webhook type is linked with that webhook URL.
611
672
  Every request that we make will have authorization token in header, like this:
612
673
 
613
674
  ```
@@ -630,9 +691,24 @@ if not api.auth.check_auth_token(token):
630
691
  # error! authentication failed!
631
692
  ```
632
693
 
694
+ Requests to a webhook URL that has its own secret key also carry a second header,
695
+ `X-Auth-Token`, signed with that key instead of your account secret. You get the
696
+ key back as `webhook.secret_key` from [Subscribe to webhook](#subscribe-webhook).
697
+ Check it the same way, using `SendbeeAuth` directly:
698
+
699
+ ```python
700
+ from sendbee_api import SendbeeAuth
701
+
702
+ secret_key = '...' # webhook.secret_key, store it when you subscribe
703
+
704
+ token = '...' # taken from the 'X-Auth-Token' request header
705
+ if not SendbeeAuth(secret_key).check_auth_token(token):
706
+ # error! authentication failed!
707
+ ```
708
+
633
709
  ### <a href='#warnings'>Warnings</a>
634
710
 
635
- Sometimes APi returns a worning so you could be warned about something.
711
+ Sometimes APi returns a warning so you could be warned about something.
636
712
  The waning is displayed in standard output:
637
713
 
638
714
  ![Debugging](docs/images/warning.png)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "sendbee_api"
7
- version = "1.7.2"
7
+ version = "1.8.0"
8
8
  description = "Python client SDK for Sendbee Public API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.6"
@@ -3,13 +3,14 @@ import click
3
3
  from sendbee_api.auth import SendbeeAuth
4
4
  from sendbee_api.teams.client import Teams
5
5
  from sendbee_api.contacts.client import Contacts
6
+ from sendbee_api.webhooks.client import Webhooks
6
7
  from sendbee_api.rate_limit.client import RateLimit
7
8
  from sendbee_api.automation.client import Automation
8
9
  from sendbee_api.conversations.client import Messages
9
10
  from sendbee_api.exceptions import SendbeeRequestApiException
10
11
 
11
12
 
12
- class Client(Contacts, Messages, Automation, Teams, RateLimit):
13
+ class Client(Contacts, Messages, Automation, Teams, Webhooks, RateLimit):
13
14
  """Main API class. Sets all API calls."""
14
15
 
15
16
  base_url = 'api-v2.sendbee.io'
File without changes
@@ -0,0 +1,16 @@
1
+ from sendbee_api import constants
2
+ from sendbee_api.webhooks import models
3
+ from sendbee_api.bind import bind_request
4
+ from sendbee_api.webhooks import query_params
5
+
6
+
7
+ class Webhooks:
8
+ """Api client for webhooks"""
9
+
10
+ subscribe_webhook = bind_request(
11
+ api_path='/webhooks/subscribe',
12
+ model=models.Webhook,
13
+ method=constants.RequestConst.POST,
14
+ query_parameters=query_params.SubscribeWebhook,
15
+ description='Api client for subscribing to a webhook event'
16
+ )
@@ -0,0 +1,22 @@
1
+ from sendbee_api.models import Model
2
+ from sendbee_api.fields import (
3
+ TextField, BooleanField, DatetimeField, ListField
4
+ )
5
+
6
+
7
+ class Webhook(Model):
8
+ """Data model for a webhook endpoint subscription"""
9
+
10
+ _id = TextField(index='id', desc='UUID')
11
+ _url = TextField(index='url', desc='Webhook endpoint URL')
12
+ _active = BooleanField(index='active', desc='Is the endpoint active')
13
+ _secret_key = TextField(
14
+ index='secret_key',
15
+ desc='Key that signs the X-Auth-Token header on inbound webhooks'
16
+ )
17
+ _selected_webhooks = ListField(
18
+ index='selected_webhooks', desc='Subscribed webhook event names'
19
+ )
20
+ _created_at = DatetimeField(
21
+ index='created_at', desc='Created at', format='%Y-%m-%d %H:%M:%'
22
+ )
@@ -0,0 +1,9 @@
1
+ from sendbee_api.query_params import QueryParams
2
+
3
+
4
+ class SubscribeWebhook(QueryParams):
5
+ """Parameters for subscribing to a webhook event"""
6
+
7
+ url = 'url', 'Webhook endpoint URL that will receive the events'
8
+ event = 'event', 'Webhook event name, e.g. "message.received". ' \
9
+ 'See https://developer.ainumber.com/#webhooks'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sendbee_api
3
- Version: 1.7.2
3
+ Version: 1.8.0
4
4
  Summary: Python client SDK for Sendbee Public API
5
5
  Author-email: Sendbee ltd <info@sendbee.io>
6
6
  License: MIT
@@ -33,6 +33,10 @@ Requires-Dist: pytest; extra == "dev"
33
33
  Requires-Dist: responses>=0.23; extra == "dev"
34
34
  Dynamic: license-file
35
35
 
36
+ <p align="center">
37
+ <img src="docs/images/number-logo-tm.png" alt="AI Number">
38
+ </p>
39
+
36
40
  # AI Number Python API Client
37
41
 
38
42
  [![PyPI version](https://badge.fury.io/py/sendbee-api.svg)](https://badge.fury.io/py/sendbee-api)
@@ -80,6 +84,10 @@ Dynamic: license-file
80
84
  - [Managing chatbot (automated responses) status settings](#bot-on-off)
81
85
  - [Get chatbot (automated responses) status](#bot-status)
82
86
 
87
+ #### Webhooks
88
+
89
+ - [Subscribe to webhook](#subscribe-webhook)
90
+
83
91
  #### Mics
84
92
 
85
93
  - [Pagination](#pagination)
@@ -557,6 +565,59 @@ response.chatbot_active # True/False
557
565
 
558
566
  ```
559
567
 
568
+ ## Webhooks
569
+
570
+ ### <a href='#subscribe-webhook'>Subscribe to webhook</a>
571
+
572
+ Register a URL of yours and subscribe it to a webhook event, so we start sending
573
+ you requests when that event happens. This does the same thing as activating a
574
+ webhook URL in the Sendbee Dashboard.
575
+
576
+ ```python
577
+ webhook = api.subscribe_webhook(
578
+ url='https://example.com/sendbee/webhook',
579
+ event='message.received'
580
+ )
581
+
582
+ webhook.id # UUID of the webhook endpoint
583
+ webhook.url # the URL you registered
584
+ webhook.active # True/False
585
+ webhook.selected_webhooks # list of all events this URL is subscribed to
586
+ webhook.secret_key # key used to sign requests we send to your URL
587
+ webhook.created_at
588
+ ```
589
+
590
+ Both `url` and `event` are required. Calling it again for the same `url` is safe -
591
+ it will not create a duplicate, it just adds the new event to that URL (and
592
+ re-activates it if it was deactivated), so subscribe one event per call:
593
+
594
+ ```python
595
+ for event in ('message.received', 'message.sent'):
596
+ api.subscribe_webhook(url='https://example.com/sendbee/webhook', event=event)
597
+ ```
598
+
599
+ Available events:
600
+
601
+ | Event | Description |
602
+ | --- | --- |
603
+ | `contact.created` | A new contact was created |
604
+ | `contact.subscription_created` | A contact subscribed |
605
+ | `contact.subscription_updated` | A contact's subscription changed |
606
+ | `contact.unsubscribed` | A contact unsubscribed |
607
+ | `message.received` | A message was received from a contact |
608
+ | `message.sent` | A message was sent to a contact |
609
+ | `message.sent_status` | Delivery status of a sent message changed |
610
+ | `whatsapp.message.session.sent_status` | Delivery status of a session message changed |
611
+ | `whatsapp.message.template.sent_status` | Delivery status of a template message changed |
612
+ | `whatsapp.message.template.approval_status` | A message template was approved or rejected |
613
+ | `conversation.change_folder` | A conversation was moved to another folder |
614
+
615
+ An unknown event name returns a `SendbeeRequestApiException`. For the authoritative
616
+ and up to date list, see the [documentation](https://developer.ainumber.com/#webhooks).
617
+
618
+ Once subscribed, see [Authenticate webhook request](#authenticate-webhook-request)
619
+ for how to verify the requests we send you.
620
+
560
621
  ## Misc
561
622
 
562
623
  ### <a href='#pagination'>Pagination</a>
@@ -642,7 +703,7 @@ except SendbeeRequestApiException as e:
642
703
 
643
704
  ### <a href='#authenticate-webhook-request'>Authenticate webhook request</a>
644
705
 
645
- After activating your webhook URL in Sendbee Dashboard, we will start sending requests on that URL depending on which webhook type is linked with that webhook URL.
706
+ After activating your webhook URL in Sendbee Dashboard or with [Subscribe to webhook](#subscribe-webhook), we will start sending requests on that URL depending on which webhook type is linked with that webhook URL.
646
707
  Every request that we make will have authorization token in header, like this:
647
708
 
648
709
  ```
@@ -665,9 +726,24 @@ if not api.auth.check_auth_token(token):
665
726
  # error! authentication failed!
666
727
  ```
667
728
 
729
+ Requests to a webhook URL that has its own secret key also carry a second header,
730
+ `X-Auth-Token`, signed with that key instead of your account secret. You get the
731
+ key back as `webhook.secret_key` from [Subscribe to webhook](#subscribe-webhook).
732
+ Check it the same way, using `SendbeeAuth` directly:
733
+
734
+ ```python
735
+ from sendbee_api import SendbeeAuth
736
+
737
+ secret_key = '...' # webhook.secret_key, store it when you subscribe
738
+
739
+ token = '...' # taken from the 'X-Auth-Token' request header
740
+ if not SendbeeAuth(secret_key).check_auth_token(token):
741
+ # error! authentication failed!
742
+ ```
743
+
668
744
  ### <a href='#warnings'>Warnings</a>
669
745
 
670
- Sometimes APi returns a worning so you could be warned about something.
746
+ Sometimes APi returns a warning so you could be warned about something.
671
747
  The waning is displayed in standard output:
672
748
 
673
749
  ![Debugging](docs/images/warning.png)
@@ -38,6 +38,10 @@ sendbee_api/teams/__init__.py
38
38
  sendbee_api/teams/client.py
39
39
  sendbee_api/teams/models.py
40
40
  sendbee_api/teams/query_params.py
41
+ sendbee_api/webhooks/__init__.py
42
+ sendbee_api/webhooks/client.py
43
+ sendbee_api/webhooks/models.py
44
+ sendbee_api/webhooks/query_params.py
41
45
  tests/test_auth.py
42
46
  tests/test_bind_request.py
43
47
  tests/test_client.py
@@ -1,6 +1,6 @@
1
1
  """End-to-end smoke tests: one happy-path per resource mixin.
2
2
 
3
- The 24 endpoint methods on `SendbeeApi` are all generated by `bind_request`,
3
+ The 25 endpoint methods on `SendbeeApi` are all generated by `bind_request`,
4
4
  which is covered in detail elsewhere. These tests verify the wiring per mixin
5
5
  - that each `bind_request(...)` call site declares the right `api_path`,
6
6
  `method`, `model`, and `QueryParams` - by exercising one representative
@@ -14,6 +14,7 @@ from sendbee_api.contacts.models import Contact
14
14
  from sendbee_api.conversations.models import Conversation, SentMessage
15
15
  from sendbee_api.automation.models import ChatbotActivityStatus
16
16
  from sendbee_api.teams.models import Team
17
+ from sendbee_api.webhooks.models import Webhook
17
18
  from sendbee_api.rate_limit.models import RateLimitError
18
19
  from sendbee_api.models import ServerMessage
19
20
 
@@ -125,6 +126,43 @@ def test_teams_list_endpoint_returns_team_models(client, register, json_body):
125
126
  assert response.models[0].name == "Support"
126
127
 
127
128
 
129
+ @responses.activate
130
+ def test_subscribe_webhook_endpoint_returns_webhook_model(client, register, json_body):
131
+ """subscribe_webhook POSTs to /webhooks/subscribe and parses one Webhook.
132
+
133
+ The body is a bare object rather than a list because the backend's
134
+ PaginationJsonRenderer emits {"data": {...}} with no meta for non-GET
135
+ requests; Response.models wraps that lone dict before processing it.
136
+ """
137
+ register("POST", "/webhooks/subscribe", body=json_body(
138
+ data={
139
+ "id": "wh1",
140
+ "active": True,
141
+ "url": "https://example.com/hook",
142
+ "secret_key": "s3cr3t",
143
+ "selected_webhooks": ["message.received"],
144
+ "created_at": "2026-07-27 10:11:12",
145
+ },
146
+ ))
147
+
148
+ result = client.subscribe_webhook(
149
+ url="https://example.com/hook", event="message.received"
150
+ )
151
+
152
+ assert responses.calls[0].request.method == "POST"
153
+ assert "/webhooks/subscribe" in responses.calls[0].request.url
154
+
155
+ body = ujson.loads(responses.calls[0].request.body)
156
+ assert body == {"url": "https://example.com/hook", "event": "message.received"}
157
+
158
+ assert isinstance(result, Webhook)
159
+ assert result.id == "wh1"
160
+ assert result.active is True
161
+ assert result.url == "https://example.com/hook"
162
+ assert result.secret_key == "s3cr3t"
163
+ assert result.selected_webhooks == ["message.received"]
164
+
165
+
128
166
  @responses.activate
129
167
  def test_rate_limit_error_test_endpoint_returns_rate_limit_error_model(client, register, json_body):
130
168
  """rate_limit_error_test hits /rate-limit/error-test on the RateLimit mixin."""
File without changes
File without changes