sendly 4.3.0__tar.gz → 4.5.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.
- {sendly-4.3.0 → sendly-4.5.0}/CHANGELOG.md +21 -0
- {sendly-4.3.0 → sendly-4.5.0}/PKG-INFO +98 -28
- {sendly-4.3.0 → sendly-4.5.0}/README.md +97 -27
- {sendly-4.3.0 → sendly-4.5.0}/pyproject.toml +1 -1
- {sendly-4.3.0 → sendly-4.5.0}/sendly/__init__.py +3 -1
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/media.py +5 -4
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/messages.py +19 -6
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/whatsapp.py +28 -33
- {sendly-4.3.0 → sendly-4.5.0}/sendly/types.py +89 -4
- {sendly-4.3.0 → sendly-4.5.0}/sendly/utils/http.py +1 -1
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_media.py +30 -7
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_whatsapp.py +188 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_whatsapp_extras.py +2 -4
- {sendly-4.3.0 → sendly-4.5.0}/.github/header.svg +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/.gitignore +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/examples/async_example.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/examples/error_handling.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/examples/send_sms.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/client.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/errors.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/py.typed +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/__init__.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/account.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/business_upgrade.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/calls.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/campaigns.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/contacts.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/conversations.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/drafts.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/enterprise.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/labels.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/links.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/numbers.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/rcs.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/rules.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/templates.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/tendlc.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/verify.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/voice.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/resources/webhooks.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/utils/__init__.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/utils/validation.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/sendly/webhooks.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/conftest.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_account.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_async_client.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_calls.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_campaigns.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_client.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_contacts.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_conversations.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_enterprise.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_errors.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_http_body_kwarg.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_http_retry.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_idempotency.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_messages.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_messages.py.bak +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_messages_batch.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_messages_schedule.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_messages_wire.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_numbers.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_path_ids.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_rcs.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_rcs_registration.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_rules.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_templates.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_tendlc.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_validation.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_voice.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_webhooks.py +0 -0
- {sendly-4.3.0 → sendly-4.5.0}/tests/test_webhooks_resource.py +0 -0
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# sendly (Python)
|
|
2
2
|
|
|
3
|
+
## 4.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- `WhatsAppMessage.fell_back` is `True` when the message went as an SMS instead (`fallback.status` is `"sent"`), matching Ruby's `fell_back?` and the other SDKs.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- The async `messages.send` docstring said `fallback_to_sms` is RCS only; it documents the WhatsApp fallback too, like the sync one.
|
|
12
|
+
- Docs give 503, the status the API sends, instead of 502 for the WhatsApp and voice errors raised when WhatsApp or the carrier couldn't be reached: `whatsapp_send_failed` when the message never reached WhatsApp (not sent, and safe to send again with the same idempotency key), the WhatsApp signup, profile, conversational-components and calling errors, `voice_attach_failed` and `carrier_refused`. The API answers every 502 as a 503 with a `Sendly-Upstream-Status: 502` header. Nothing changes at runtime.
|
|
13
|
+
|
|
14
|
+
## 4.4.0
|
|
15
|
+
|
|
16
|
+
### Minor Changes
|
|
17
|
+
|
|
18
|
+
- **WhatsApp to SMS fallback.** `messages.send(channel="whatsapp", fallback_to_sms=True)` (sync and async) sends `fallbackToSms`. When WhatsApp can't deliver the message, it goes as an SMS from the same number, billed as an SMS. The new `WhatsAppSmsFallback` (`channel`, `status`, `reason`, `message_id`, `credits_used`, `error`, `content_dropped`) is `WhatsAppMessage.fallback`; when the SMS went in place of the WhatsApp message, `status` is `failed` and `fallback.status` is `sent`. `Message` (from `messages.get` and `list`) gains `fallback`, `fallback_of` (on the SMS, the WhatsApp message it replaced), `message_format`, `batch_id` and `media_urls`.
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- A WhatsApp send that WhatsApp accepted but Sendly couldn't fully record answers 201 without `whatsapp` or `createdAt`, and `messages.send` raised `invalid_response` on it. It now returns the message, with `whatsapp` and `created_at` set to `None`. That message is in flight and was charged, so don't send it again.
|
|
23
|
+
|
|
3
24
|
## 4.3.0
|
|
4
25
|
|
|
5
26
|
### Minor Changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sendly
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.5.0
|
|
4
4
|
Summary: Official Sendly Python SDK for SMS messaging
|
|
5
5
|
Project-URL: Homepage, https://sendly.live
|
|
6
6
|
Project-URL: Documentation, https://sendly.live/docs
|
|
@@ -448,7 +448,7 @@ File uploads (`media.upload()`, `business_upgrade.start()` and `resubmit()`,
|
|
|
448
448
|
make is the `too_many_concurrent_verifications` wait. `whatsapp.signup.verify()`
|
|
449
449
|
and `whatsapp.signup.create()` with a `business_account_id` also raise a
|
|
450
450
|
timeout, a network error or a `5xx` at once, because repeating them can use up
|
|
451
|
-
a verification attempt or start a new
|
|
451
|
+
a verification attempt or start a new signup; they still wait out a
|
|
452
452
|
`429` the API never ran.
|
|
453
453
|
|
|
454
454
|
After the last attempt the SDK raises the error it last saw (`TimeoutError`,
|
|
@@ -1072,9 +1072,10 @@ client.messages.send(
|
|
|
1072
1072
|
|
|
1073
1073
|
Uploads accept JPEG, PNG and GIF up to 600 KB. When `content_type` is one of
|
|
1074
1074
|
those types but the content is not, the upload raises `SendlyError` with code
|
|
1075
|
-
`invalid_file`; any other `content_type
|
|
1076
|
-
|
|
1077
|
-
for your account it is
|
|
1075
|
+
`invalid_file`; any other `content_type` raises `unsupported_media_type`
|
|
1076
|
+
(HTTP 415) with the reason in the message, and a file over 600 KB raises
|
|
1077
|
+
`file_too_large` (HTTP 413). Without MMS enabled for your account it is
|
|
1078
|
+
`feature_disabled`.
|
|
1078
1079
|
|
|
1079
1080
|
## Account & Credits
|
|
1080
1081
|
|
|
@@ -1354,7 +1355,7 @@ client.links.update(link.code, True)
|
|
|
1354
1355
|
|
|
1355
1356
|
## WhatsApp
|
|
1356
1357
|
|
|
1357
|
-
Connect a number you own to WhatsApp (
|
|
1358
|
+
Connect a number you own to WhatsApp (free to connect, no monthly fee), create
|
|
1358
1359
|
Meta-reviewed message templates, and send on the `whatsapp` channel. Connecting
|
|
1359
1360
|
the first number always ends with a human step: hand the `connect_url` to your
|
|
1360
1361
|
user - they open it in a browser and log in with Facebook to link their
|
|
@@ -1386,10 +1387,8 @@ print(f'Open {signup.connect_url} and log in with Facebook')
|
|
|
1386
1387
|
# After the Facebook step it stays 'registering' while WhatsApp activates the
|
|
1387
1388
|
# number. Activation usually takes a few minutes but can take hours. If it
|
|
1388
1389
|
# hasn't finished about 6 hours after the session began, the session fails
|
|
1389
|
-
# with registration_timeout
|
|
1390
|
-
#
|
|
1391
|
-
# is refunded automatically; once a number has connected, a later disconnect
|
|
1392
|
-
# gets nothing back.
|
|
1390
|
+
# with registration_timeout ('failed', with signup.failure_reasons explaining
|
|
1391
|
+
# why).
|
|
1393
1392
|
|
|
1394
1393
|
# 2. Create a template (Meta reviews it, usually 24-48h). category is required
|
|
1395
1394
|
# (UTILITY, AUTHENTICATION or MARKETING) with no default, and an update can't
|
|
@@ -1473,7 +1472,7 @@ components = client.whatsapp.senders.get_conversational_components('+15555550123
|
|
|
1473
1472
|
client.whatsapp.senders.set_calling('+15555550123', enabled=True)
|
|
1474
1473
|
|
|
1475
1474
|
# Add another number to an account you already connected: no Facebook step.
|
|
1476
|
-
#
|
|
1475
|
+
# Free, like a Facebook connection. WhatsApp sends the number a 6-digit code
|
|
1477
1476
|
senders = client.whatsapp.senders.list().senders
|
|
1478
1477
|
account_id = next(
|
|
1479
1478
|
s.business_account_id for s in senders if s.status == 'active' and s.business_account_id
|
|
@@ -1497,14 +1496,14 @@ Senders also carry `business_account_id` (None while `pending`),
|
|
|
1497
1496
|
name on file), `calling_enabled`, and `outbound_calling_allowed`, which is
|
|
1498
1497
|
False for +1, +20, +84 and +234 numbers. A signup that is `verifying` carries
|
|
1499
1498
|
`verification_method` and `verification_attempts_remaining`; after 5 wrong
|
|
1500
|
-
codes it fails with `verification_failed
|
|
1499
|
+
codes it fails with `verification_failed`. Calls carry
|
|
1501
1500
|
`channel` (`phone`, `whatsapp` or `browser`; see `CallChannel`).
|
|
1502
1501
|
|
|
1503
1502
|
`verification_code` comes only from `signup.get()`. Until a code has been
|
|
1504
1503
|
submitted, it is the newest code that has arrived since the signup started, so
|
|
1505
1504
|
after a resend it still shows the earlier code until the new one arrives. Once
|
|
1506
1505
|
WhatsApp has checked a code, only a code that arrived after the last
|
|
1507
|
-
submission or resend is returned. A submission answered with
|
|
1506
|
+
submission or resend is returned. A submission answered with 503
|
|
1508
1507
|
`whatsapp_verification_unavailable` is not counted, so the same unchecked code
|
|
1509
1508
|
can come back, and submitting it again is safe.
|
|
1510
1509
|
|
|
@@ -1530,14 +1529,14 @@ Errors specific to WhatsApp:
|
|
|
1530
1529
|
body carries `retryAfter: 3600` and the response a `Retry-After: 3600`
|
|
1531
1530
|
header. The SDK retries it like any `5xx` before raising;
|
|
1532
1531
|
`e.response.retry_after` says how many seconds to wait before trying again.
|
|
1533
|
-
- `whatsapp_signup_limit_reached` (HTTP 429) after 5 failed
|
|
1532
|
+
- `whatsapp_signup_limit_reached` (HTTP 429) after 5 failed signups in
|
|
1534
1533
|
24 hours. It is final and not retried; try again the next day.
|
|
1535
1534
|
- `whatsapp_send_failed` on a send: HTTP 422 when WhatsApp refused the message,
|
|
1536
1535
|
which is final (cached under the idempotency key and replayed for 24 hours),
|
|
1537
|
-
or HTTP
|
|
1538
|
-
|
|
1539
|
-
retries it
|
|
1540
|
-
charged.
|
|
1536
|
+
or HTTP 503 when the message provably never reached WhatsApp, so it was not
|
|
1537
|
+
sent and is safe to resend under the same idempotency key. A 503 is never
|
|
1538
|
+
cached, and the SDK retries it automatically, up to 3 times under the same
|
|
1539
|
+
key, before raising. Either way the message was not charged.
|
|
1541
1540
|
- `whatsapp_send_unconfirmed` (HTTP 409) on a send: the outcome is unknown.
|
|
1542
1541
|
The message was marked failed and refunded but may still be delivered, so
|
|
1543
1542
|
check before sending it again (it could arrive twice). It is not retried
|
|
@@ -1555,28 +1554,28 @@ Errors specific to WhatsApp:
|
|
|
1555
1554
|
marketing template without an opt-out button only gets a warning.
|
|
1556
1555
|
- Profile photo: `file_required` and `whatsapp_profile_photo_invalid` (HTTP
|
|
1557
1556
|
400), `whatsapp_profile_photo_too_large` (HTTP 413),
|
|
1558
|
-
`whatsapp_profile_update_failed` (HTTP
|
|
1557
|
+
`whatsapp_profile_update_failed` (HTTP 503). Conversational components:
|
|
1559
1558
|
`invalid_request` (HTTP 400) with the reason,
|
|
1560
1559
|
`whatsapp_conversational_components_fetch_failed` and
|
|
1561
|
-
`whatsapp_conversational_components_update_failed` (HTTP
|
|
1560
|
+
`whatsapp_conversational_components_update_failed` (HTTP 503). Calling:
|
|
1562
1561
|
`voice_not_enabled` (HTTP 409), `whatsapp_calling_unavailable` (HTTP 422;
|
|
1563
1562
|
WhatsApp only allows calling once the account may message 2,000 people a
|
|
1564
1563
|
day and the display name is approved), `whatsapp_calling_update_failed`
|
|
1565
|
-
(HTTP
|
|
1564
|
+
(HTTP 503).
|
|
1566
1565
|
- Adding a number by code: `whatsapp_business_account_not_found` (HTTP 404),
|
|
1567
1566
|
`display_name_required` (HTTP 400), `whatsapp_signup_in_progress` and
|
|
1568
1567
|
`whatsapp_already_enabled` (HTTP 409), and
|
|
1569
|
-
`whatsapp_verification_start_failed` (HTTP 422 refused,
|
|
1570
|
-
|
|
1568
|
+
`whatsapp_verification_start_failed` (HTTP 422 refused, 503 unreachable). A
|
|
1569
|
+
Facebook `signup.create()` for a number being
|
|
1571
1570
|
added by code gets `whatsapp_verification_in_progress` (HTTP 409).
|
|
1572
1571
|
`verify()`: `invalid_verification_code` (HTTP 400, not 6 digits),
|
|
1573
1572
|
`whatsapp_verification_code_invalid` (HTTP 422, with
|
|
1574
1573
|
`e.response.attempts_remaining`), `whatsapp_verification_failed` (HTTP 409,
|
|
1575
1574
|
too many wrong codes), `whatsapp_verification_busy` (HTTP 409, retry),
|
|
1576
1575
|
`whatsapp_verification_unavailable` and `whatsapp_activation_pending` (HTTP
|
|
1577
|
-
|
|
1576
|
+
503). `resend()`: `whatsapp_verification_resend_too_soon` (HTTP 429, wait
|
|
1578
1577
|
`e.response.retry_after` seconds) and `whatsapp_verification_resend_failed`
|
|
1579
|
-
(HTTP 422 or
|
|
1578
|
+
(HTTP 422 or 503). Both raise `signup_not_active` (HTTP 409) once the signup
|
|
1580
1579
|
stops waiting for a code, and `signup_not_found` (HTTP 404). The SDK never
|
|
1581
1580
|
retries a `5xx`, a timeout or a network error from `signup.create()` with a
|
|
1582
1581
|
`business_account_id` or from `verify()`; it retries `resend()` like any
|
|
@@ -1584,6 +1583,78 @@ Errors specific to WhatsApp:
|
|
|
1584
1583
|
|
|
1585
1584
|
`message.whatsapp.message_id` is `None` until the first delivery report lands.
|
|
1586
1585
|
|
|
1586
|
+
### SMS fallback
|
|
1587
|
+
|
|
1588
|
+
Pass `fallback_to_sms=True` and a WhatsApp message that can't be delivered
|
|
1589
|
+
goes as an SMS from the same number instead.
|
|
1590
|
+
|
|
1591
|
+
```python
|
|
1592
|
+
from sendly import Sendly, SendlyError, WhatsAppSmsFallback
|
|
1593
|
+
|
|
1594
|
+
client = Sendly('sk_live_v1_YOUR_API_KEY')
|
|
1595
|
+
|
|
1596
|
+
try:
|
|
1597
|
+
message = client.messages.send(
|
|
1598
|
+
channel='whatsapp',
|
|
1599
|
+
to='+15555550100',
|
|
1600
|
+
from_='+15555550123',
|
|
1601
|
+
text='Your table is ready!',
|
|
1602
|
+
fallback_to_sms=True,
|
|
1603
|
+
)
|
|
1604
|
+
print(message.fallback.status) # 'pending', 'sent', 'failed', 'unavailable' or 'skipped'
|
|
1605
|
+
print(message.fallback.reason) # 'window_closed', 'whatsapp_refused' or 'whatsapp_failed'
|
|
1606
|
+
if message.fell_back and message.fallback.message_id:
|
|
1607
|
+
# The SMS went instead (message.status is 'failed')
|
|
1608
|
+
sms = client.messages.get(message.fallback.message_id)
|
|
1609
|
+
print(sms.fallback_of) # the WhatsApp message's id
|
|
1610
|
+
except SendlyError as e:
|
|
1611
|
+
extra = (e.response.model_extra or {}) if e.response else {}
|
|
1612
|
+
if e.code == 'sms_fallback_unavailable':
|
|
1613
|
+
pass # 403: this number can't send SMS to the recipient; nothing was sent
|
|
1614
|
+
elif e.code == 'sms_fallback_not_supported':
|
|
1615
|
+
print(extra.get('reason')) # marketing_template, authentication_template or media_without_caption
|
|
1616
|
+
elif extra.get('fallback'):
|
|
1617
|
+
# 422 whatsapp_window_closed or whatsapp_send_failed: the SMS couldn't go
|
|
1618
|
+
# either. A closed window also carries the WhatsApp message's id.
|
|
1619
|
+
fallback = WhatsAppSmsFallback.model_validate(extra['fallback'])
|
|
1620
|
+
print(e.code, extra.get('id'), fallback.status, fallback.error)
|
|
1621
|
+
else:
|
|
1622
|
+
raise
|
|
1623
|
+
```
|
|
1624
|
+
|
|
1625
|
+
- It falls back when the 24-hour window is closed (the SMS goes at once:
|
|
1626
|
+
free-form text and media go only within 24 hours of the customer's last
|
|
1627
|
+
message, templates any time), when WhatsApp refuses the message, or when
|
|
1628
|
+
WhatsApp reports it undeliverable later. Never after WhatsApp reports it
|
|
1629
|
+
delivered or read, and at most once per message.
|
|
1630
|
+
- The number must already be able to send SMS to the recipient (a verified
|
|
1631
|
+
toll-free number, or a local number on a 10DLC campaign, to a US or Canada
|
|
1632
|
+
number), or the send is refused with HTTP 403 `sms_fallback_unavailable`
|
|
1633
|
+
before anything is sent.
|
|
1634
|
+
- Marketing and authentication templates, and media without a caption, can't
|
|
1635
|
+
fall back: HTTP 422 `sms_fallback_not_supported`. A utility template falls
|
|
1636
|
+
back as its text header, body and footer with the variables filled in, and
|
|
1637
|
+
its URL and phone buttons as `label: target`; quick replies are dropped and
|
|
1638
|
+
listed in `fallback.content_dropped`.
|
|
1639
|
+
- Billing: the WhatsApp charge is refunded (a closed window is never charged)
|
|
1640
|
+
and the SMS is billed as an SMS. A message that fell back at once comes
|
|
1641
|
+
back with `status == 'failed'`, `credits_used == 0` and
|
|
1642
|
+
`fallback.status == 'sent'` (`message.fell_back` is `True`);
|
|
1643
|
+
`fallback.credits_used` is what the SMS cost.
|
|
1644
|
+
- No SMS goes on an HTTP 409 `whatsapp_send_unconfirmed` or an HTTP 503
|
|
1645
|
+
`whatsapp_send_failed`. `fallback.status` is `'unavailable'` when SMS rules
|
|
1646
|
+
stop it (the contact opted out, a promotional-looking text in the
|
|
1647
|
+
recipient's quiet hours, restricted content), with the reason in
|
|
1648
|
+
`fallback.error`, and `'skipped'` for a WhatsApp error an SMS can't fix.
|
|
1649
|
+
- There is no time-based fallback: a message WhatsApp accepted but hasn't
|
|
1650
|
+
delivered, for example to a phone that is off, stays `'pending'`, and no
|
|
1651
|
+
SMS goes unless WhatsApp later reports a failure.
|
|
1652
|
+
- `messages.get()` and `messages.list()` return the same `fallback` on the
|
|
1653
|
+
WhatsApp message, so check a pending one later. The SMS carries
|
|
1654
|
+
`fallback_of`, the WhatsApp message's id.
|
|
1655
|
+
|
|
1656
|
+
Guide: [Send WhatsApp with an SMS Fallback](https://sendly.live/docs/whatsapp/send-with-fallback).
|
|
1657
|
+
|
|
1587
1658
|
## RCS
|
|
1588
1659
|
|
|
1589
1660
|
Send branded, verified-sender messages on Android: rich cards, suggestion
|
|
@@ -1945,8 +2016,8 @@ default, or `CA`) are optional.
|
|
|
1945
2016
|
|
|
1946
2017
|
Refusals: `agent_required` (agent mode with no agent), `agent_not_found`,
|
|
1947
2018
|
`agent_disabled` (turn the agent on first), `agent_limit`, `agent_in_use`,
|
|
1948
|
-
`number_not_found`, `invalid_voice_mode`, `voice_attach_failed` (HTTP
|
|
1949
|
-
retry shortly), `carrier_refused` (HTTP
|
|
2019
|
+
`number_not_found`, `invalid_voice_mode`, `voice_attach_failed` (HTTP 503,
|
|
2020
|
+
retry shortly), `carrier_refused` (HTTP 503, retry shortly unless the message
|
|
1950
2021
|
says the number couldn't be found for emergency registration: contact
|
|
1951
2022
|
support), `voice_unavailable` (HTTP 503),
|
|
1952
2023
|
`e911_not_applicable` (only US and Canadian numbers take an emergency
|
|
@@ -2592,7 +2663,6 @@ client.enterprise.workspaces.revoke_key("ws_xxx", "key_abc")
|
|
|
2592
2663
|
|
|
2593
2664
|
# Pooled credits and auto top-up
|
|
2594
2665
|
client.enterprise.credits.get()
|
|
2595
|
-
client.enterprise.credits.deposit(10000, description="Q2 top-up")
|
|
2596
2666
|
client.enterprise.settings.update_auto_top_up(
|
|
2597
2667
|
enabled=True, threshold=1000, amount=5000, source_workspace_id="ws_source"
|
|
2598
2668
|
)
|
|
@@ -413,7 +413,7 @@ File uploads (`media.upload()`, `business_upgrade.start()` and `resubmit()`,
|
|
|
413
413
|
make is the `too_many_concurrent_verifications` wait. `whatsapp.signup.verify()`
|
|
414
414
|
and `whatsapp.signup.create()` with a `business_account_id` also raise a
|
|
415
415
|
timeout, a network error or a `5xx` at once, because repeating them can use up
|
|
416
|
-
a verification attempt or start a new
|
|
416
|
+
a verification attempt or start a new signup; they still wait out a
|
|
417
417
|
`429` the API never ran.
|
|
418
418
|
|
|
419
419
|
After the last attempt the SDK raises the error it last saw (`TimeoutError`,
|
|
@@ -1037,9 +1037,10 @@ client.messages.send(
|
|
|
1037
1037
|
|
|
1038
1038
|
Uploads accept JPEG, PNG and GIF up to 600 KB. When `content_type` is one of
|
|
1039
1039
|
those types but the content is not, the upload raises `SendlyError` with code
|
|
1040
|
-
`invalid_file`; any other `content_type
|
|
1041
|
-
|
|
1042
|
-
for your account it is
|
|
1040
|
+
`invalid_file`; any other `content_type` raises `unsupported_media_type`
|
|
1041
|
+
(HTTP 415) with the reason in the message, and a file over 600 KB raises
|
|
1042
|
+
`file_too_large` (HTTP 413). Without MMS enabled for your account it is
|
|
1043
|
+
`feature_disabled`.
|
|
1043
1044
|
|
|
1044
1045
|
## Account & Credits
|
|
1045
1046
|
|
|
@@ -1319,7 +1320,7 @@ client.links.update(link.code, True)
|
|
|
1319
1320
|
|
|
1320
1321
|
## WhatsApp
|
|
1321
1322
|
|
|
1322
|
-
Connect a number you own to WhatsApp (
|
|
1323
|
+
Connect a number you own to WhatsApp (free to connect, no monthly fee), create
|
|
1323
1324
|
Meta-reviewed message templates, and send on the `whatsapp` channel. Connecting
|
|
1324
1325
|
the first number always ends with a human step: hand the `connect_url` to your
|
|
1325
1326
|
user - they open it in a browser and log in with Facebook to link their
|
|
@@ -1351,10 +1352,8 @@ print(f'Open {signup.connect_url} and log in with Facebook')
|
|
|
1351
1352
|
# After the Facebook step it stays 'registering' while WhatsApp activates the
|
|
1352
1353
|
# number. Activation usually takes a few minutes but can take hours. If it
|
|
1353
1354
|
# hasn't finished about 6 hours after the session began, the session fails
|
|
1354
|
-
# with registration_timeout
|
|
1355
|
-
#
|
|
1356
|
-
# is refunded automatically; once a number has connected, a later disconnect
|
|
1357
|
-
# gets nothing back.
|
|
1355
|
+
# with registration_timeout ('failed', with signup.failure_reasons explaining
|
|
1356
|
+
# why).
|
|
1358
1357
|
|
|
1359
1358
|
# 2. Create a template (Meta reviews it, usually 24-48h). category is required
|
|
1360
1359
|
# (UTILITY, AUTHENTICATION or MARKETING) with no default, and an update can't
|
|
@@ -1438,7 +1437,7 @@ components = client.whatsapp.senders.get_conversational_components('+15555550123
|
|
|
1438
1437
|
client.whatsapp.senders.set_calling('+15555550123', enabled=True)
|
|
1439
1438
|
|
|
1440
1439
|
# Add another number to an account you already connected: no Facebook step.
|
|
1441
|
-
#
|
|
1440
|
+
# Free, like a Facebook connection. WhatsApp sends the number a 6-digit code
|
|
1442
1441
|
senders = client.whatsapp.senders.list().senders
|
|
1443
1442
|
account_id = next(
|
|
1444
1443
|
s.business_account_id for s in senders if s.status == 'active' and s.business_account_id
|
|
@@ -1462,14 +1461,14 @@ Senders also carry `business_account_id` (None while `pending`),
|
|
|
1462
1461
|
name on file), `calling_enabled`, and `outbound_calling_allowed`, which is
|
|
1463
1462
|
False for +1, +20, +84 and +234 numbers. A signup that is `verifying` carries
|
|
1464
1463
|
`verification_method` and `verification_attempts_remaining`; after 5 wrong
|
|
1465
|
-
codes it fails with `verification_failed
|
|
1464
|
+
codes it fails with `verification_failed`. Calls carry
|
|
1466
1465
|
`channel` (`phone`, `whatsapp` or `browser`; see `CallChannel`).
|
|
1467
1466
|
|
|
1468
1467
|
`verification_code` comes only from `signup.get()`. Until a code has been
|
|
1469
1468
|
submitted, it is the newest code that has arrived since the signup started, so
|
|
1470
1469
|
after a resend it still shows the earlier code until the new one arrives. Once
|
|
1471
1470
|
WhatsApp has checked a code, only a code that arrived after the last
|
|
1472
|
-
submission or resend is returned. A submission answered with
|
|
1471
|
+
submission or resend is returned. A submission answered with 503
|
|
1473
1472
|
`whatsapp_verification_unavailable` is not counted, so the same unchecked code
|
|
1474
1473
|
can come back, and submitting it again is safe.
|
|
1475
1474
|
|
|
@@ -1495,14 +1494,14 @@ Errors specific to WhatsApp:
|
|
|
1495
1494
|
body carries `retryAfter: 3600` and the response a `Retry-After: 3600`
|
|
1496
1495
|
header. The SDK retries it like any `5xx` before raising;
|
|
1497
1496
|
`e.response.retry_after` says how many seconds to wait before trying again.
|
|
1498
|
-
- `whatsapp_signup_limit_reached` (HTTP 429) after 5 failed
|
|
1497
|
+
- `whatsapp_signup_limit_reached` (HTTP 429) after 5 failed signups in
|
|
1499
1498
|
24 hours. It is final and not retried; try again the next day.
|
|
1500
1499
|
- `whatsapp_send_failed` on a send: HTTP 422 when WhatsApp refused the message,
|
|
1501
1500
|
which is final (cached under the idempotency key and replayed for 24 hours),
|
|
1502
|
-
or HTTP
|
|
1503
|
-
|
|
1504
|
-
retries it
|
|
1505
|
-
charged.
|
|
1501
|
+
or HTTP 503 when the message provably never reached WhatsApp, so it was not
|
|
1502
|
+
sent and is safe to resend under the same idempotency key. A 503 is never
|
|
1503
|
+
cached, and the SDK retries it automatically, up to 3 times under the same
|
|
1504
|
+
key, before raising. Either way the message was not charged.
|
|
1506
1505
|
- `whatsapp_send_unconfirmed` (HTTP 409) on a send: the outcome is unknown.
|
|
1507
1506
|
The message was marked failed and refunded but may still be delivered, so
|
|
1508
1507
|
check before sending it again (it could arrive twice). It is not retried
|
|
@@ -1520,28 +1519,28 @@ Errors specific to WhatsApp:
|
|
|
1520
1519
|
marketing template without an opt-out button only gets a warning.
|
|
1521
1520
|
- Profile photo: `file_required` and `whatsapp_profile_photo_invalid` (HTTP
|
|
1522
1521
|
400), `whatsapp_profile_photo_too_large` (HTTP 413),
|
|
1523
|
-
`whatsapp_profile_update_failed` (HTTP
|
|
1522
|
+
`whatsapp_profile_update_failed` (HTTP 503). Conversational components:
|
|
1524
1523
|
`invalid_request` (HTTP 400) with the reason,
|
|
1525
1524
|
`whatsapp_conversational_components_fetch_failed` and
|
|
1526
|
-
`whatsapp_conversational_components_update_failed` (HTTP
|
|
1525
|
+
`whatsapp_conversational_components_update_failed` (HTTP 503). Calling:
|
|
1527
1526
|
`voice_not_enabled` (HTTP 409), `whatsapp_calling_unavailable` (HTTP 422;
|
|
1528
1527
|
WhatsApp only allows calling once the account may message 2,000 people a
|
|
1529
1528
|
day and the display name is approved), `whatsapp_calling_update_failed`
|
|
1530
|
-
(HTTP
|
|
1529
|
+
(HTTP 503).
|
|
1531
1530
|
- Adding a number by code: `whatsapp_business_account_not_found` (HTTP 404),
|
|
1532
1531
|
`display_name_required` (HTTP 400), `whatsapp_signup_in_progress` and
|
|
1533
1532
|
`whatsapp_already_enabled` (HTTP 409), and
|
|
1534
|
-
`whatsapp_verification_start_failed` (HTTP 422 refused,
|
|
1535
|
-
|
|
1533
|
+
`whatsapp_verification_start_failed` (HTTP 422 refused, 503 unreachable). A
|
|
1534
|
+
Facebook `signup.create()` for a number being
|
|
1536
1535
|
added by code gets `whatsapp_verification_in_progress` (HTTP 409).
|
|
1537
1536
|
`verify()`: `invalid_verification_code` (HTTP 400, not 6 digits),
|
|
1538
1537
|
`whatsapp_verification_code_invalid` (HTTP 422, with
|
|
1539
1538
|
`e.response.attempts_remaining`), `whatsapp_verification_failed` (HTTP 409,
|
|
1540
1539
|
too many wrong codes), `whatsapp_verification_busy` (HTTP 409, retry),
|
|
1541
1540
|
`whatsapp_verification_unavailable` and `whatsapp_activation_pending` (HTTP
|
|
1542
|
-
|
|
1541
|
+
503). `resend()`: `whatsapp_verification_resend_too_soon` (HTTP 429, wait
|
|
1543
1542
|
`e.response.retry_after` seconds) and `whatsapp_verification_resend_failed`
|
|
1544
|
-
(HTTP 422 or
|
|
1543
|
+
(HTTP 422 or 503). Both raise `signup_not_active` (HTTP 409) once the signup
|
|
1545
1544
|
stops waiting for a code, and `signup_not_found` (HTTP 404). The SDK never
|
|
1546
1545
|
retries a `5xx`, a timeout or a network error from `signup.create()` with a
|
|
1547
1546
|
`business_account_id` or from `verify()`; it retries `resend()` like any
|
|
@@ -1549,6 +1548,78 @@ Errors specific to WhatsApp:
|
|
|
1549
1548
|
|
|
1550
1549
|
`message.whatsapp.message_id` is `None` until the first delivery report lands.
|
|
1551
1550
|
|
|
1551
|
+
### SMS fallback
|
|
1552
|
+
|
|
1553
|
+
Pass `fallback_to_sms=True` and a WhatsApp message that can't be delivered
|
|
1554
|
+
goes as an SMS from the same number instead.
|
|
1555
|
+
|
|
1556
|
+
```python
|
|
1557
|
+
from sendly import Sendly, SendlyError, WhatsAppSmsFallback
|
|
1558
|
+
|
|
1559
|
+
client = Sendly('sk_live_v1_YOUR_API_KEY')
|
|
1560
|
+
|
|
1561
|
+
try:
|
|
1562
|
+
message = client.messages.send(
|
|
1563
|
+
channel='whatsapp',
|
|
1564
|
+
to='+15555550100',
|
|
1565
|
+
from_='+15555550123',
|
|
1566
|
+
text='Your table is ready!',
|
|
1567
|
+
fallback_to_sms=True,
|
|
1568
|
+
)
|
|
1569
|
+
print(message.fallback.status) # 'pending', 'sent', 'failed', 'unavailable' or 'skipped'
|
|
1570
|
+
print(message.fallback.reason) # 'window_closed', 'whatsapp_refused' or 'whatsapp_failed'
|
|
1571
|
+
if message.fell_back and message.fallback.message_id:
|
|
1572
|
+
# The SMS went instead (message.status is 'failed')
|
|
1573
|
+
sms = client.messages.get(message.fallback.message_id)
|
|
1574
|
+
print(sms.fallback_of) # the WhatsApp message's id
|
|
1575
|
+
except SendlyError as e:
|
|
1576
|
+
extra = (e.response.model_extra or {}) if e.response else {}
|
|
1577
|
+
if e.code == 'sms_fallback_unavailable':
|
|
1578
|
+
pass # 403: this number can't send SMS to the recipient; nothing was sent
|
|
1579
|
+
elif e.code == 'sms_fallback_not_supported':
|
|
1580
|
+
print(extra.get('reason')) # marketing_template, authentication_template or media_without_caption
|
|
1581
|
+
elif extra.get('fallback'):
|
|
1582
|
+
# 422 whatsapp_window_closed or whatsapp_send_failed: the SMS couldn't go
|
|
1583
|
+
# either. A closed window also carries the WhatsApp message's id.
|
|
1584
|
+
fallback = WhatsAppSmsFallback.model_validate(extra['fallback'])
|
|
1585
|
+
print(e.code, extra.get('id'), fallback.status, fallback.error)
|
|
1586
|
+
else:
|
|
1587
|
+
raise
|
|
1588
|
+
```
|
|
1589
|
+
|
|
1590
|
+
- It falls back when the 24-hour window is closed (the SMS goes at once:
|
|
1591
|
+
free-form text and media go only within 24 hours of the customer's last
|
|
1592
|
+
message, templates any time), when WhatsApp refuses the message, or when
|
|
1593
|
+
WhatsApp reports it undeliverable later. Never after WhatsApp reports it
|
|
1594
|
+
delivered or read, and at most once per message.
|
|
1595
|
+
- The number must already be able to send SMS to the recipient (a verified
|
|
1596
|
+
toll-free number, or a local number on a 10DLC campaign, to a US or Canada
|
|
1597
|
+
number), or the send is refused with HTTP 403 `sms_fallback_unavailable`
|
|
1598
|
+
before anything is sent.
|
|
1599
|
+
- Marketing and authentication templates, and media without a caption, can't
|
|
1600
|
+
fall back: HTTP 422 `sms_fallback_not_supported`. A utility template falls
|
|
1601
|
+
back as its text header, body and footer with the variables filled in, and
|
|
1602
|
+
its URL and phone buttons as `label: target`; quick replies are dropped and
|
|
1603
|
+
listed in `fallback.content_dropped`.
|
|
1604
|
+
- Billing: the WhatsApp charge is refunded (a closed window is never charged)
|
|
1605
|
+
and the SMS is billed as an SMS. A message that fell back at once comes
|
|
1606
|
+
back with `status == 'failed'`, `credits_used == 0` and
|
|
1607
|
+
`fallback.status == 'sent'` (`message.fell_back` is `True`);
|
|
1608
|
+
`fallback.credits_used` is what the SMS cost.
|
|
1609
|
+
- No SMS goes on an HTTP 409 `whatsapp_send_unconfirmed` or an HTTP 503
|
|
1610
|
+
`whatsapp_send_failed`. `fallback.status` is `'unavailable'` when SMS rules
|
|
1611
|
+
stop it (the contact opted out, a promotional-looking text in the
|
|
1612
|
+
recipient's quiet hours, restricted content), with the reason in
|
|
1613
|
+
`fallback.error`, and `'skipped'` for a WhatsApp error an SMS can't fix.
|
|
1614
|
+
- There is no time-based fallback: a message WhatsApp accepted but hasn't
|
|
1615
|
+
delivered, for example to a phone that is off, stays `'pending'`, and no
|
|
1616
|
+
SMS goes unless WhatsApp later reports a failure.
|
|
1617
|
+
- `messages.get()` and `messages.list()` return the same `fallback` on the
|
|
1618
|
+
WhatsApp message, so check a pending one later. The SMS carries
|
|
1619
|
+
`fallback_of`, the WhatsApp message's id.
|
|
1620
|
+
|
|
1621
|
+
Guide: [Send WhatsApp with an SMS Fallback](https://sendly.live/docs/whatsapp/send-with-fallback).
|
|
1622
|
+
|
|
1552
1623
|
## RCS
|
|
1553
1624
|
|
|
1554
1625
|
Send branded, verified-sender messages on Android: rich cards, suggestion
|
|
@@ -1910,8 +1981,8 @@ default, or `CA`) are optional.
|
|
|
1910
1981
|
|
|
1911
1982
|
Refusals: `agent_required` (agent mode with no agent), `agent_not_found`,
|
|
1912
1983
|
`agent_disabled` (turn the agent on first), `agent_limit`, `agent_in_use`,
|
|
1913
|
-
`number_not_found`, `invalid_voice_mode`, `voice_attach_failed` (HTTP
|
|
1914
|
-
retry shortly), `carrier_refused` (HTTP
|
|
1984
|
+
`number_not_found`, `invalid_voice_mode`, `voice_attach_failed` (HTTP 503,
|
|
1985
|
+
retry shortly), `carrier_refused` (HTTP 503, retry shortly unless the message
|
|
1915
1986
|
says the number couldn't be found for emergency registration: contact
|
|
1916
1987
|
support), `voice_unavailable` (HTTP 503),
|
|
1917
1988
|
`e911_not_applicable` (only US and Canadian numbers take an emergency
|
|
@@ -2557,7 +2628,6 @@ client.enterprise.workspaces.revoke_key("ws_xxx", "key_abc")
|
|
|
2557
2628
|
|
|
2558
2629
|
# Pooled credits and auto top-up
|
|
2559
2630
|
client.enterprise.credits.get()
|
|
2560
|
-
client.enterprise.credits.deposit(10000, description="Q2 top-up")
|
|
2561
2631
|
client.enterprise.settings.update_auto_top_up(
|
|
2562
2632
|
enabled=True, threshold=1000, amount=5000, source_workspace_id="ws_source"
|
|
2563
2633
|
)
|
|
@@ -29,7 +29,7 @@ Async Example:
|
|
|
29
29
|
>>> asyncio.run(main())
|
|
30
30
|
"""
|
|
31
31
|
|
|
32
|
-
__version__ = "4.
|
|
32
|
+
__version__ = "4.5.0"
|
|
33
33
|
|
|
34
34
|
# Main clients
|
|
35
35
|
from .client import AsyncSendly, Sendly
|
|
@@ -160,6 +160,7 @@ from .types import (
|
|
|
160
160
|
TenDlcThroughput,
|
|
161
161
|
# WhatsApp types
|
|
162
162
|
WhatsAppMessage,
|
|
163
|
+
WhatsAppSmsFallback,
|
|
163
164
|
WhatsAppMessageDetails,
|
|
164
165
|
WhatsAppMessageTemplate,
|
|
165
166
|
WhatsAppSender,
|
|
@@ -409,6 +410,7 @@ __all__ = [
|
|
|
409
410
|
"TenDlcThroughput",
|
|
410
411
|
# WhatsApp types
|
|
411
412
|
"WhatsAppMessage",
|
|
413
|
+
"WhatsAppSmsFallback",
|
|
412
414
|
"WhatsAppMessageDetails",
|
|
413
415
|
"WhatsAppMessageTemplate",
|
|
414
416
|
"WhatsAppSender",
|
|
@@ -49,10 +49,11 @@ class MediaResource:
|
|
|
49
49
|
ValidationError: If no file reached the API (code ``invalid_request``)
|
|
50
50
|
SendlyError: With code ``invalid_file`` when content_type is
|
|
51
51
|
image/jpeg, image/png or image/gif but the content is not a
|
|
52
|
-
JPEG, PNG or GIF; ``
|
|
53
|
-
content_type is any other type
|
|
54
|
-
|
|
55
|
-
MMS is not enabled for
|
|
52
|
+
JPEG, PNG or GIF; ``unsupported_media_type`` (HTTP 415) when
|
|
53
|
+
content_type is any other type, with the reason in the
|
|
54
|
+
message; ``file_too_large`` (HTTP 413) when the file is over
|
|
55
|
+
600 KB; or ``feature_disabled`` when MMS is not enabled for
|
|
56
|
+
your account
|
|
56
57
|
AuthenticationError: If the API key is invalid
|
|
57
58
|
RateLimitError: If rate limit is exceeded
|
|
58
59
|
|
|
@@ -126,9 +126,13 @@ class MessagesResource:
|
|
|
126
126
|
'url': 'https://example.com/track'}}]. Card buttons go in
|
|
127
127
|
card['suggestions'] instead. Dropped (and disclosed via
|
|
128
128
|
message.rcs.suggestions_dropped) when the send falls back
|
|
129
|
-
fallback_to_sms: RCS
|
|
129
|
+
fallback_to_sms: RCS - deliver as SMS when the recipient
|
|
130
130
|
doesn't support RCS (default True). Pass False to get a 422
|
|
131
|
-
rcs_not_supported_for_recipient instead
|
|
131
|
+
rcs_not_supported_for_recipient instead. WhatsApp - default
|
|
132
|
+
False; True sends an SMS from the same number when WhatsApp
|
|
133
|
+
can't deliver the message (see message.fallback). The number
|
|
134
|
+
must be able to send SMS to the recipient, or the API answers
|
|
135
|
+
403 sms_fallback_unavailable
|
|
132
136
|
idempotency_key: Optional key (1-255 printable ASCII characters)
|
|
133
137
|
sent as the Idempotency-Key header - retrying with the same
|
|
134
138
|
key returns the original result instead of sending again.
|
|
@@ -148,8 +152,8 @@ class MessagesResource:
|
|
|
148
152
|
SendlyError: ``whatsapp_send_failed`` on WhatsApp: 422 when
|
|
149
153
|
WhatsApp refused the message, which is final and not retried
|
|
150
154
|
(cached under the idempotency key and replayed for 24 hours);
|
|
151
|
-
|
|
152
|
-
was not sent and is safe to send again. A
|
|
155
|
+
503 when the message provably never reached the carrier, so it
|
|
156
|
+
was not sent and is safe to send again. A 503 is never cached,
|
|
153
157
|
so the SDK retries it like any 5xx under the same idempotency
|
|
154
158
|
key. Either way the message wasn't charged. No send returns
|
|
155
159
|
503 ``whatsapp_unavailable``.
|
|
@@ -242,6 +246,8 @@ class MessagesResource:
|
|
|
242
246
|
body["template"] = template
|
|
243
247
|
if metadata:
|
|
244
248
|
body["metadata"] = metadata
|
|
249
|
+
if fallback_to_sms is not None:
|
|
250
|
+
body["fallbackToSms"] = fallback_to_sms
|
|
245
251
|
|
|
246
252
|
data = self._http.request(
|
|
247
253
|
method="POST",
|
|
@@ -1054,8 +1060,13 @@ class AsyncMessagesResource:
|
|
|
1054
1060
|
agent_id: RCS only - the agent to send as
|
|
1055
1061
|
card: RCS only - a rich card (title and description required)
|
|
1056
1062
|
suggestions: RCS only - suggestion chips for a text message
|
|
1057
|
-
fallback_to_sms: RCS
|
|
1058
|
-
doesn't support RCS (default True)
|
|
1063
|
+
fallback_to_sms: RCS - deliver as SMS when the recipient
|
|
1064
|
+
doesn't support RCS (default True). Pass False to get a 422
|
|
1065
|
+
rcs_not_supported_for_recipient instead. WhatsApp - default
|
|
1066
|
+
False; True sends an SMS from the same number when WhatsApp
|
|
1067
|
+
can't deliver the message (see message.fallback). The number
|
|
1068
|
+
must be able to send SMS to the recipient, or the API answers
|
|
1069
|
+
403 sms_fallback_unavailable
|
|
1059
1070
|
idempotency_key: Optional key (1-255 printable ASCII characters)
|
|
1060
1071
|
sent as the Idempotency-Key header - retrying with the same
|
|
1061
1072
|
key returns the original result instead of sending again.
|
|
@@ -1116,6 +1127,8 @@ class AsyncMessagesResource:
|
|
|
1116
1127
|
body["template"] = template
|
|
1117
1128
|
if metadata:
|
|
1118
1129
|
body["metadata"] = metadata
|
|
1130
|
+
if fallback_to_sms is not None:
|
|
1131
|
+
body["fallbackToSms"] = fallback_to_sms
|
|
1119
1132
|
|
|
1120
1133
|
data = await self._http.request(
|
|
1121
1134
|
method="POST",
|