sendly 4.4.0 → 4.5.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/Gemfile.lock +1 -1
- data/README.md +94 -44
- data/lib/sendly/errors.rb +3 -3
- data/lib/sendly/messages.rb +1 -1
- data/lib/sendly/version.rb +1 -1
- data/lib/sendly/voice_resource.rb +3 -3
- data/lib/sendly/whatsapp_resource.rb +35 -38
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 511e36e32d4475552b4000a026856bde62c06bd7f43a52b59646c347d7ff4b0c
|
|
4
|
+
data.tar.gz: 92531f77aa366320d2887a3b9a0ac9793c6562d507f3863577352830097f4d32
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 000020f0a21c87fb3f4b33ca84fad93e36a73abaa936ca602b22c3065e7fbc3d09b2c0fe62b02f2080dbab1f2b4d37d8ad60648d08e0a0d022a71bbcd64b1ee6
|
|
7
|
+
data.tar.gz: fbc1718f201afe7895d433d989bf41e7c0938fac6c029dca8380b2d88620ef56d65851a258b9d3e39ad683652023e8468ef534c8a459ce930fcf35cc6d07ddba
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# sendly (Ruby)
|
|
2
2
|
|
|
3
|
+
## 4.5.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- **`Sendly::ValidationError#status_code` is the response's real status.** It was always 400, so a 422 (such as `whatsapp_window_closed` or `sms_fallback_not_supported`) reported 400. Code that compared `status_code` with 400 to spot a validation error should check the class (or `code`) instead. `ValidationError.new` takes an optional `status_code:`, 400 by default.
|
|
8
|
+
- 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.
|
|
9
|
+
|
|
3
10
|
## 4.4.0
|
|
4
11
|
|
|
5
12
|
### Minor Changes
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -1153,11 +1153,11 @@ puts status.disabled?
|
|
|
1153
1153
|
|
|
1154
1154
|
Connect a number you own to WhatsApp, create Meta-reviewed message
|
|
1155
1155
|
templates, and send via `client.messages.send(channel: "whatsapp", ...)`.
|
|
1156
|
-
Connecting is
|
|
1156
|
+
Connecting is free (no monthly fee either). The first number always
|
|
1157
1157
|
ends with a human step: the signup returns a connect URL a person must open in
|
|
1158
1158
|
a browser and log in with Facebook to link their WhatsApp Business Account.
|
|
1159
1159
|
More numbers can then be added to that account with a 6-digit code Meta sends
|
|
1160
|
-
to the number, with no Facebook step (
|
|
1160
|
+
to the number, with no Facebook step (also free).
|
|
1161
1161
|
|
|
1162
1162
|
Sends go through `messages.send(channel: "whatsapp")` and need `sms:send`,
|
|
1163
1163
|
not `whatsapp:write`. Reads (`signup.get`, templates, the window, senders and
|
|
@@ -1191,16 +1191,14 @@ gives its slot back. Note: Meta
|
|
|
1191
1191
|
has paused marketing template delivery to US (+1) numbers.
|
|
1192
1192
|
|
|
1193
1193
|
```ruby
|
|
1194
|
-
# 1. Connect a number (
|
|
1194
|
+
# 1. Connect a number (free; a human must open the connect URL)
|
|
1195
1195
|
signup = client.whatsapp.signup.create(phone_number: "+15125550123")
|
|
1196
1196
|
puts "Have your user open: #{signup.connect_url}"
|
|
1197
1197
|
|
|
1198
1198
|
# 2. Poll until active. After the Facebook step the signup is "registering"
|
|
1199
1199
|
# while WhatsApp activates the number. Activation usually takes a few minutes
|
|
1200
1200
|
# but can take hours. If it hasn't finished about 6 hours after the session
|
|
1201
|
-
# began, the session fails with registration_timeout
|
|
1202
|
-
# If the connection fails, the $19 fee is refunded automatically; once a
|
|
1203
|
-
# number has connected, a later disconnect gets nothing back.
|
|
1201
|
+
# began, the session fails with registration_timeout.
|
|
1204
1202
|
status = client.whatsapp.signup.get(signup.id)
|
|
1205
1203
|
puts status.failure_reasons if status.failed? # e.g. ["waba_mismatch"]
|
|
1206
1204
|
|
|
@@ -1257,7 +1255,7 @@ puts added.status # "verifying"
|
|
|
1257
1255
|
# code has been submitted it is the newest code since the signup started, so
|
|
1258
1256
|
# after a resend it still shows the earlier code until the new one arrives.
|
|
1259
1257
|
# Once WhatsApp has checked a code, only a code that arrived after the last
|
|
1260
|
-
# submission or resend is returned. A
|
|
1258
|
+
# submission or resend is returned. A 503 whatsapp_verification_unavailable
|
|
1261
1259
|
# isn't counted, so the same unchecked code can come back, and submitting it
|
|
1262
1260
|
# again is safe.
|
|
1263
1261
|
code = client.whatsapp.signup.get(added.id).verification_code
|
|
@@ -1316,25 +1314,6 @@ client.messages.send(
|
|
|
1316
1314
|
text: "Here is your receipt",
|
|
1317
1315
|
media_urls: ["https://acme.example/receipt.pdf"]
|
|
1318
1316
|
)
|
|
1319
|
-
|
|
1320
|
-
# Fall back to SMS from the same number when WhatsApp can't deliver: the
|
|
1321
|
-
# window is closed, WhatsApp refuses it, or reports it undeliverable later.
|
|
1322
|
-
# The SMS is billed as an SMS. The number must already be able to send SMS to
|
|
1323
|
-
# the recipient (US and Canada), or this raises 403 sms_fallback_unavailable;
|
|
1324
|
-
# marketing and authentication templates, and media without a caption, raise
|
|
1325
|
-
# 422 sms_fallback_not_supported.
|
|
1326
|
-
message = client.messages.send(
|
|
1327
|
-
channel: "whatsapp",
|
|
1328
|
-
to: "+15555550100",
|
|
1329
|
-
from: "+15125550123",
|
|
1330
|
-
text: "Your table is ready!",
|
|
1331
|
-
fallback_to_sms: true
|
|
1332
|
-
)
|
|
1333
|
-
message.fallback.status # "pending", "sent", "failed", "unavailable" or "skipped"
|
|
1334
|
-
message.fallback.reason # "window_closed", "whatsapp_refused" or "whatsapp_failed"
|
|
1335
|
-
message.fell_back? # true when the SMS went (message.status is then "failed")
|
|
1336
|
-
sms = client.messages.get(message.fallback.message_id) if message.fallback.message_id
|
|
1337
|
-
sms&.fallback_of # the WhatsApp message's id
|
|
1338
1317
|
```
|
|
1339
1318
|
|
|
1340
1319
|
WhatsApp errors map onto the usual classes:
|
|
@@ -1343,7 +1322,7 @@ WhatsApp errors map onto the usual classes:
|
|
|
1343
1322
|
while WhatsApp connections are unavailable. Only signup returns it; no send
|
|
1344
1323
|
does. Nothing is charged, the response carries a `Retry-After: 3600` header,
|
|
1345
1324
|
and `e.response_body["retryAfter"]` (3600) says how many seconds to wait; the client
|
|
1346
|
-
retries it like any 5xx before raising. After 5 failed
|
|
1325
|
+
retries it like any 5xx before raising. After 5 failed signups in
|
|
1347
1326
|
24 hours it raises `Sendly::RateLimitError` for 429
|
|
1348
1327
|
`whatsapp_signup_limit_reached`, which is not retried: try again the next
|
|
1349
1328
|
day.
|
|
@@ -1351,10 +1330,13 @@ WhatsApp errors map onto the usual classes:
|
|
|
1351
1330
|
`whatsapp_window_closed`); send a template instead.
|
|
1352
1331
|
- `whatsapp_send_failed` is a `Sendly::ValidationError` (422) when WhatsApp
|
|
1353
1332
|
refused the message, which is final (cached under the idempotency key and
|
|
1354
|
-
replayed for 24 hours), and a `Sendly::ServerError` (
|
|
1355
|
-
provably never reached
|
|
1356
|
-
|
|
1357
|
-
same
|
|
1333
|
+
replayed for 24 hours), and a `Sendly::ServerError` (503) when the message
|
|
1334
|
+
provably never reached WhatsApp, so it was not sent and is safe to resend
|
|
1335
|
+
under the same idempotency key; the 503 is never cached and the client
|
|
1336
|
+
retries it automatically, up to 3 times under the same key, before raising.
|
|
1337
|
+
Neither is charged. With `fallback_to_sms: true`, a 422 means the SMS
|
|
1338
|
+
couldn't go either, and `e.response_body["fallback"]` says why; see
|
|
1339
|
+
[SMS fallback](#sms-fallback).
|
|
1358
1340
|
- `whatsapp_send_unconfirmed` is a `Sendly::APIError` with `status_code` 409:
|
|
1359
1341
|
the outcome is unknown. The message was marked failed and refunded but may
|
|
1360
1342
|
still be delivered, so check before sending it again (it could arrive
|
|
@@ -1382,11 +1364,11 @@ WhatsApp errors map onto the usual classes:
|
|
|
1382
1364
|
400 `display_name_required` and for 422 `whatsapp_verification_start_failed`
|
|
1383
1365
|
(WhatsApp refused to verify the number; final), `Sendly::APIError` (409) for
|
|
1384
1366
|
`whatsapp_signup_in_progress` and `whatsapp_already_enabled`, and
|
|
1385
|
-
`Sendly::ServerError` for
|
|
1367
|
+
`Sendly::ServerError` for 503 `whatsapp_verification_start_failed` (WhatsApp
|
|
1386
1368
|
couldn't start verifying the number; start again). Either failure fails the
|
|
1387
|
-
session
|
|
1388
|
-
|
|
1389
|
-
|
|
1369
|
+
session. With `business_account_id:` the client never retries a 5xx, a
|
|
1370
|
+
timeout or a dropped connection, because each retry would start a new
|
|
1371
|
+
session. A
|
|
1390
1372
|
Facebook signup for a number
|
|
1391
1373
|
that is being added by code gets 409 `whatsapp_verification_in_progress`,
|
|
1392
1374
|
with that session's id in `e.response_body["id"]`.
|
|
@@ -1394,9 +1376,9 @@ WhatsApp errors map onto the usual classes:
|
|
|
1394
1376
|
`invalid_verification_code` (not 6 digits) and 422
|
|
1395
1377
|
`whatsapp_verification_code_invalid` (a wrong code, with
|
|
1396
1378
|
`e.response_body["attemptsRemaining"]`); `Sendly::APIError` (409) for
|
|
1397
|
-
`whatsapp_verification_failed` (after 5 wrong codes the session fails
|
|
1398
|
-
|
|
1399
|
-
`signup_not_active`; and `Sendly::ServerError` for
|
|
1379
|
+
`whatsapp_verification_failed` (after 5 wrong codes the session fails),
|
|
1380
|
+
`whatsapp_verification_busy` (try again) and
|
|
1381
|
+
`signup_not_active`; and `Sendly::ServerError` for 503
|
|
1400
1382
|
`whatsapp_verification_unavailable` (the attempt isn't counted) and
|
|
1401
1383
|
`whatsapp_activation_pending` (the code was accepted but connecting didn't
|
|
1402
1384
|
finish; check back shortly with `signup.get`). The client never retries a
|
|
@@ -1406,13 +1388,13 @@ WhatsApp errors map onto the usual classes:
|
|
|
1406
1388
|
- `signup.resend` raises `Sendly::RateLimitError` for 429
|
|
1407
1389
|
`whatsapp_verification_resend_too_soon` at once, with the seconds to wait in
|
|
1408
1390
|
`e.retry_after`, and `whatsapp_verification_resend_failed` as a
|
|
1409
|
-
`Sendly::ValidationError` (422) or a `Sendly::ServerError` (
|
|
1391
|
+
`Sendly::ValidationError` (422) or a `Sendly::ServerError` (503).
|
|
1410
1392
|
- `senders.upload_profile_photo` raises `Sendly::ValidationError` for 400
|
|
1411
1393
|
`whatsapp_profile_photo_invalid` (not a JPEG or PNG) and `file_required`,
|
|
1412
1394
|
and `Sendly::APIError` with `status_code` 413 for
|
|
1413
1395
|
`whatsapp_profile_photo_too_large`. The photo methods raise
|
|
1414
|
-
`Sendly::ServerError` for
|
|
1415
|
-
conversational component methods for
|
|
1396
|
+
`Sendly::ServerError` for 503 `whatsapp_profile_update_failed`, and the
|
|
1397
|
+
conversational component methods for 503
|
|
1416
1398
|
`whatsapp_conversational_components_fetch_failed` and
|
|
1417
1399
|
`whatsapp_conversational_components_update_failed`; an invalid list is a
|
|
1418
1400
|
`Sendly::ValidationError` (400 `invalid_request`) with a message naming the
|
|
@@ -1422,10 +1404,78 @@ WhatsApp errors map onto the usual classes:
|
|
|
1422
1404
|
(switch voice on for the number first), `Sendly::ValidationError` for 422
|
|
1423
1405
|
`whatsapp_calling_unavailable` (Meta only allows calling once the account may
|
|
1424
1406
|
message at least 2,000 people a day and the display name is approved), and
|
|
1425
|
-
`Sendly::ServerError` for
|
|
1407
|
+
`Sendly::ServerError` for 503 `whatsapp_calling_update_failed`.
|
|
1426
1408
|
- A sender method for a number that isn't connected raises
|
|
1427
1409
|
`Sendly::NotFoundError` (404 `whatsapp_sender_not_connected`).
|
|
1428
1410
|
|
|
1411
|
+
### SMS fallback
|
|
1412
|
+
|
|
1413
|
+
Pass `fallback_to_sms: true` and a WhatsApp message that can't be delivered
|
|
1414
|
+
goes as an SMS from the same number instead.
|
|
1415
|
+
|
|
1416
|
+
```ruby
|
|
1417
|
+
begin
|
|
1418
|
+
message = client.messages.send(
|
|
1419
|
+
channel: "whatsapp",
|
|
1420
|
+
to: "+15555550100",
|
|
1421
|
+
from: "+15125550123",
|
|
1422
|
+
text: "Your table is ready!",
|
|
1423
|
+
fallback_to_sms: true
|
|
1424
|
+
)
|
|
1425
|
+
puts message.fallback.status # "pending", "sent", "failed", "unavailable" or "skipped"
|
|
1426
|
+
puts message.fallback.reason # "window_closed", "whatsapp_refused" or "whatsapp_failed"
|
|
1427
|
+
if message.fell_back? # the SMS went instead (message.status is "failed")
|
|
1428
|
+
sms = client.messages.get(message.fallback.message_id)
|
|
1429
|
+
puts sms.fallback_of # the WhatsApp message's id
|
|
1430
|
+
end
|
|
1431
|
+
rescue Sendly::Error => e
|
|
1432
|
+
body = e.response_body || {}
|
|
1433
|
+
case e.code
|
|
1434
|
+
when "sms_fallback_unavailable"
|
|
1435
|
+
# 403: this number can't send SMS to the recipient; nothing was sent
|
|
1436
|
+
when "sms_fallback_not_supported"
|
|
1437
|
+
puts body["reason"] # marketing_template, authentication_template or media_without_caption
|
|
1438
|
+
else
|
|
1439
|
+
raise unless body["fallback"]
|
|
1440
|
+
# 422 whatsapp_window_closed or whatsapp_send_failed: the SMS couldn't go
|
|
1441
|
+
# either. A closed window also carries the WhatsApp message's id.
|
|
1442
|
+
fallback = Sendly::WhatsAppSmsFallback.new(body["fallback"])
|
|
1443
|
+
puts "#{e.code} #{body["id"]} #{fallback.status}: #{fallback.error}"
|
|
1444
|
+
end
|
|
1445
|
+
end
|
|
1446
|
+
```
|
|
1447
|
+
|
|
1448
|
+
- It falls back when the 24-hour window is closed (the SMS goes at once:
|
|
1449
|
+
free-form text and media go only within 24 hours of the customer's last
|
|
1450
|
+
message, templates any time), when WhatsApp refuses the message, or when
|
|
1451
|
+
WhatsApp reports it undeliverable later. Never after WhatsApp reports it
|
|
1452
|
+
delivered or read, and at most once per message.
|
|
1453
|
+
- The number must already be able to send SMS to the recipient (a verified
|
|
1454
|
+
toll-free number, or a local number on a 10DLC campaign, to a US or Canada
|
|
1455
|
+
number), or the send is refused with 403 `sms_fallback_unavailable`
|
|
1456
|
+
before anything is sent.
|
|
1457
|
+
- Marketing and authentication templates, and media without a caption, can't
|
|
1458
|
+
fall back: 422 `sms_fallback_not_supported`. A utility template falls
|
|
1459
|
+
back as its text header, body and footer with the variables filled in, and
|
|
1460
|
+
its URL and phone buttons as `label: target`; quick replies are dropped and
|
|
1461
|
+
listed in `fallback.content_dropped`.
|
|
1462
|
+
- Billing: the WhatsApp charge is refunded (a closed window is never charged)
|
|
1463
|
+
and the SMS is billed as an SMS. A message that fell back at once comes
|
|
1464
|
+
back with `status` `"failed"`, `credits_used` 0 and `fallback.status` `"sent"`; `fallback.credits_used` is
|
|
1465
|
+
what the SMS cost.
|
|
1466
|
+
- No SMS goes on a 409 `whatsapp_send_unconfirmed` or a 503
|
|
1467
|
+
`whatsapp_send_failed`. The status is `"unavailable"` when SMS rules stop it
|
|
1468
|
+
(the contact opted out, a promotional-looking text in the recipient's quiet
|
|
1469
|
+
hours, restricted content), with the reason in `fallback.error`, and
|
|
1470
|
+
`"skipped"` for a WhatsApp error an SMS can't fix.
|
|
1471
|
+
- There is no time-based fallback: a message WhatsApp accepted but hasn't
|
|
1472
|
+
delivered, for example to a phone that is off, stays `"pending"`, and no SMS
|
|
1473
|
+
goes unless WhatsApp later reports a failure.
|
|
1474
|
+
- `messages.get` and `messages.list` return the same fallback on the WhatsApp message, so check a
|
|
1475
|
+
pending one later. The SMS carries `fallback_of`, the WhatsApp message's id.
|
|
1476
|
+
|
|
1477
|
+
Guide: [Send WhatsApp with an SMS Fallback](https://sendly.live/docs/whatsapp/send-with-fallback).
|
|
1478
|
+
|
|
1429
1479
|
## RCS
|
|
1430
1480
|
|
|
1431
1481
|
Send branded rich messages — text with suggested replies and actions, or
|
|
@@ -1769,8 +1819,8 @@ string), `invalid_voice_mode`, `agent_required`,
|
|
|
1769
1819
|
the address could not be validated, and `e.response_body["suggested"]` holds
|
|
1770
1820
|
a corrected address when one was found); `Sendly::APIError` with
|
|
1771
1821
|
`status_code` 409 for `agent_disabled`, `agent_limit` (20 agents per
|
|
1772
|
-
workspace) and `agent_in_use`; and `Sendly::ServerError` for
|
|
1773
|
-
`voice_attach_failed
|
|
1822
|
+
workspace) and `agent_in_use`; and `Sendly::ServerError` for 503
|
|
1823
|
+
`voice_attach_failed`, `carrier_refused` and `voice_unavailable`,
|
|
1774
1824
|
raised after the client has already retried the 5xx on its own. Not every
|
|
1775
1825
|
`carrier_refused` is worth retrying: when the message says the number
|
|
1776
1826
|
couldn't be found for emergency registration, retrying won't help, so
|
data/lib/sendly/errors.rb
CHANGED
|
@@ -73,9 +73,9 @@ module Sendly
|
|
|
73
73
|
# e.g. +[{ "path" => "brand.ein", "message" => "Enter a 9-digit EIN" }]+
|
|
74
74
|
attr_reader :field_errors
|
|
75
75
|
|
|
76
|
-
def initialize(message = "Validation failed", field_errors: nil, details: nil, code: nil)
|
|
76
|
+
def initialize(message = "Validation failed", field_errors: nil, details: nil, code: nil, status_code: 400)
|
|
77
77
|
@field_errors = field_errors
|
|
78
|
-
super(message, code: code || "VALIDATION_ERROR", details: details, status_code:
|
|
78
|
+
super(message, code: code || "VALIDATION_ERROR", details: details, status_code: status_code)
|
|
79
79
|
end
|
|
80
80
|
end
|
|
81
81
|
|
|
@@ -127,7 +127,7 @@ module Sendly
|
|
|
127
127
|
|
|
128
128
|
error = case status
|
|
129
129
|
when 400, 422
|
|
130
|
-
ValidationError.new(message, details: details, field_errors: body["errors"], code: code)
|
|
130
|
+
ValidationError.new(message, details: details, field_errors: body["errors"], code: code, status_code: status)
|
|
131
131
|
when 401
|
|
132
132
|
AuthenticationError.new(message, code: code)
|
|
133
133
|
when 402
|
data/lib/sendly/messages.rb
CHANGED
|
@@ -86,7 +86,7 @@ module Sendly
|
|
|
86
86
|
# WhatsApp the 422 +whatsapp_send_failed+ when WhatsApp refused the
|
|
87
87
|
# message (final, not retried, not charged; cached under the
|
|
88
88
|
# idempotency key and replayed for 24 hours)
|
|
89
|
-
# @raise [Sendly::ServerError] On WhatsApp, the
|
|
89
|
+
# @raise [Sendly::ServerError] On WhatsApp, the 503 +whatsapp_send_failed+:
|
|
90
90
|
# the message provably never reached the carrier, so it was not sent
|
|
91
91
|
# and is safe to send again. It isn't charged and is never cached, so
|
|
92
92
|
# the client retries it like any 5xx first under the same idempotency
|
data/lib/sendly/version.rb
CHANGED
|
@@ -402,7 +402,7 @@ module Sendly
|
|
|
402
402
|
# @raise [Sendly::NotFoundError] HTTP 404 +number_not_found+ / +agent_not_found+
|
|
403
403
|
# @raise [Sendly::APIError] HTTP 409 +agent_disabled+ (the agent is
|
|
404
404
|
# switched off), 403 +forbidden+ / +live_key_required+
|
|
405
|
-
# @raise [Sendly::ServerError] HTTP
|
|
405
|
+
# @raise [Sendly::ServerError] HTTP 503 +voice_attach_failed+ (voice could
|
|
406
406
|
# not be switched on; try again) or 503 +voice_unavailable+, after the
|
|
407
407
|
# client's automatic retries. Either can follow a mode alone on a number
|
|
408
408
|
# whose voice is off.
|
|
@@ -454,7 +454,7 @@ module Sendly
|
|
|
454
454
|
# 422 +invalid_address+ when the address could not be validated, with a
|
|
455
455
|
# corrected address (or +nil+) in +e.response_body["suggested"]+
|
|
456
456
|
# @raise [Sendly::NotFoundError] HTTP 404 +number_not_found+
|
|
457
|
-
# @raise [Sendly::ServerError] HTTP
|
|
457
|
+
# @raise [Sendly::ServerError] HTTP 503 +carrier_refused+ when the
|
|
458
458
|
# registration was refused, raised after the client has already retried
|
|
459
459
|
# the 5xx on its own. When the message says the number couldn't be
|
|
460
460
|
# found for emergency registration, retrying won't help: contact
|
|
@@ -691,7 +691,7 @@ module Sendly
|
|
|
691
691
|
# +agent_disabled+ / +agent_limit+ / +agent_in_use+ (409) and +forbidden+
|
|
692
692
|
# / +live_key_required+ / +insufficient_permissions+ (403); and
|
|
693
693
|
# {Sendly::ServerError} (not an +APIError+) for +voice_attach_failed+ /
|
|
694
|
-
# +carrier_refused+
|
|
694
|
+
# +carrier_refused+ / +voice_unavailable+ (503) and
|
|
695
695
|
# +voice_internal_error+ (500). Every API error keeps the parsed body on
|
|
696
696
|
# {Sendly::Error#response_body}. The full list is {Call::ERROR_CODES}.
|
|
697
697
|
#
|
|
@@ -63,8 +63,8 @@ module Sendly
|
|
|
63
63
|
# The status of a WhatsApp signup. +status+ is +"initiated"+,
|
|
64
64
|
# +"registering"+ (WhatsApp is activating the number; activation usually
|
|
65
65
|
# takes a few minutes but can take hours, and a session that hasn't
|
|
66
|
-
# finished about 6 hours after it began fails with +registration_timeout+
|
|
67
|
-
#
|
|
66
|
+
# finished about 6 hours after it began fails with +registration_timeout+),
|
|
67
|
+
# +"verifying"+ (a number being added to an
|
|
68
68
|
# already-connected account, waiting for its code), +"active"+ or
|
|
69
69
|
# +"failed"+. The API does not send +"expired"+,
|
|
70
70
|
# so {#expired?} is never true; it stays for compatibility.
|
|
@@ -72,7 +72,8 @@ module Sendly
|
|
|
72
72
|
# +"active"+ (from the start for a number added by code); it is nil while
|
|
73
73
|
# +"initiated"+ or +"registering"+ and after a failure.
|
|
74
74
|
# +failure_reasons+ is set when the status is +"failed"+, with one code:
|
|
75
|
-
# +setup_fee_payment_failed
|
|
75
|
+
# +setup_fee_payment_failed+ (the connection couldn't be started),
|
|
76
|
+
# +signup_abandoned+, +meta_exchange_failed+,
|
|
76
77
|
# +registration_failed+, +waba_already_connected+, +waba_mismatch+ (the
|
|
77
78
|
# WhatsApp Business Account chosen in the Facebook step doesn't hold the
|
|
78
79
|
# verified number), +registration_timeout+ (activation hadn't finished
|
|
@@ -80,7 +81,6 @@ module Sendly
|
|
|
80
81
|
# +verification_start_failed+ (WhatsApp couldn't start verifying an added
|
|
81
82
|
# number), +verification_failed+ (too many wrong codes) or
|
|
82
83
|
# +verification_expired+ (a verifying session left untouched for an hour).
|
|
83
|
-
# If the connection fails, the $19 fee is refunded automatically.
|
|
84
84
|
#
|
|
85
85
|
# While the status is +"verifying"+, +verification_method+ (+"sms"+ or
|
|
86
86
|
# +"voice"+) and +verification_attempts_remaining+ are set, and on
|
|
@@ -90,7 +90,7 @@ module Sendly
|
|
|
90
90
|
# started, so after a resend it still shows the earlier code until the new
|
|
91
91
|
# one arrives. Once WhatsApp has checked a code, only a code that arrived
|
|
92
92
|
# after the last submission or resend is returned. A submission answered
|
|
93
|
-
# with
|
|
93
|
+
# with 503 +whatsapp_verification_unavailable+ is not counted, so the same
|
|
94
94
|
# unchecked code can come back, and submitting it again with
|
|
95
95
|
# {WhatsAppSignupResource#verify} is safe. All three are nil in any other
|
|
96
96
|
# status.
|
|
@@ -533,34 +533,31 @@ module Sendly
|
|
|
533
533
|
|
|
534
534
|
# Start connecting a number to WhatsApp.
|
|
535
535
|
#
|
|
536
|
-
#
|
|
536
|
+
# Connecting a number is free (no setup or monthly fee). Returns a
|
|
537
537
|
# +connect_url+. Completing the connection requires a human: hand the
|
|
538
538
|
# URL to your user — they open it in a browser and log in with Facebook
|
|
539
539
|
# to link their WhatsApp Business Account. Then poll {#get} until the
|
|
540
540
|
# status is +"active"+.
|
|
541
541
|
#
|
|
542
542
|
# Calling again for a number with an in-flight signup returns the
|
|
543
|
-
# existing signup (same +connect_url+)
|
|
543
|
+
# existing signup (same +connect_url+). Requires
|
|
544
544
|
# a live API key with the +whatsapp:write+ scope (a test key gets 403
|
|
545
545
|
# +whatsapp_requires_live_key+) and, in a team workspace, an owner or
|
|
546
546
|
# admin (+settings:write+). After the Facebook step the signup stays
|
|
547
547
|
# +"registering"+ while WhatsApp activates the number. Activation usually
|
|
548
548
|
# takes a few minutes but can take hours. If it hasn't finished about 6
|
|
549
549
|
# hours after the session began, the session fails with
|
|
550
|
-
# +registration_timeout
|
|
551
|
-
# fails, the $19 fee is refunded automatically; once the number has
|
|
552
|
-
# connected there is no refund, and a later disconnect gets nothing
|
|
553
|
-
# back.
|
|
550
|
+
# +registration_timeout+.
|
|
554
551
|
#
|
|
555
552
|
# To add a number to a WhatsApp Business Account this workspace has
|
|
556
553
|
# already connected, pass its +business_account_id+ (as shown on
|
|
557
554
|
# {WhatsAppSender#business_account_id}). There is no Facebook step and no
|
|
558
|
-
# +connect_url+:
|
|
555
|
+
# +connect_url+: it is free too, Meta sends the number a
|
|
559
556
|
# 6-digit code by text or voice call, and the session is +"verifying"+
|
|
560
557
|
# until you submit the code with {#verify}. Calling again for a number
|
|
561
|
-
# that is verifying returns the same session without
|
|
562
|
-
#
|
|
563
|
-
#
|
|
558
|
+
# that is verifying returns the same session without sending a second
|
|
559
|
+
# code; a verifying session older than 3 hours is expired and a new one
|
|
560
|
+
# is started.
|
|
564
561
|
#
|
|
565
562
|
# @param phone_number [String] The number to connect, in E.164 format.
|
|
566
563
|
# Must be an active number in your workspace.
|
|
@@ -575,20 +572,20 @@ module Sendly
|
|
|
575
572
|
# +business_account_id+.
|
|
576
573
|
# @return [WhatsAppSignupSession]
|
|
577
574
|
# @raise [Sendly::ServerError] +whatsapp_unavailable+ (503) while
|
|
578
|
-
# WhatsApp connections are unavailable.
|
|
575
|
+
# WhatsApp connections are unavailable. Only
|
|
579
576
|
# signup returns it, with +retryAfter+ 3600 in the body
|
|
580
577
|
# (+e.response_body["retryAfter"]+) and a +Retry-After: 3600+ header.
|
|
581
578
|
# The client retries it like any 5xx before raising.
|
|
582
579
|
# @raise [Sendly::RateLimitError] +whatsapp_signup_limit_reached+ (429)
|
|
583
|
-
# after 5 failed
|
|
584
|
-
# the next day.
|
|
580
|
+
# after 5 failed signups (or 20 cancelled) in 24 hours. Not retried;
|
|
581
|
+
# try again the next day.
|
|
585
582
|
# @raise [Sendly::NotFoundError] +whatsapp_business_account_not_found+
|
|
586
583
|
# (404) when +business_account_id+ isn't connected in this workspace
|
|
587
584
|
# with an active number.
|
|
588
585
|
# @raise [Sendly::ValidationError] +display_name_required+ (400) when no
|
|
589
586
|
# display name is given and the account has none to reuse, and
|
|
590
587
|
# +whatsapp_verification_start_failed+ (422) when WhatsApp refused to
|
|
591
|
-
# verify the number: final, the session failed
|
|
588
|
+
# verify the number: final, the session failed.
|
|
592
589
|
# @raise [Sendly::APIError] 409 +whatsapp_signup_in_progress+ (a Facebook
|
|
593
590
|
# connection for the number is in flight; +e.response_body["id"]+ is
|
|
594
591
|
# that session), 409 +whatsapp_verification_in_progress+ (a Facebook
|
|
@@ -597,14 +594,14 @@ module Sendly
|
|
|
597
594
|
# still being added, so try again in a moment) or 409
|
|
598
595
|
# +whatsapp_already_enabled+.
|
|
599
596
|
# @raise [Sendly::InsufficientCreditsError] 402 +payment_method_required+
|
|
600
|
-
# or +payment_failed
|
|
601
|
-
# @raise [Sendly::ServerError] +whatsapp_verification_start_failed+ (
|
|
602
|
-
# when WhatsApp couldn't start verifying the number: the session failed
|
|
603
|
-
#
|
|
604
|
-
#
|
|
605
|
-
#
|
|
606
|
-
#
|
|
607
|
-
#
|
|
597
|
+
# or +payment_failed+: not returned while connecting a number is free.
|
|
598
|
+
# @raise [Sendly::ServerError] +whatsapp_verification_start_failed+ (503)
|
|
599
|
+
# when WhatsApp couldn't start verifying the number: the session failed,
|
|
600
|
+
# so start again. With +business_account_id+, a 5xx is raised at once
|
|
601
|
+
# and never retried by the client, because each retry would start a
|
|
602
|
+
# new session (a timeout or dropped connection is never retried
|
|
603
|
+
# either). Without it the client retries a 5xx before raising, as
|
|
604
|
+
# before.
|
|
608
605
|
#
|
|
609
606
|
# @example
|
|
610
607
|
# signup = client.whatsapp.signup.create(phone_number: "+15559876543")
|
|
@@ -649,14 +646,14 @@ module Sendly
|
|
|
649
646
|
# (422) for a wrong code, with the attempts left in
|
|
650
647
|
# +e.response_body["attemptsRemaining"]+
|
|
651
648
|
# @raise [Sendly::APIError] 409 +whatsapp_verification_failed+ after 5
|
|
652
|
-
# wrong codes (the session failed
|
|
649
|
+
# wrong codes (the session failed and
|
|
653
650
|
# +whatsapp_account.failed+ fires), 409 +whatsapp_verification_busy+
|
|
654
651
|
# while another code for the number is being checked (try again), or
|
|
655
652
|
# 409 +signup_not_active+ when the signup isn't waiting for a code or is
|
|
656
653
|
# older than 3 hours
|
|
657
654
|
# @raise [Sendly::NotFoundError] +signup_not_found+ (404)
|
|
658
|
-
# @raise [Sendly::ServerError]
|
|
659
|
-
# (WhatsApp couldn't check the code; the attempt isn't counted) or
|
|
655
|
+
# @raise [Sendly::ServerError] 503 +whatsapp_verification_unavailable+
|
|
656
|
+
# (WhatsApp couldn't check the code; the attempt isn't counted) or 503
|
|
660
657
|
# +whatsapp_activation_pending+ (the code was accepted but connecting
|
|
661
658
|
# the number didn't finish; check back shortly with {#get}). A 5xx, a
|
|
662
659
|
# timeout or a dropped connection is raised at once and never retried
|
|
@@ -695,7 +692,7 @@ module Sendly
|
|
|
695
692
|
# waiting for a code or is older than 3 hours
|
|
696
693
|
# @raise [Sendly::NotFoundError] +signup_not_found+ (404)
|
|
697
694
|
# @raise [Sendly::ServerError] +whatsapp_verification_resend_failed+
|
|
698
|
-
# (
|
|
695
|
+
# (503) when WhatsApp couldn't be reached. The client retries a 5xx
|
|
699
696
|
# before raising.
|
|
700
697
|
#
|
|
701
698
|
# @example
|
|
@@ -847,7 +844,7 @@ module Sendly
|
|
|
847
844
|
# @raise [Sendly::APIError] +whatsapp_profile_photo_too_large+ with
|
|
848
845
|
# +status_code+ 413 when the photo is over 5 MB
|
|
849
846
|
# @raise [Sendly::NotFoundError] +whatsapp_sender_not_connected+ (404)
|
|
850
|
-
# @raise [Sendly::ServerError] +whatsapp_profile_update_failed+ (
|
|
847
|
+
# @raise [Sendly::ServerError] +whatsapp_profile_update_failed+ (503)
|
|
851
848
|
# when WhatsApp refused the image or couldn't be reached. A 5xx, a
|
|
852
849
|
# timeout or a dropped connection is raised at once and never retried
|
|
853
850
|
# by the client; check the image is
|
|
@@ -876,7 +873,7 @@ module Sendly
|
|
|
876
873
|
# @return [WhatsAppSenderProfile] The profile, normally with
|
|
877
874
|
# +profile_photo_url+ nil
|
|
878
875
|
# @raise [Sendly::NotFoundError] +whatsapp_sender_not_connected+ (404)
|
|
879
|
-
# @raise [Sendly::ServerError] +whatsapp_profile_update_failed+ (
|
|
876
|
+
# @raise [Sendly::ServerError] +whatsapp_profile_update_failed+ (503).
|
|
880
877
|
# The client retries a 5xx before raising.
|
|
881
878
|
#
|
|
882
879
|
# @example
|
|
@@ -899,7 +896,7 @@ module Sendly
|
|
|
899
896
|
# @return [WhatsAppConversationalComponents]
|
|
900
897
|
# @raise [Sendly::NotFoundError] +whatsapp_sender_not_connected+ (404)
|
|
901
898
|
# @raise [Sendly::ServerError]
|
|
902
|
-
# +whatsapp_conversational_components_fetch_failed+ (
|
|
899
|
+
# +whatsapp_conversational_components_fetch_failed+ (503). The client
|
|
903
900
|
# retries a 5xx before raising.
|
|
904
901
|
#
|
|
905
902
|
# @example
|
|
@@ -933,7 +930,7 @@ module Sendly
|
|
|
933
930
|
# +invalid_request+ (400) with a message naming the problem
|
|
934
931
|
# @raise [Sendly::NotFoundError] +whatsapp_sender_not_connected+ (404)
|
|
935
932
|
# @raise [Sendly::ServerError]
|
|
936
|
-
# +whatsapp_conversational_components_update_failed+ (
|
|
933
|
+
# +whatsapp_conversational_components_update_failed+ (503). The client
|
|
937
934
|
# retries a 5xx before raising.
|
|
938
935
|
#
|
|
939
936
|
# @example
|
|
@@ -974,7 +971,7 @@ module Sendly
|
|
|
974
971
|
# when Meta refused: calling needs the account at the 2,000
|
|
975
972
|
# recipients a day messaging limit and an approved display name
|
|
976
973
|
# @raise [Sendly::NotFoundError] +whatsapp_sender_not_connected+ (404)
|
|
977
|
-
# @raise [Sendly::ServerError] +whatsapp_calling_update_failed+ (
|
|
974
|
+
# @raise [Sendly::ServerError] +whatsapp_calling_update_failed+ (503).
|
|
978
975
|
# The client retries a 5xx before raising.
|
|
979
976
|
#
|
|
980
977
|
# @example
|
|
@@ -1153,7 +1150,7 @@ module Sendly
|
|
|
1153
1150
|
# create Meta-reviewed message templates, and send via
|
|
1154
1151
|
# +client.messages.send(channel: "whatsapp", ...)+.
|
|
1155
1152
|
#
|
|
1156
|
-
# Connecting a number is
|
|
1153
|
+
# Connecting a number is free (no setup or monthly fee). The first
|
|
1157
1154
|
# number always ends with a human step: {WhatsAppSignupResource#create}
|
|
1158
1155
|
# returns a +connect_url+ that a person must open in a browser and log in
|
|
1159
1156
|
# with Facebook to link their WhatsApp Business Account. More numbers can
|
|
@@ -1195,7 +1192,7 @@ module Sendly
|
|
|
1195
1192
|
# every method on this resource gets 404 +not_found+.
|
|
1196
1193
|
#
|
|
1197
1194
|
# @example Connect a number, create a template, then send
|
|
1198
|
-
# # 1. Connect (
|
|
1195
|
+
# # 1. Connect (free; a human must open the connect URL)
|
|
1199
1196
|
# signup = client.whatsapp.signup.create(phone_number: "+15559876543")
|
|
1200
1197
|
# puts "Have your user open: #{signup.connect_url}"
|
|
1201
1198
|
#
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sendly
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sendly
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|