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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5a69a00fe63398e0ad342ed03ad9ddf38d668fe0d2ddd85533a2aa43e534eacb
4
- data.tar.gz: 8488579a38c0f09720a8d9e67a5084819eb884dfe6cc99afc66be5d143ae5323
3
+ metadata.gz: 511e36e32d4475552b4000a026856bde62c06bd7f43a52b59646c347d7ff4b0c
4
+ data.tar.gz: 92531f77aa366320d2887a3b9a0ac9793c6562d507f3863577352830097f4d32
5
5
  SHA512:
6
- metadata.gz: caa9cd2bb16dc70c5eea42f760fe9295587e0f3b8c7a83bb4de6af903b5ba29ff6ec89c6c53ab211fc630c68d8139648516a95dccd27412382be08012ba922ed
7
- data.tar.gz: b5ef93d9ce46c42dd53ef375b23aa66a5027e6534bf93425ab507d37f4c2f17311cdefbda52269e2fbad9b167808ad96abae255fd0b216942d5af1873d5f8337
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sendly (4.4.0)
4
+ sendly (4.5.0)
5
5
  faraday (~> 2.0)
6
6
  faraday-retry (~> 2.0)
7
7
 
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 a one-time $19 setup (no monthly fee). The first number always
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 (same $19 fee, refunded if it fails).
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 ($19 one-time; a human must open the connect URL)
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 and the fee is refunded.
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 502 whatsapp_verification_unavailable
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, charged signups in
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` (502) when the message
1355
- provably never reached the carrier, so it was not sent and is safe to send
1356
- again; the 502 is never cached and the client retries it first under the
1357
- same idempotency key. Neither is charged.
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 502 `whatsapp_verification_start_failed` (WhatsApp
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 and refunds its fee. With `business_account_id:` the client never
1388
- retries a 5xx, a timeout or a dropped connection, because each retry would
1389
- start a new charged session. A
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 and
1398
- the fee is refunded), `whatsapp_verification_busy` (try again) and
1399
- `signup_not_active`; and `Sendly::ServerError` for 502
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` (502).
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 502 `whatsapp_profile_update_failed`, and the
1415
- conversational component methods for 502
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 502 `whatsapp_calling_update_failed`.
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 502
1773
- `voice_attach_failed` and `carrier_refused` and 503 `voice_unavailable`,
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: 400)
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
@@ -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 502 +whatsapp_send_failed+:
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sendly
4
- VERSION = "4.4.0"
4
+ VERSION = "4.5.0"
5
5
  end
@@ -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 502 +voice_attach_failed+ (voice could
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 502 +carrier_refused+ when the
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+ (502), +voice_unavailable+ (503) and
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
- # and the fee is refunded), +"verifying"+ (a number being added to an
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+, +signup_abandoned+, +meta_exchange_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 502 +whatsapp_verification_unavailable+ is not counted, so the same
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
- # Charges a one-time $19 setup fee (no monthly fee) and returns a
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+) without charging again. Requires
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+ and the fee is refunded. If the connection
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+: the same $19 fee is charged, Meta sends the number a
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 charging again or
562
- # sending a second code; a verifying session older than 3 hours is
563
- # expired (and refunded) and a new one is started.
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. Nothing is charged. Only
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, charged signups in 24 hours. Not retried; try again
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 and the fee is refunded.
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+ when the $19 fee can't be charged.
601
- # @raise [Sendly::ServerError] +whatsapp_verification_start_failed+ (502)
602
- # when WhatsApp couldn't start verifying the number: the session failed
603
- # and its fee is refunded, so start again. With +business_account_id+,
604
- # a 5xx is raised at once and never retried by the client, because
605
- # each retry would start a new charged session (a timeout or dropped
606
- # connection is never retried either). Without it the client retries a
607
- # 5xx before raising, as before.
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, the fee is refunded and
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] 502 +whatsapp_verification_unavailable+
659
- # (WhatsApp couldn't check the code; the attempt isn't counted) or 502
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
- # (502) when WhatsApp couldn't be reached. The client retries a 5xx
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+ (502)
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+ (502).
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+ (502). The client
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+ (502). The client
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+ (502).
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 a one-time $19 setup (no monthly fee). The first
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 ($19 one-time; a human must open the connect URL)
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.0
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-17 00:00:00.000000000 Z
11
+ date: 2026-10-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday