didww-v3 5.3.0 → 6.1.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/.gitignore +7 -0
- data/CHANGELOG.md +68 -0
- data/README.md +214 -11
- data/didww-v3.gemspec +1 -0
- data/examples/README.md +14 -2
- data/examples/address_verifications.rb +50 -0
- data/examples/did_groups.rb +3 -1
- data/examples/did_history.rb +56 -0
- data/examples/dids.rb +7 -1
- data/examples/emergency_calling_services.rb +61 -0
- data/examples/emergency_requirement_validations.rb +47 -0
- data/examples/emergency_requirements.rb +48 -0
- data/examples/emergency_scenario.rb +204 -0
- data/examples/emergency_verifications.rb +66 -0
- data/examples/exports.rb +40 -6
- data/examples/identities_and_proofs.rb +7 -2
- data/examples/orders.rb +11 -2
- data/examples/orders_emergency.rb +63 -0
- data/examples/orders_reservation_dids.rb +1 -1
- data/examples/shared_capacity_groups.rb +1 -0
- data/examples/voice_in_trunk_groups.rb +6 -3
- data/examples/voice_in_trunks.rb +65 -0
- data/examples/voice_out_trunks.rb +41 -12
- data/lib/didww/callback/const.rb +2 -2
- data/lib/didww/client.rb +15 -5
- data/lib/didww/complex_objects/authentication_method.rb +18 -0
- data/lib/didww/complex_objects/authentication_methods/base.rb +29 -0
- data/lib/didww/complex_objects/authentication_methods/credentials_and_ip.rb +20 -0
- data/lib/didww/complex_objects/authentication_methods/generic.rb +34 -0
- data/lib/didww/complex_objects/authentication_methods/ip_only.rb +21 -0
- data/lib/didww/complex_objects/authentication_methods/twilio.rb +15 -0
- data/lib/didww/complex_objects/base.rb +35 -2
- data/lib/didww/complex_objects/configurations/sip_configuration.rb +142 -1
- data/lib/didww/complex_objects/emergency_order_item.rb +17 -0
- data/lib/didww/complex_objects/export_filters.rb +2 -3
- data/lib/didww/resource/address.rb +4 -0
- data/lib/didww/resource/{requirement.rb → address_requirement.rb} +1 -1
- data/lib/didww/resource/address_requirement_validation.rb +15 -0
- data/lib/didww/resource/address_verification.rb +13 -10
- data/lib/didww/resource/did.rb +8 -1
- data/lib/didww/resource/did_group.rb +10 -13
- data/lib/didww/resource/did_history.rb +72 -0
- data/lib/didww/resource/did_reservation.rb +1 -1
- data/lib/didww/resource/emergency_calling_service.rb +78 -0
- data/lib/didww/resource/emergency_requirement.rb +55 -0
- data/lib/didww/resource/emergency_requirement_validation.rb +24 -0
- data/lib/didww/resource/emergency_verification.rb +65 -0
- data/lib/didww/resource/encrypted_file.rb +17 -16
- data/lib/didww/resource/export.rb +15 -1
- data/lib/didww/resource/identity.rb +4 -3
- data/lib/didww/resource/order.rb +10 -5
- data/lib/didww/resource/permanent_supporting_document.rb +4 -0
- data/lib/didww/resource/proof.rb +4 -0
- data/lib/didww/resource/shared_capacity_group.rb +4 -0
- data/lib/didww/resource/voice_in_trunk.rb +4 -0
- data/lib/didww/resource/voice_in_trunk_group.rb +4 -0
- data/lib/didww/resource/voice_out_trunk.rb +47 -17
- data/lib/didww/types.rb +3 -1
- data/lib/didww/version.rb +1 -1
- metadata +38 -4
- data/lib/didww/resource/requirement_validation.rb +0 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9cdeedbb317141c84de28bbedb4f652b858793e257d8d84f70155b08fc54623c
|
|
4
|
+
data.tar.gz: c48319beb5292ef53595e7905d4ac62ff0e59685bcb57eb48c2f7e6efbb7bd3e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 42be04280b6673a827de5f4bc680f032f28d72437137d081455f2168aa02e92d3861b7fc842e6c70406e6495d1f377745f90a23ead254fdf0a5a6caf40417d63
|
|
7
|
+
data.tar.gz: e6aeccf261500d0307c5cbc7eae427880a853d008ce28639ff2512daa1ea1cb40c54962b3d0f972f7e3ea0a73274bc40a6a8a57b9df45689b9c0ddfb83987acb
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,74 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [6.1.0] - 2026-05-04
|
|
8
|
+
### Added
|
|
9
|
+
- Complete the 2026-04-16 `SipConfiguration` attribute set that was missed during the 2026-04-16 rollout (and is not present in the public Postman collection — server form is the source of truth):
|
|
10
|
+
- `enabled_sip_registration` (bool) — enables SIP registration; the API generates `incoming_auth_*` credentials when set true and requires `port` to be blank.
|
|
11
|
+
- `use_did_in_ruri` (bool) — uses DID number in R-URI; requires SIP registration to be enabled.
|
|
12
|
+
- `network_protocol_priority` (string enum) — `force_ipv4` / `force_ipv6` / `any` / `prefer_ipv4` / `prefer_ipv6`. Constants exposed as `NETWORK_PROTOCOL_PRIORITY_*` and `NETWORK_PROTOCOL_PRIORITIES`.
|
|
13
|
+
- `diversion_inject_mode` (string enum) — `none` / `did_number`. Constants exposed as `DIVERSION_INJECT_MODE_*` and `DIVERSION_INJECT_MODES`.
|
|
14
|
+
- `cnam_lookup` (bool) — enables CNAM resolution for inbound calls.
|
|
15
|
+
- `SipConfiguration#incoming_auth_username` and `#incoming_auth_password` — read-only server-generated SIP authentication credentials returned on `voice_in_trunks` when `enabled_sip_registration` is true.
|
|
16
|
+
- `ComplexObject::Base.property` accepts `read_only: true`. Read-only attributes are still parsed from server responses but are stripped from the JSON:API write payload, so round-tripping a loaded configuration through PATCH no longer triggers `400 Param not allowed`.
|
|
17
|
+
- `ComplexObject::Base.property` accepts `sensitive: true`. The default `#inspect` representation redacts those values with `[FILTERED]` so credentials never leak through default logging / REPL / unhandled exception output. Marked as sensitive: `SipConfiguration#auth_password`, `SipConfiguration#incoming_auth_username/password`, and `AuthenticationMethod::CredentialsAndIp#username/password`.
|
|
18
|
+
- `SipConfiguration` auto-cascades dependent fields whose constraints are server-enforced when set via property setters: `enabled_sip_registration = true` clears `host`/`port` (when previously set), `enabled_sip_registration = false` forces `use_did_in_ruri = false`, and assigning a non-blank `host` flips both flags. Constructor / `[]=` assignments bypass the cascade so server responses deserialize as-is.
|
|
19
|
+
|
|
20
|
+
## [6.0.0] - 2026-04-24
|
|
21
|
+
Support for DIDWW API version **2026-04-16**. The gem now sends `X-DIDWW-API-Version: 2026-04-16` with every request by default. Users staying on API `2022-05-10` should pin to the [`2022-05-10`](https://github.com/didww/didww-v3-ruby/tree/2022-05-10) branch (5.x).
|
|
22
|
+
|
|
23
|
+
### Breaking Changes
|
|
24
|
+
- Default `X-DIDWW-API-Version` header is now `2026-04-16`.
|
|
25
|
+
- Resource `requirement_validations` renamed to `address_requirement_validations`.
|
|
26
|
+
- Resource `requirements` renamed to `address_requirements`.
|
|
27
|
+
- `AddressRequirementValidation#requirement` relationship renamed to `address_requirement`.
|
|
28
|
+
- `DidGroup#requirement` relationship renamed to `address_requirement`.
|
|
29
|
+
- `DidReservation#expire_at` renamed to `expires_at`.
|
|
30
|
+
- `EncryptedFile#expire_at` renamed to `expires_at`.
|
|
31
|
+
- `AddressVerification#reject_reasons` is now an array of strings.
|
|
32
|
+
- Dropped `sms_out` from `DidGroup` features.
|
|
33
|
+
- `EncryptedFile` POST now accepts a single file per request.
|
|
34
|
+
- `Export` year/month filters replaced with `from`/`to` datetime range (`from` inclusive, `to` exclusive).
|
|
35
|
+
- `VoiceInTrunk` SIP configuration gains `diversion_relay_policy`.
|
|
36
|
+
- Attribute values standardized to lowercase `snake_case` (status/area-level enums, etc.).
|
|
37
|
+
- `Order#cancelled?` renamed to `canceled?`; `Order::STATUS_CANCELLED` → `STATUS_CANCELED` to match wire format (`status: "canceled"`).
|
|
38
|
+
- `VoiceOutTrunk`: flat credentials (`username`/`password`/`auth_type`) replaced with a polymorphic `authentication_method` relationship. Supported types: `CredentialsAuthenticationMethod`, `IpAuthenticationMethod`, `CredentialsAndIpAuthenticationMethod`, `TwilioAuthenticationMethod`, and `GenericAuthenticationMethod` (forward-compatible fallback).
|
|
39
|
+
- `DidGroup`: removed `features_human` and `FEATURES` constant.
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
- New resource `DidHistory` (`/v3/did_history`) with `meta.billing_cycles_count_changed`.
|
|
43
|
+
- New resource `EmergencyRequirement` (`/v3/emergency_requirements`).
|
|
44
|
+
- New resource `EmergencyRequirementValidation` (`/v3/emergency_requirement_validations`).
|
|
45
|
+
- New resource `EmergencyCallingService` (`/v3/emergency_calling_services`) with `address` has-one relationship.
|
|
46
|
+
- New resource `EmergencyVerification` (`/v3/emergency_verifications`).
|
|
47
|
+
- `external_reference_id` attribute on `Address`, `AddressVerification`, `Export`, `EmergencyVerification`, `Order`, `PermanentSupportingDocument`, `Proof`, `SharedCapacityGroup`, `VoiceInTrunkGroup`, `VoiceInTrunk`, `VoiceOutTrunk`.
|
|
48
|
+
- PATCH support for `external_reference_id` on `/v3/address_verifications/:id`, `/v3/exports/:id`, `/v3/emergency_verifications/:id`.
|
|
49
|
+
- `VoiceOutTrunk`: `emergency_enable_all`, `rtp_timeout`, `emergency_dids` has-many relationship, status predicate helpers.
|
|
50
|
+
- `Did`: `emergency_enabled`, `emergency_calling_service` / `emergency_verification` / `identity` has-one relationships, PATCH support for unassigning `emergency_calling_service`.
|
|
51
|
+
- `Identity`: `birth_country` has-one relationship.
|
|
52
|
+
- `DidGroup`: new features `p2p`, `a2p`, `emergency`, `cnam_out`; `service_restrictions` attribute.
|
|
53
|
+
- `AddressVerification`: `reject_comment` attribute.
|
|
54
|
+
- `Order`: new `EmergencyOrderItem` complex object.
|
|
55
|
+
- Status predicate helpers on `VoiceOutTrunk`, `EmergencyCallingService`, `AddressVerification`, `EmergencyVerification`, and `Order`.
|
|
56
|
+
- `Export` exposes `STATUS_PENDING` and `STATUS_PROCESSING`.
|
|
57
|
+
- `randomize_cli` added to `OnCliMismatchAction` constants.
|
|
58
|
+
- Examples for all new 2026-04-16 resources and an end-to-end `emergency_scenario` example.
|
|
59
|
+
|
|
60
|
+
### Changed
|
|
61
|
+
- Unknown `authentication_method` types are wrapped in `Generic` for forward compatibility.
|
|
62
|
+
- Resource-level meta support for `EmergencyCallingService` and `EmergencyRequirement`.
|
|
63
|
+
- `ExportFilters` from/to semantics clarified (from=inclusive, to=exclusive).
|
|
64
|
+
- Examples: fixtures use RFC 5737 documentation IPs (`203.0.113.0/24`); `SecureRandom.hex(4)` used for random IDs.
|
|
65
|
+
- Shared rspec examples extracted for PATCH `external_reference_id` and requirement validations POST→204.
|
|
66
|
+
|
|
67
|
+
### Fixed
|
|
68
|
+
- Declare `has_one :address` on `EmergencyCallingService` to mirror server.
|
|
69
|
+
- `features_human` falls back to raw key for unknown features (superseded by removal in 6.0).
|
|
70
|
+
|
|
71
|
+
## [5.3.1]
|
|
72
|
+
### Fixed
|
|
73
|
+
- Correct `VoiceOutTrunk` constant wire values to match API responses.
|
|
74
|
+
|
|
7
75
|
## [5.3.0]
|
|
8
76
|
### Added
|
|
9
77
|
- `DIDWW::Client.customize_connection` to allow customizing the Faraday connection (e.g. proxy, timeouts, custom middleware).
|
data/README.md
CHANGED
|
@@ -16,15 +16,17 @@ This SDK uses [json_api_client](https://github.com/JsonApiClient/json_api_client
|
|
|
16
16
|
|
|
17
17
|
Read more https://doc.didww.com/api
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
## API Version
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
This SDK sends the `X-DIDWW-API-Version: 2026-04-16` header with every request by default.
|
|
22
22
|
|
|
23
|
-
Gem
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
| Gem Version | Branch | DIDWW API Version |
|
|
24
|
+
|-------------|--------|-------------------|
|
|
25
|
+
| **6.x** | [`master`](https://github.com/didww/didww-v3-ruby) | [`2026-04-16`](https://doc.didww.com/api3/2026-04-16/index.html) |
|
|
26
|
+
| **5.x** | [`2022-05-10`](https://github.com/didww/didww-v3-ruby/tree/2022-05-10) | [`2022-05-10`](https://doc.didww.com/api3/2022-05-10/index.html) |
|
|
27
|
+
| **3.x** | [`release-3`](https://github.com/didww/didww-v3-ruby/tree/release-3) | [`2021-12-15`](https://doc.didww.com/api3/2021-12-15/index.html) |
|
|
28
|
+
| **2.x** | [`release-2`](https://github.com/didww/didww-v3-ruby/tree/release-2) | [`2021-04-19`](https://doc.didww.com/api3/2021-04-19/index.html) |
|
|
29
|
+
| **1.x** | [`release-1`](https://github.com/didww/didww-v3-ruby/tree/release-1) | [`2017-09-18`](https://doc.didww.com/api3/2017-09-18/index.html) |
|
|
28
30
|
|
|
29
31
|
## Installation
|
|
30
32
|
|
|
@@ -132,7 +134,7 @@ end
|
|
|
132
134
|
|
|
133
135
|
### API Version
|
|
134
136
|
|
|
135
|
-
The SDK sends `X-DIDWW-API-Version:
|
|
137
|
+
The SDK sends `X-DIDWW-API-Version: 2026-04-16` by default. You can override it per block (e.g., to pin to a previous API version during migration):
|
|
136
138
|
|
|
137
139
|
```ruby
|
|
138
140
|
DIDWW::Client.with_api_version('2022-05-10') do
|
|
@@ -203,6 +205,70 @@ trunk = DIDWW::Client.voice_in_trunks.new(
|
|
|
203
205
|
trunk.save
|
|
204
206
|
```
|
|
205
207
|
|
|
208
|
+
#### SIP Registration (2026-04-16)
|
|
209
|
+
|
|
210
|
+
A Voice In Trunk can also be configured to authenticate inbound calls
|
|
211
|
+
via SIP registration credentials generated by DIDWW. The SDK
|
|
212
|
+
auto-cascades the dependent fields the server requires:
|
|
213
|
+
|
|
214
|
+
* setting `enabled_sip_registration = true` clears any previously-set
|
|
215
|
+
`host` / `port` (the server rejects them with 422 otherwise);
|
|
216
|
+
* setting `host` to a non-blank value flips `enabled_sip_registration`
|
|
217
|
+
back to `false` and forces `use_did_in_ruri = false` so the server
|
|
218
|
+
accepts the disable PATCH.
|
|
219
|
+
|
|
220
|
+
DIDWW generates `incoming_auth_username` and `incoming_auth_password`
|
|
221
|
+
and surfaces them in the response when SIP registration is enabled.
|
|
222
|
+
|
|
223
|
+
```ruby
|
|
224
|
+
trunk = DIDWW::Client.voice_in_trunks.new(
|
|
225
|
+
name: 'Office (registered)',
|
|
226
|
+
configuration: DIDWW::ComplexObject::SipConfiguration.new.tap do |c|
|
|
227
|
+
c.enabled_sip_registration = true
|
|
228
|
+
c.use_did_in_ruri = true
|
|
229
|
+
c.cnam_lookup = true
|
|
230
|
+
end
|
|
231
|
+
)
|
|
232
|
+
trunk.save
|
|
233
|
+
# trunk.configuration.incoming_auth_username -- server-generated
|
|
234
|
+
# trunk.configuration.incoming_auth_password -- server-generated
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
To disable SIP registration on an existing trunk, just set `host` —
|
|
238
|
+
the cascade flips `enabled_sip_registration` to `false` and
|
|
239
|
+
`use_did_in_ruri` to `false` automatically:
|
|
240
|
+
|
|
241
|
+
```ruby
|
|
242
|
+
trunk.configuration = DIDWW::ComplexObject::SipConfiguration.new.tap do |c|
|
|
243
|
+
c.host = 'sip.example.com'
|
|
244
|
+
end
|
|
245
|
+
trunk.save
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Voice Out Trunks
|
|
249
|
+
|
|
250
|
+
Voice Out Trunks use a polymorphic `authentication_method` (2026-04-16). Three types are supported:
|
|
251
|
+
|
|
252
|
+
- **`credentials_and_ip`** -- default method; `username` and `password` are server-generated and returned in the response.
|
|
253
|
+
- **`twilio`** -- requires a `twilio_account_sid`.
|
|
254
|
+
- **`ip_only`** -- read-only; can only be configured by DIDWW staff upon request. Cannot be set via the API.
|
|
255
|
+
|
|
256
|
+
```ruby
|
|
257
|
+
# NOTE: 203.0.113.0/24 is RFC 5737 TEST-NET-3 documentation space.
|
|
258
|
+
# Replace with the real CIDR of your SIP infrastructure.
|
|
259
|
+
trunk = DIDWW::Client.voice_out_trunks.new(
|
|
260
|
+
name: 'My Outbound Trunk',
|
|
261
|
+
on_cli_mismatch_action: 'reject_call',
|
|
262
|
+
authentication_method: DIDWW::ComplexObject::AuthenticationMethod::CredentialsAndIp.new(
|
|
263
|
+
allowed_sip_ips: ['203.0.113.0/24']
|
|
264
|
+
)
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
trunk.save
|
|
268
|
+
# trunk.authentication_method.username -- server-generated
|
|
269
|
+
# trunk.authentication_method.password -- server-generated
|
|
270
|
+
```
|
|
271
|
+
|
|
206
272
|
### Orders
|
|
207
273
|
|
|
208
274
|
```ruby
|
|
@@ -219,16 +285,141 @@ order = DIDWW::Client.orders.new(
|
|
|
219
285
|
order.save
|
|
220
286
|
```
|
|
221
287
|
|
|
288
|
+
### Emergency Services
|
|
289
|
+
|
|
290
|
+
```ruby
|
|
291
|
+
# List emergency requirements with filters
|
|
292
|
+
requirements = DIDWW::Client.emergency_requirements
|
|
293
|
+
.includes(:country, :did_group_type)
|
|
294
|
+
.all
|
|
295
|
+
|
|
296
|
+
# Filter by country
|
|
297
|
+
requirements = DIDWW::Client.emergency_requirements
|
|
298
|
+
.where('country.id': 'country-uuid')
|
|
299
|
+
.all
|
|
300
|
+
|
|
301
|
+
requirements.each do |req|
|
|
302
|
+
puts req.identity_type
|
|
303
|
+
puts req.address_area_level
|
|
304
|
+
puts req.estimate_setup_time # e.g. "7-14 days"
|
|
305
|
+
puts req.requirement_restriction_message
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# Create an emergency verification
|
|
309
|
+
verification = DIDWW::Client.emergency_verifications.new(
|
|
310
|
+
address: DIDWW::Resource::Address.load(id: 'address-uuid'),
|
|
311
|
+
emergency_calling_service:
|
|
312
|
+
DIDWW::Resource::EmergencyCallingService.load(id: 'ecs-uuid'),
|
|
313
|
+
dids: [DIDWW::Resource::Did.load(id: 'did-uuid')],
|
|
314
|
+
external_reference_id: 'my-ref-123'
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
if verification.save
|
|
318
|
+
puts "Created: #{verification.id} (status: #{verification.status})"
|
|
319
|
+
else
|
|
320
|
+
puts "Errors: #{verification.errors.full_messages}"
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# List emergency calling services
|
|
324
|
+
services = DIDWW::Client.emergency_calling_services
|
|
325
|
+
.includes(:country, :did_group_type, :dids)
|
|
326
|
+
.all
|
|
327
|
+
|
|
328
|
+
services.each do |svc|
|
|
329
|
+
puts "#{svc.name} — #{svc.status}"
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
# Cancel (destroy) an emergency calling service
|
|
333
|
+
svc = DIDWW::Client.emergency_calling_services.find('uuid').first
|
|
334
|
+
svc.destroy
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### DID History
|
|
338
|
+
|
|
339
|
+
```ruby
|
|
340
|
+
# List recent DID history events (retained for the last 90 days)
|
|
341
|
+
events = DIDWW::Client.did_history.all
|
|
342
|
+
|
|
343
|
+
events.each do |event|
|
|
344
|
+
puts "#{event.created_at.iso8601} #{event.did_number} #{event.action} via #{event.method}"
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# Filter by action
|
|
348
|
+
assigned = DIDWW::Client.did_history
|
|
349
|
+
.where(action: DIDWW::Resource::DidHistory::ACTION_ASSIGNED)
|
|
350
|
+
.all
|
|
351
|
+
|
|
352
|
+
# Filter by DID number
|
|
353
|
+
per_number = DIDWW::Client.did_history
|
|
354
|
+
.where(did_number: '12125551234')
|
|
355
|
+
.all
|
|
356
|
+
|
|
357
|
+
# Filter by date range
|
|
358
|
+
seven_days_ago = (Time.now - 7 * 24 * 60 * 60).iso8601
|
|
359
|
+
recent = DIDWW::Client.did_history
|
|
360
|
+
.where(created_at_gteq: seven_days_ago)
|
|
361
|
+
.all
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
## Error Handling
|
|
365
|
+
|
|
366
|
+
The SDK uses [json_api_client](https://github.com/JsonApiClient/json_api_client) which raises exceptions for HTTP-level errors. Validation errors from the API are returned on the resource's `errors` collection after a failed `save`.
|
|
367
|
+
|
|
368
|
+
```ruby
|
|
369
|
+
# Validation errors (422 Unprocessable Entity)
|
|
370
|
+
trunk = DIDWW::Client.voice_in_trunks.new(name: '')
|
|
371
|
+
unless trunk.save
|
|
372
|
+
trunk.errors.full_messages.each do |msg|
|
|
373
|
+
puts "Validation error: #{msg}"
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# HTTP errors (404, 401, 500, etc.)
|
|
378
|
+
begin
|
|
379
|
+
DIDWW::Client.dids.find('nonexistent-uuid')
|
|
380
|
+
rescue JsonApiClient::Errors::NotFound => e
|
|
381
|
+
puts "Not found: #{e.message}"
|
|
382
|
+
rescue JsonApiClient::Errors::AccessDenied => e
|
|
383
|
+
puts "Access denied: #{e.message}"
|
|
384
|
+
rescue JsonApiClient::Errors::ServerError => e
|
|
385
|
+
puts "Server error: #{e.message}"
|
|
386
|
+
rescue JsonApiClient::Errors::ConnectionError => e
|
|
387
|
+
puts "Connection error: #{e.message}"
|
|
388
|
+
end
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
## Dirty Tracking
|
|
392
|
+
|
|
393
|
+
The SDK (via `json_api_client`) tracks which attributes have been modified. When you call `save` on a fetched resource, the resulting PATCH request sends only the changed attributes, avoiding unintended overwrites of server-side values.
|
|
394
|
+
|
|
395
|
+
```ruby
|
|
396
|
+
did = DIDWW::Client.dids.find('uuid').first
|
|
397
|
+
did.description = 'Updated'
|
|
398
|
+
did.save
|
|
399
|
+
# PATCH payload includes only "description", not all attributes
|
|
400
|
+
```
|
|
401
|
+
|
|
222
402
|
## Date and Datetime Fields
|
|
223
403
|
|
|
224
404
|
The SDK distinguishes between date-only and datetime fields:
|
|
225
405
|
|
|
226
406
|
- **Datetime fields** — deserialized as `Time`:
|
|
227
|
-
-
|
|
228
|
-
-
|
|
407
|
+
- `created_at` — present on most resources
|
|
408
|
+
- `expires_at` — `Did`, `DidReservation`, `Proof`, `EncryptedFile` (nullable)
|
|
409
|
+
- `activated_at` — `EmergencyCallingService` (nullable)
|
|
410
|
+
- `canceled_at` — `EmergencyCallingService` (nullable)
|
|
229
411
|
- **Date-only fields** — deserialized as `Date`:
|
|
230
412
|
- `Identity#birth_date`
|
|
231
|
-
- **Date-only fields kept as strings**
|
|
413
|
+
- **Date-only fields kept as strings** — remain as `String`:
|
|
414
|
+
- `CapacityPool#renew_date`, `EmergencyCallingService#renew_date` — `"YYYY-MM-DD"` (nullable)
|
|
415
|
+
- **String fields** (not numeric):
|
|
416
|
+
- `EmergencyRequirement#estimate_setup_time` — e.g. `"7-14 days"`, `"1"`
|
|
417
|
+
- `EmergencyRequirement#requirement_restriction_message` — nullable
|
|
418
|
+
|
|
419
|
+
**Important changes from previous API versions:**
|
|
420
|
+
- `expire_at` renamed to `expires_at` on `DidReservation` and `EncryptedFile`
|
|
421
|
+
- `renew_date` is a date-only string, NOT a datetime
|
|
422
|
+
- `estimate_setup_time` is a string, NOT an integer
|
|
232
423
|
|
|
233
424
|
```ruby
|
|
234
425
|
did = DIDWW::Client.dids.find("uuid").first
|
|
@@ -283,6 +474,18 @@ class WebhooksController < ApplicationController
|
|
|
283
474
|
end
|
|
284
475
|
```
|
|
285
476
|
|
|
477
|
+
## Enums
|
|
478
|
+
|
|
479
|
+
The SDK provides constants for all API option fields. These are defined as constants on their respective modules/classes:
|
|
480
|
+
|
|
481
|
+
`CallbackMethod`, `IdentityType`, `OrderStatus`, `ExportType`, `ExportStatus`, `CliFormat`,
|
|
482
|
+
`OnCliMismatchAction`\*, `MediaEncryptionMode`, `DefaultDstAction`, `VoiceOutTrunkStatus`,
|
|
483
|
+
`EmergencyCallingServiceStatus`, `EmergencyVerificationStatus`, `DiversionRelayPolicy`,
|
|
484
|
+
`TransportProtocol`, `Codec`, `RxDtmfFormat`, `TxDtmfFormat`, `SstRefreshMethod`,
|
|
485
|
+
`ReroutingDisconnectCode`, `Feature`, `AreaLevel`, `AddressVerificationStatus`, `StirShakenMode`
|
|
486
|
+
|
|
487
|
+
\* `REPLACE_CLI` and `RANDOMIZE_CLI` require additional account configuration. Contact DIDWW support to enable these values.
|
|
488
|
+
|
|
286
489
|
## Development
|
|
287
490
|
|
|
288
491
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/didww-v3.gemspec
CHANGED
data/examples/README.md
CHANGED
|
@@ -34,7 +34,8 @@ DIDWW_API_KEY=your_api_key ruby examples/orders_nanpa.rb
|
|
|
34
34
|
| [`regions.rb`](regions.rb) | Lists regions with filters/includes and fetches a specific region. |
|
|
35
35
|
| [`did_groups.rb`](did_groups.rb) | Fetches DID groups with included SKUs and shows group details. |
|
|
36
36
|
| [`dids.rb`](dids.rb) | Updates DID routing/capacity by assigning trunk and capacity pool. |
|
|
37
|
-
| [`
|
|
37
|
+
| [`did_history.rb`](did_history.rb) | Lists DID ownership history (last 90 days, 2026-04-16). |
|
|
38
|
+
| [`exports.rb`](exports.rb) | Creates and lists CDR exports, with 2026-04-16 external_reference_id. |
|
|
38
39
|
|
|
39
40
|
### Voice In (Inbound)
|
|
40
41
|
| Script | Description |
|
|
@@ -77,7 +78,18 @@ DIDWW_API_KEY=your_api_key ruby examples/orders_nanpa.rb
|
|
|
77
78
|
### Compliance & Verification
|
|
78
79
|
| Script | Description |
|
|
79
80
|
|---|---|
|
|
80
|
-
| [`identities_and_proofs.rb`](identities_and_proofs.rb) | Creates identities, addresses, and demonstrates proof workflow. |
|
|
81
|
+
| [`identities_and_proofs.rb`](identities_and_proofs.rb) | Creates identities, addresses, and demonstrates proof workflow (2026-04-16 birth_country). |
|
|
82
|
+
| [`address_verifications.rb`](address_verifications.rb) | Lists address verifications with 2026-04-16 reject_comment / external_reference_id. |
|
|
83
|
+
|
|
84
|
+
### Emergency Services (2026-04-16)
|
|
85
|
+
| Script | Description |
|
|
86
|
+
|---|---|
|
|
87
|
+
| [`emergency_requirements.rb`](emergency_requirements.rb) | Lists emergency service requirements per country/did_group_type. |
|
|
88
|
+
| [`emergency_calling_services.rb`](emergency_calling_services.rb) | Lists and cancels customer emergency calling services. |
|
|
89
|
+
| [`emergency_verifications.rb`](emergency_verifications.rb) | Lists and creates emergency verifications. |
|
|
90
|
+
| [`emergency_requirement_validations.rb`](emergency_requirement_validations.rb) | Pre-validates an emergency order triple (requirement + address + identity). |
|
|
91
|
+
| [`orders_emergency.rb`](orders_emergency.rb) | Inspects server-created Emergency orders and `emergency_order_items`. |
|
|
92
|
+
| [`emergency_scenario.rb`](emergency_scenario.rb) | End-to-end: find DID → check requirements → validate → create verification → get service. |
|
|
81
93
|
|
|
82
94
|
## Troubleshooting
|
|
83
95
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Lists Address Verifications (with 2026-04-16 reject_comment / external_reference_id).
|
|
3
|
+
#
|
|
4
|
+
# AddressVerification ties an address to one or more DIDs and a set of
|
|
5
|
+
# supporting documents so DIDWW compliance can approve or reject the
|
|
6
|
+
# declaration. 2026-04-16 adds:
|
|
7
|
+
# * reject_comment — free-form comment accompanying a rejection
|
|
8
|
+
# * external_reference_id — customer-supplied reference (max 100 chars)
|
|
9
|
+
#
|
|
10
|
+
# Usage: DIDWW_API_KEY=your_api_key ruby examples/address_verifications.rb
|
|
11
|
+
|
|
12
|
+
require 'bundler/setup'
|
|
13
|
+
require 'didww'
|
|
14
|
+
|
|
15
|
+
DIDWW::Client.configure do |client|
|
|
16
|
+
client.api_key = ENV.fetch('DIDWW_API_KEY') { abort 'Please set DIDWW_API_KEY' }
|
|
17
|
+
client.api_mode = :sandbox
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
puts '=== Address Verifications ==='
|
|
21
|
+
verifications = DIDWW::Client.address_verifications
|
|
22
|
+
.includes(:address, :dids)
|
|
23
|
+
.all
|
|
24
|
+
puts "Found #{verifications.size} address verifications"
|
|
25
|
+
|
|
26
|
+
verifications.first(5).each do |av|
|
|
27
|
+
puts "\nVerification: #{av.id}"
|
|
28
|
+
puts " Reference: #{av.reference}"
|
|
29
|
+
puts " Status: #{av.status}"
|
|
30
|
+
puts " External Reference: #{av.external_reference_id}" if av.external_reference_id
|
|
31
|
+
puts " Service description: #{av.service_description}" if av.service_description
|
|
32
|
+
puts " Address: #{av.address&.id}"
|
|
33
|
+
puts " DIDs: #{av.dids.map(&:number).join(', ')}" if av.dids&.any?
|
|
34
|
+
if av.rejected?
|
|
35
|
+
puts " Reject reasons: #{Array(av.reject_reasons).join(', ')}"
|
|
36
|
+
puts " Reject comment: #{av.reject_comment}" if av.reject_comment
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Filter: only rejected verifications
|
|
41
|
+
puts "\n=== Rejected verifications ==="
|
|
42
|
+
rejected = DIDWW::Client.address_verifications
|
|
43
|
+
.where(status: DIDWW::Resource::AddressVerification::STATUS_REJECTED)
|
|
44
|
+
.all
|
|
45
|
+
puts "Found #{rejected.size} rejected verifications"
|
|
46
|
+
rejected.first(3).each do |av|
|
|
47
|
+
puts " #{av.reference}: #{av.reject_comment || Array(av.reject_reasons).join(', ')}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
puts "\nAvailable statuses: #{DIDWW::Resource::AddressVerification::STATUSES.join(', ')}"
|
data/examples/did_groups.rb
CHANGED
|
@@ -24,7 +24,9 @@ did_groups.first(3).each do |did_group|
|
|
|
24
24
|
puts " Area: #{did_group.area_name}"
|
|
25
25
|
puts " Prefix: #{did_group.prefix}"
|
|
26
26
|
puts " Metered: #{did_group.is_metered}"
|
|
27
|
-
puts " Features: #{did_group.
|
|
27
|
+
puts " Features: #{did_group.features.join(', ')}" # 2026-04-16 adds p2p / a2p / emergency / cnam_out
|
|
28
|
+
puts " Allow additional channels: #{did_group.allow_additional_channels}" # 2026-04-16
|
|
29
|
+
puts " Service restrictions: #{did_group.service_restrictions}" if did_group.service_restrictions # 2026-04-16
|
|
28
30
|
|
|
29
31
|
if did_group.stock_keeping_units && !did_group.stock_keeping_units.empty?
|
|
30
32
|
puts ' SKUs:'
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Lists DID ownership history (2026-04-16).
|
|
3
|
+
# Records are retained for the last 90 days only.
|
|
4
|
+
#
|
|
5
|
+
# Server-side filters supported:
|
|
6
|
+
# did_number (eq), action (eq), method (eq),
|
|
7
|
+
# created_at_gteq, created_at_lteq
|
|
8
|
+
#
|
|
9
|
+
# Usage: DIDWW_API_KEY=your_api_key ruby examples/did_history.rb
|
|
10
|
+
|
|
11
|
+
require 'bundler/setup'
|
|
12
|
+
require 'didww'
|
|
13
|
+
|
|
14
|
+
DIDWW::Client.configure do |client|
|
|
15
|
+
client.api_key = ENV.fetch('DIDWW_API_KEY') { abort 'Please set DIDWW_API_KEY' }
|
|
16
|
+
client.api_mode = :sandbox
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# List most recent DID history events (server sorts by created_at desc)
|
|
20
|
+
puts '=== Recent DID History ==='
|
|
21
|
+
events = DIDWW::Client.did_history.all
|
|
22
|
+
puts "Found #{events.size} events in the last 90 days"
|
|
23
|
+
|
|
24
|
+
events.first(10).each do |event|
|
|
25
|
+
puts "#{event.created_at.iso8601} #{event.did_number.ljust(16)} #{event.action.ljust(28)} via #{event.method}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Filter by action
|
|
29
|
+
puts "\n=== Only 'assigned' events ==="
|
|
30
|
+
assigned = DIDWW::Client.did_history
|
|
31
|
+
.where(action: DIDWW::Resource::DidHistory::ACTION_ASSIGNED)
|
|
32
|
+
.all
|
|
33
|
+
puts "Found #{assigned.size} assignments"
|
|
34
|
+
|
|
35
|
+
# Filter by a specific DID number
|
|
36
|
+
if (first_event = events.first)
|
|
37
|
+
number = first_event.did_number
|
|
38
|
+
puts "\n=== History for DID #{number} ==="
|
|
39
|
+
per_number = DIDWW::Client.did_history
|
|
40
|
+
.where(did_number: number)
|
|
41
|
+
.all
|
|
42
|
+
per_number.each do |event|
|
|
43
|
+
puts "#{event.created_at.iso8601} #{event.action} via #{event.method}"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Filter by date range (last 7 days)
|
|
48
|
+
seven_days_ago = (Time.now - 7 * 24 * 60 * 60).iso8601
|
|
49
|
+
puts "\n=== History since #{seven_days_ago} ==="
|
|
50
|
+
recent = DIDWW::Client.did_history
|
|
51
|
+
.where(created_at_gteq: seven_days_ago)
|
|
52
|
+
.all
|
|
53
|
+
puts "Found #{recent.size} events in the last 7 days"
|
|
54
|
+
|
|
55
|
+
puts "\nAvailable actions: #{DIDWW::Resource::DidHistory::ACTIONS.join(', ')}"
|
|
56
|
+
puts "Available methods: #{DIDWW::Resource::DidHistory::METHODS.join(', ')}"
|
data/examples/dids.rb
CHANGED
|
@@ -11,9 +11,10 @@ DIDWW::Client.configure do |client|
|
|
|
11
11
|
client.api_mode = :sandbox
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
# Get the last ordered DID
|
|
14
|
+
# Get the last ordered DID (include 2026-04-16 emergency relationships)
|
|
15
15
|
puts '=== Finding last ordered DID ==='
|
|
16
16
|
dids = DIDWW::Client.dids
|
|
17
|
+
.includes(:identity, :emergency_calling_service, :emergency_verification)
|
|
17
18
|
.all
|
|
18
19
|
|
|
19
20
|
if dids.empty?
|
|
@@ -23,6 +24,11 @@ end
|
|
|
23
24
|
|
|
24
25
|
did = dids.first
|
|
25
26
|
puts "Selected DID: #{did.id}"
|
|
27
|
+
puts " Number: #{did.number}"
|
|
28
|
+
puts " Emergency enabled: #{did.emergency_enabled}" if did.respond_to?(:emergency_enabled)
|
|
29
|
+
puts " Emergency Calling Service: #{did.emergency_calling_service&.id}" if did.emergency_calling_service
|
|
30
|
+
puts " Emergency Verification: #{did.emergency_verification&.id}" if did.emergency_verification
|
|
31
|
+
puts " Identity: #{did.identity&.id}" if did.identity
|
|
26
32
|
|
|
27
33
|
# Get last SIP trunk
|
|
28
34
|
puts "\n=== Finding SIP trunk ==="
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Lists and cancels customer Emergency Calling Services (2026-04-16).
|
|
3
|
+
#
|
|
4
|
+
# An EmergencyCallingService represents a customer's 911/112 subscription
|
|
5
|
+
# attached to one or more DIDs. It ties an address, identity, DID group type
|
|
6
|
+
# and country together with a pricing snapshot (meta.setup_price, meta.monthly_price).
|
|
7
|
+
#
|
|
8
|
+
# Supported operations: index, show, destroy (cancel).
|
|
9
|
+
#
|
|
10
|
+
# Usage: DIDWW_API_KEY=your_api_key ruby examples/emergency_calling_services.rb
|
|
11
|
+
|
|
12
|
+
require 'bundler/setup'
|
|
13
|
+
require 'didww'
|
|
14
|
+
|
|
15
|
+
DIDWW::Client.configure do |client|
|
|
16
|
+
client.api_key = ENV.fetch('DIDWW_API_KEY') { abort 'Please set DIDWW_API_KEY' }
|
|
17
|
+
client.api_mode = :sandbox
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
puts '=== Emergency Calling Services ==='
|
|
21
|
+
services = DIDWW::Client.emergency_calling_services
|
|
22
|
+
.includes(:country, :did_group_type, :dids)
|
|
23
|
+
.all
|
|
24
|
+
puts "Found #{services.size} emergency calling services"
|
|
25
|
+
|
|
26
|
+
services.first(5).each do |svc|
|
|
27
|
+
puts "\nService: #{svc.id}"
|
|
28
|
+
puts " Name: #{svc.name}"
|
|
29
|
+
puts " Reference: #{svc.reference}"
|
|
30
|
+
puts " Status: #{svc.status}"
|
|
31
|
+
puts " Country: #{svc.country&.name}"
|
|
32
|
+
puts " DID Group Type: #{svc.did_group_type&.name}"
|
|
33
|
+
puts " Activated: #{svc.activated_at}"
|
|
34
|
+
puts " Canceled: #{svc.canceled_at}" if svc.canceled_at
|
|
35
|
+
puts " Renews: #{svc.renew_date}" if svc.renew_date
|
|
36
|
+
puts " Attached DIDs: #{svc.dids.map(&:number).join(', ')}" if svc.dids&.any?
|
|
37
|
+
if svc.meta
|
|
38
|
+
puts " Setup price: #{svc.meta[:setup_price]}" if svc.meta[:setup_price]
|
|
39
|
+
puts " Monthly price: #{svc.meta[:monthly_price]}" if svc.meta[:monthly_price]
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Filter by status
|
|
44
|
+
puts "\n=== Only active emergency calling services ==="
|
|
45
|
+
active = DIDWW::Client.emergency_calling_services
|
|
46
|
+
.where(status: 'active')
|
|
47
|
+
.all
|
|
48
|
+
puts "Found #{active.size} active services"
|
|
49
|
+
|
|
50
|
+
# Cancel a service (destroy) — uncomment to try:
|
|
51
|
+
#
|
|
52
|
+
# if (svc = services.find { |s| s.status == 'active' })
|
|
53
|
+
# puts "\nCancelling service #{svc.id}..."
|
|
54
|
+
# if svc.destroy
|
|
55
|
+
# puts 'Service canceled'
|
|
56
|
+
# else
|
|
57
|
+
# puts "Error: #{svc.errors.full_messages}"
|
|
58
|
+
# end
|
|
59
|
+
# end
|
|
60
|
+
|
|
61
|
+
puts "\nAvailable statuses: #{DIDWW::Resource::EmergencyCallingService::STATUSES.join(', ')}"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Validates an emergency calling service order before placing it (2026-04-16).
|
|
3
|
+
#
|
|
4
|
+
# EmergencyRequirementValidation is a write-only endpoint: POST the
|
|
5
|
+
# intended (emergency_requirement, address, identity) triple and the server
|
|
6
|
+
# either returns 204 No Content (OK to order) or JSONAPI errors describing
|
|
7
|
+
# what the customer must fix (missing address fields, wrong identity type,
|
|
8
|
+
# unsupported area level, etc.).
|
|
9
|
+
#
|
|
10
|
+
# Usage: DIDWW_API_KEY=your_api_key ruby examples/emergency_requirement_validations.rb
|
|
11
|
+
|
|
12
|
+
require 'bundler/setup'
|
|
13
|
+
require 'didww'
|
|
14
|
+
|
|
15
|
+
DIDWW::Client.configure do |client|
|
|
16
|
+
client.api_key = ENV.fetch('DIDWW_API_KEY') { abort 'Please set DIDWW_API_KEY' }
|
|
17
|
+
client.api_mode = :sandbox
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Pick any emergency requirement + address + identity from your account.
|
|
21
|
+
requirement = DIDWW::Client.emergency_requirements.all.first
|
|
22
|
+
address = DIDWW::Client.addresses.all.first
|
|
23
|
+
identity = DIDWW::Client.identities.all.first
|
|
24
|
+
|
|
25
|
+
abort 'No emergency_requirements found on this account' unless requirement
|
|
26
|
+
abort 'No addresses found on this account' unless address
|
|
27
|
+
abort 'No identities found on this account' unless identity
|
|
28
|
+
|
|
29
|
+
puts 'Validating order setup with:'
|
|
30
|
+
puts " Emergency Requirement: #{requirement.id}"
|
|
31
|
+
puts " Address: #{address.id}"
|
|
32
|
+
puts " Identity: #{identity.id}"
|
|
33
|
+
|
|
34
|
+
validation = DIDWW::Client.emergency_requirement_validation.new(
|
|
35
|
+
relationships: {
|
|
36
|
+
emergency_requirement: requirement,
|
|
37
|
+
address: address,
|
|
38
|
+
identity: identity
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
if validation.save
|
|
43
|
+
puts "\nValidation passed — this combination can be used to order emergency calling."
|
|
44
|
+
else
|
|
45
|
+
puts "\nValidation failed:"
|
|
46
|
+
validation.errors.full_messages.each { |msg| puts " * #{msg}" }
|
|
47
|
+
end
|