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 sends the X-DIDWW-API-Version: 2026-04-16 header with every request by default.
| Module Version | Branch | DIDWW API Version |
|---|---|---|
| 3.x | main |
2026-04-16 |
| 2.x | 2022-05-10 |
2022-05-10 |
- Go 1.23+
go get github.com/didww/didww-api-3-go-sdk/v3Note on module path: Starting with v3, this module follows Go's
Semantic Import Versioning:
the major version appears in the import path (/v3). Projects pinned to v2
(import path without a suffix) are unaffected by go get -u and must
consciously update their imports to adopt v3.
package main
import (
"context"
"fmt"
didww "github.com/didww/didww-api-3-go-sdk/v3"
)
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/v3"
)
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)
// Address Requirements
requirements, _ := client.AddressRequirements().List(ctx, nil)
// Emergency Requirements (2026-04-16)
emergReqs, _ := client.EmergencyRequirements().List(ctx, nil)
// DID History (2026-04-16)
history, _ := client.DIDHistory().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/v3/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.
Voice Out Trunks use a polymorphic AuthenticationMethod (2026-04-16). Three types are supported:
credentials_and_ip-- default method;UsernameandPasswordare server-generated and returned in the response.twilio-- requires aTwilioAccountSid.ip_only-- read-only; can only be configured by DIDWW staff upon request. Cannot be set via the API.
import (
"github.com/didww/didww-api-3-go-sdk/v3/resource/authenticationmethod"
"github.com/didww/didww-api-3-go-sdk/v3/resource/enums"
)
// NOTE: 203.0.113.0/24 is RFC 5737 TEST-NET-3 documentation space.
// Replace with the real CIDR of your SIP infrastructure.
trunk := &didww.VoiceOutTrunk{
Name: "My Outbound Trunk",
AuthenticationMethod: &authenticationmethod.CredentialsAndIp{
AllowedSipIPs: []string{"203.0.113.0/24"},
},
DefaultDstAction: enums.DefaultDstActionAllowAll,
OnCliMismatchAction: enums.OnCliMismatchActionRejectCall,
MediaEncryptionMode: enums.MediaEncryptionModeDisabled,
}
created, _ := client.VoiceOutTrunks().Create(ctx, trunk)
// created.AuthenticationMethod.(*authenticationmethod.CredentialsAndIp).Username -- server-generated
// created.AuthenticationMethod.(*authenticationmethod.CredentialsAndIp).Password -- server-generated// 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/v3/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)// List emergency requirements filtered by country
params := didww.NewQueryParams().Filter("country.id", "country-uuid")
reqs, _ := client.EmergencyRequirements().List(ctx, params)
// Create emergency verification
verification := &didww.EmergencyVerification{
AddressID: "address-uuid",
IdentityID: "identity-uuid",
DIDIDs: []string{"did-uuid"},
}
created, _ := client.EmergencyVerifications().Create(ctx, verification)
// List emergency calling services
services, _ := client.EmergencyCallingServices().List(ctx, nil)// List DID history entries
params := didww.NewQueryParams().Filter("did.id", "did-uuid")
history, _ := client.DIDHistory().List(ctx, params)import "github.com/didww/didww-api-3-go-sdk/v3/resource/enums"
export := &didww.Export{
ExportType: enums.ExportTypeCdrIn,
Filters: map[string]interface{}{"from": "2026-04-01 00:00:00", "to": "2026-04-16 00:00:00"},
}
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 |
| Emergency | emergency_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 |
| AddressRequirement | client.AddressRequirements() |
list, find |
| EmergencyRequirement | client.EmergencyRequirements() |
list, find |
| DIDHistory | client.DIDHistory() |
list |
| 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, update |
| Address | client.Addresses() |
list, find, create, update, delete |
| AddressVerification | client.AddressVerifications() |
list, find, create, update |
| EmergencyCallingService | client.EmergencyCallingServices() |
list, find, delete |
| EmergencyVerification | client.EmergencyVerifications() |
list, find, create, update |
| EmergencyRequirementValidation | client.EmergencyRequirementValidations() |
create |
| Identity | client.Identities() |
list, find, create, update, delete |
| EncryptedFile | client.EncryptedFiles() |
list, find, delete |
| PermanentSupportingDocument | client.PermanentSupportingDocuments() |
create |
| Proof | client.Proofs() |
create |
| AddressRequirementValidation | client.AddressRequirementValidations() |
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):CreatedAt—time.Time, present on most resourcesExpiresAt—*time.Time:DID,DIDReservation,Proof,EncryptedFileActivatedAt—*time.Time:EmergencyCallingService(nullable)CanceledAt—*time.Time:EmergencyCallingService(nullable)
- Date-only fields remain as
stringin"YYYY-MM-DD"format — Go has no separate date-only type, so the raw string avoids timezone ambiguity:Identity.BirthDateCapacityPool.RenewDate,EmergencyCallingService.RenewDate(nullable)- Order item
BilledFrom/BilledTo
- String fields (not numeric):
EmergencyRequirement.EstimateSetupTime— e.g."7-14 days","1"EmergencyRequirement.RequirementRestrictionMessage— nullable
Important changes from previous API versions:
ExpiresAtreplacesExpireAtonDIDReservationandEncryptedFileRenewDateis a date-only string, NOT atime.TimeEstimateSetupTimeis a string, NOT an integer
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/v3/resource/enums:
CallbackMethod, IdentityType, OrderStatus, ExportType, ExportStatus, CliFormat,
OnCliMismatchAction*, MediaEncryptionMode, DefaultDstAction, VoiceOutTrunkStatus,
EmergencyCallingServiceStatus, EmergencyVerificationStatus, DiversionRelayPolicy,
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/v3"
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)Go enforces Semantic Import Versioning:
every major version ≥ 2 must live at a distinct import path (/v3, /v4, ...).
Bumping the major version is therefore mechanical — follow the same recipe as
established libraries (e.g. stripe-go's v84→v85 bump).
Steps for /vN → /v(N+1):
- Cut a maintenance branch from the current
mainbefore starting (e.g.release-N) and push it, so v(N) can still receive patch releases. - Create a feature branch from
main(e.g.feat/api-YYYY-MM-DD). - Update the module path in
go.mod:- module github.com/didww/didww-api-3-go-sdk/vN + module github.com/didww/didww-api-3-go-sdk/v(N+1)
- Rewrite every internal import across the repo:
grep -rln 'github.com/didww/didww-api-3-go-sdk/vN' --include='*.go' \ | xargs sed -i '' 's|didww-api-3-go-sdk/vN|didww-api-3-go-sdk/v(N+1)|g'
- Update this README: installation snippet, usage imports, and the API version compatibility table at the top.
- Update
examples/imports the same way. - Run
go build ./... && go test ./...— everything must stay green. - Commit as a single atomic change (
chore!: bump module path to /v(N+1)), merge tomain, then tagvN+1.0.0and push the tag.
Existing users on /vN are unaffected — they must consciously change their
import path to adopt the new major version.
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.