Go client for DIDWW API v3.
The DIDWW API provides a simple yet powerful interface that allows you to fully integrate your own applications with DIDWW services. An extensive set of actions may be performed using this API, such as ordering and configuring phone numbers, setting capacity, creating SIP trunks and retrieving CDRs and other operational data.
The DIDWW API v3 is a fully compliant implementation of the JSON API specification.
This SDK implements JSON:API serialization and deserialization without external dependencies, using only the Go standard library.
Read more https://doc.didww.com/api
This SDK targets DIDWW API v3 documentation version: https://doc.didww.com/api3/2022-05-10/index.html
The client sends the X-DIDWW-API-Version: 2022-05-10 header with each request.
- Go 1.23+
go get github.com/didww/didww-api-3-go-sdkpackage main
import (
"context"
"fmt"
didww "github.com/didww/didww-api-3-go-sdk"
)
func main() {
client, err := didww.NewClient("YOUR_API_KEY")
if err != nil {
panic(err)
}
// Check balance
balance, err := client.Balance().Find(context.Background())
if err != nil {
panic(err)
}
fmt.Println("Balance:", balance.TotalBalance)
// List DID groups with stock keeping units
params := didww.NewQueryParams().
Include("stock_keeping_units").
Filter("area_name", "Acapulco")
didGroups, err := client.DIDGroups().List(context.Background(), params)
if err != nil {
panic(err)
}
fmt.Println("DID groups:", len(didGroups))
}For more examples visit examples.
For details on obtaining your API key please visit https://doc.didww.com/api3/configuration.html
- Source code: examples
- How to run: examples/README.md
client, err := didww.NewClient("YOUR_API_KEY",
didww.WithEnvironment(didww.Production),
didww.WithTimeout(30000), // 30 seconds
)You can pass a custom *http.Client for advanced configuration such as proxy support:
import (
"net/http"
"net/url"
didww "github.com/didww/didww-api-3-go-sdk"
)
proxyURL, _ := url.Parse("http://proxy.example.com:8080")
httpClient := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(proxyURL),
},
}
client, err := didww.NewClient("YOUR_API_KEY",
didww.WithEnvironment(didww.Production),
didww.WithHTTPClient(httpClient),
)The API key header is added automatically. Any other HTTP settings (timeouts, TLS, proxies, transports) can be configured on the *http.Client.
| Environment | Base URL |
|---|---|
didww.Production |
https://api.didww.com/v3 |
didww.Sandbox |
https://sandbox-api.didww.com/v3 |
ctx := context.Background()
// Countries
countries, _ := client.Countries().List(ctx, nil)
country, _ := client.Countries().Find(ctx, "uuid")
// Regions
regions, _ := client.Regions().List(ctx, nil)
// Cities
cities, _ := client.Cities().List(ctx, nil)
// Areas
areas, _ := client.Areas().List(ctx, nil)
// NANPA Prefixes
prefixes, _ := client.NanpaPrefixes().List(ctx, nil)
// POPs (Points of Presence)
pops, _ := client.Pops().List(ctx, nil)
// DID Group Types
types, _ := client.DIDGroupTypes().List(ctx, nil)
// DID Groups (with stock keeping units)
params := didww.NewQueryParams().Include("stock_keeping_units")
groups, _ := client.DIDGroups().List(ctx, params)
// Available DIDs (with DID group and stock keeping units)
params = didww.NewQueryParams().Include("did_group.stock_keeping_units")
available, _ := client.AvailableDIDs().List(ctx, params)
// Proof Types
proofTypes, _ := client.ProofTypes().List(ctx, nil)
// Public Keys
publicKeys, _ := client.PublicKeys().List(ctx, nil)
// Requirements
requirements, _ := client.Requirements().List(ctx, nil)
// Supporting Document Templates
templates, _ := client.SupportingDocumentTemplates().List(ctx, nil)
// Balance (singleton)
balance, _ := client.Balance().Find(ctx)// List DIDs
dids, _ := client.DIDs().List(ctx, nil)
// Update DID - only changed fields are sent (dirty-only PATCH)
did, _ := client.DIDs().Find(ctx, "uuid")
desc := "Updated"
did.Description = &desc
updated, _ := client.DIDs().Update(ctx, did)import "github.com/didww/didww-api-3-go-sdk/resource/enums"
// Create SIP trunk
ringingTimeout := 30
trunk := &didww.VoiceInTrunk{
Name: "My SIP Trunk",
Priority: 1,
Weight: 100,
CliFormat: enums.CliFormatE164,
RingingTimeout: &ringingTimeout,
Configuration: &didww.SIPConfiguration{
Host: "sip.example.com",
Port: 5060,
CodecIDs: []enums.Codec{enums.CodecPCMU, enums.CodecPCMA},
TransportProtocolID: enums.TransportProtocolUDP,
},
}
created, _ := client.VoiceInTrunks().Create(ctx, trunk)
// Update trunk
desc := "Updated"
created.Description = &desc
updated, _ := client.VoiceInTrunks().Update(ctx, created)
// Delete trunk
client.VoiceInTrunks().Delete(ctx, created.ID)capacityLimit := 50
group := &didww.VoiceInTrunkGroup{
Name: "Primary Group",
CapacityLimit: &capacityLimit,
VoiceInTrunkIDs: []string{trunkA.ID, trunkB.ID},
}
created, _ := client.VoiceInTrunkGroups().Create(ctx, group)Note: Voice Out Trunks require additional account configuration. Contact DIDWW support to enable. The
replace_cliandrandomize_clivalues ofOnCliMismatchActionalso require account configuration.
import "github.com/didww/didww-api-3-go-sdk/resource/enums"
trunk := &didww.VoiceOutTrunk{
Name: "My Outbound Trunk",
AllowedSipIPs: []string{"0.0.0.0/0"},
AllowedRtpIPs: []string{"0.0.0.0/0"},
DstPrefixes: []string{},
DefaultDstAction: enums.DefaultDstActionAllowAll,
OnCliMismatchAction: enums.OnCliMismatchActionRejectCall,
MediaEncryptionMode: enums.MediaEncryptionModeDisabled,
}
created, _ := client.VoiceOutTrunks().Create(ctx, trunk)// Create order with DID order item
order := &didww.Order{
Items: []didww.OrderItem{
{
Type: "did_order_items",
Attributes: didww.OrderItemAttributes{
SkuID: "sku-uuid",
Qty: 2,
},
},
},
}
created, _ := client.Orders().Create(ctx, order)
// Delete order (cancel)
client.Orders().Delete(ctx, created.ID)reservation := &didww.DIDReservation{
Description: "Reserved for client",
AvailableDIDID: "available-did-uuid",
}
created, _ := client.DIDReservations().Create(ctx, reservation)
// Delete reservation
client.DIDReservations().Delete(ctx, created.ID)group := &didww.SharedCapacityGroup{
Name: "Shared Group",
SharedChannelsCount: 20,
MeteredChannelsCount: 0,
CapacityPoolID: "pool-uuid",
}
created, _ := client.SharedCapacityGroups().Create(ctx, group)import "github.com/didww/didww-api-3-go-sdk/resource/enums"
identity := &didww.Identity{
FirstName: "John",
LastName: "Doe",
PhoneNumber: "12125551234",
IdentityType: enums.IdentityTypePersonal,
CountryID: "country-uuid",
}
created, _ := client.Identities().Create(ctx, identity)address := &didww.Address{
CityName: "New York",
PostalCode: "10001",
Address: "123 Main St",
IdentityID: identity.ID,
CountryID: "country-uuid",
}
created, _ := client.Addresses().Create(ctx, address)cbURL := "http://example.com/callback"
cbMethod := "GET"
verification := &didww.AddressVerification{
CallbackURL: &cbURL,
CallbackMethod: &cbMethod,
AddressID: address.ID,
DIDIDs: []string{"did-uuid"},
}
created, _ := client.AddressVerifications().Create(ctx, verification)import "github.com/didww/didww-api-3-go-sdk/resource/enums"
export := &didww.Export{
ExportType: enums.ExportTypeCdrIn,
Filters: map[string]interface{}{"year": 2025, "month": 1},
}
created, _ := client.Exports().Create(ctx, export)params := didww.NewQueryParams().
Filter("country.id", "uuid").
Filter("name", "Arizona").
Include("country").
Sort("name").
Page(1, 25)
regions, _ := client.Regions().List(ctx, params)The SDK tracks which fields have been modified and sends only those fields in PATCH requests. This avoids overwriting server-side values that your code hasn't touched.
When you fetch a resource and modify it, only the changed fields are sent:
did, _ := client.DIDs().Find(ctx, "uuid")
did.DedicatedChannelsCount = 5
// PATCH payload includes only "dedicated_channels_count", not all attributes
updated, _ := client.DIDs().Update(ctx, did)Create a struct with just the ID to send a PATCH without fetching first:
desc := "New description"
updated, _ := client.DIDs().Update(ctx, &didww.DID{
ID: "uuid",
Description: &desc,
})
// PATCH payload includes only "description"Setting a pointer field to nil after it had a value sends an explicit null in the payload:
did, _ := client.DIDs().Find(ctx, "uuid")
did.Description = nil
// PATCH payload: { "description": null }
updated, _ := client.DIDs().Update(ctx, did)Setting a relationship ID to empty on a built resource sends "data": null for to-one relationships:
did, _ := client.DIDs().Find(ctx, "uuid")
did.VoiceInTrunkID = "trunk-uuid"
// PATCH payload includes: "relationships": { "voice_in_trunk": { "data": { ... } }, "voice_in_trunk_group": { "data": null } }
// Mutual exclusion is handled automatically.
updated, _ := client.DIDs().Update(ctx, did)Dirty tracking is automatically enabled on included (sideloaded) resources, so you can fetch with includes and update a related resource directly:
params := didww.NewQueryParams().Include("voice_in_trunk")
did, _ := client.DIDs().Find(ctx, "uuid", params)
trunk := did.VoiceInTrunk
desc := "Updated via include"
trunk.Description = &desc
updated, _ := client.VoiceInTrunks().Update(ctx, trunk)| Type | Struct |
|---|---|
| SIP | SIPConfiguration |
| PSTN | PSTNConfiguration |
| Type | JSON:API type |
|---|---|
| DID | did_order_items |
| Available DID | available_did_order_items |
| Reservation DID | reservation_did_order_items |
| Capacity | capacity_order_items |
| Generic (response only) | generic_order_items |
import "errors"
trunk, err := client.VoiceInTrunks().Find(ctx, "nonexistent")
if err != nil {
var apiErr *didww.APIError
if errors.As(err, &apiErr) {
fmt.Println("HTTP Status:", apiErr.HTTPStatus)
for _, e := range apiErr.Errors {
fmt.Println("Error:", e.Detail)
}
}
var clientErr *didww.ClientError
if errors.As(err, &clientErr) {
fmt.Println("Client error:", clientErr.Message)
}
}| Resource | Repository | Operations |
|---|---|---|
| Country | client.Countries() |
list, find |
| Region | client.Regions() |
list, find |
| City | client.Cities() |
list, find |
| Area | client.Areas() |
list, find |
| NanpaPrefix | client.NanpaPrefixes() |
list, find |
| Pop | client.Pops() |
list, find |
| DIDGroupType | client.DIDGroupTypes() |
list, find |
| DIDGroup | client.DIDGroups() |
list, find |
| AvailableDID | client.AvailableDIDs() |
list, find |
| ProofType | client.ProofTypes() |
list, find |
| PublicKey | client.PublicKeys() |
list |
| Requirement | client.Requirements() |
list, find |
| SupportingDocumentTemplate | client.SupportingDocumentTemplates() |
list, find |
| Balance | client.Balance() |
find |
| DID | client.DIDs() |
list, find, update, delete |
| VoiceInTrunk | client.VoiceInTrunks() |
list, find, create, update, delete |
| VoiceInTrunkGroup | client.VoiceInTrunkGroups() |
list, find, create, update, delete |
| VoiceOutTrunk | client.VoiceOutTrunks() |
list, find, create, update, delete |
| VoiceOutTrunkRegenerateCredential | client.VoiceOutTrunkRegenerateCredentials() |
create |
| DIDReservation | client.DIDReservations() |
list, find, create, delete |
| CapacityPool | client.CapacityPools() |
list, find, update |
| SharedCapacityGroup | client.SharedCapacityGroups() |
list, find, create, update, delete |
| Order | client.Orders() |
list, find, create, delete |
| Export | client.Exports() |
list, find, create |
| Address | client.Addresses() |
list, find, create, update, delete |
| AddressVerification | client.AddressVerifications() |
list, find, create |
| Identity | client.Identities() |
list, find, create, update, delete |
| EncryptedFile | client.EncryptedFiles() |
list, find, delete |
| PermanentSupportingDocument | client.PermanentSupportingDocuments() |
create |
| Proof | client.Proofs() |
create |
| RequirementValidation | client.RequirementValidations() |
create |
| StockKeepingUnit | include on DIDGroups |
— |
| QtyBasedPricing | include on CapacityPools |
— |
Note:
StockKeepingUnitandQtyBasedPricinghave no standalone API endpoints. Access them viaincludeonDIDGroupsandCapacityPoolsrespectively.
The SDK distinguishes between date-only and datetime fields:
- Datetime fields are deserialized as
time.Time(UTC) when always present, or*time.Timewhen optional (nil if the API omits the value):- All
CreatedAtfields —time.Time, present on most resources - Expiry fields —
*time.Time:DID.ExpiresAt,Proof.ExpiresAt,EncryptedFile.ExpireAt;DIDReservation.ExpireAtistime.Time(always present)
- All
- Date-only fields (
Identity.BirthDate,CapacityPool.RenewDate, order itemBilledFrom/BilledTo) remain asstringin"YYYY-MM-DD"format — Go has no separate date-only type, so the raw string avoids timezone ambiguity.
did, _ := client.DIDs().Find(ctx, "uuid")
fmt.Println(did.CreatedAt) // 2024-01-15 10:00:00 +0000 UTC
fmt.Println(did.ExpiresAt) // <nil> or &2025-01-15 10:00:00 +0000 UTC
identity, _ := client.Identities().Find(ctx, "uuid")
fmt.Println(identity.BirthDate) // "1990-05-20"The SDK provides enum types in github.com/didww/didww-api-3-go-sdk/resource/enums:
CallbackMethod, IdentityType, OrderStatus, ExportType, ExportStatus, CliFormat,
OnCliMismatchAction*, MediaEncryptionMode, DefaultDstAction, VoiceOutTrunkStatus,
TransportProtocol, Codec, RxDtmfFormat, TxDtmfFormat, SstRefreshMethod,
ReroutingDisconnectCode, Feature, AreaLevel, AddressVerificationStatus, StirShakenMode
* replace_cli and randomize_cli require account configuration.
Validate incoming webhook callbacks from DIDWW using HMAC-SHA1 signature verification.
import didww "github.com/didww/didww-api-3-go-sdk"
validator := didww.NewRequestValidator("YOUR_API_KEY")
// In your webhook handler:
signature := r.Header.Get(didww.SignatureHeaderName) // "X-DIDWW-Signature"
payload := map[string]string{"key": "value"} // parsed form/query payload
valid := validator.Validate(requestURL, payload, signature)Bug reports and pull requests are welcome on GitHub at https://github.com/didww/didww-api-3-go-sdk
The package is available as open source under the terms of the MIT License.