Phone numbers
Your lines (the IncomingPhoneNumbers resource): their webhook URLs, the dongle behind each one, and the line's
SIP login. Lines are assigned in the console; v1 has no buying or releasing of numbers.
GET /Accounts/{AccountSid}/IncomingPhoneNumbers.json
List your lines.
Lists your lines, one page at a time. A filter value that isn't valid, or a query parameter not listed here, answers 400 with code 790005.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
PhoneNumber | query | Only the line with this number (E.164). | |
PageSize | query | Items per page, ≤ 1000, default 50. List pages with | |
Page | query | The page number, from 0. Use the | |
PageToken | query | An opaque paging token. It comes inside |
Responses
| Status | Meaning |
|---|---|
200 | One page of lines. |
400 | A list filter value that isn't valid, or a query parameter the list doesn't take (790005, naming it in |
401 | Authentication failed (20003), or the account isn't active (20005). This comes before any lookup. |
429 | Over a rate (20429, with |
500 | An internal error (799999). |
Response fields
| Field | Type | Always there | Description |
|---|---|---|---|
page | integer | The page number, from 0. | |
page_size | integer | ||
uri | string | ||
first_page_uri | string | ||
next_page_uri | string or null | The next page, or null. | |
previous_page_uri | string or null | The previous page, or null. | |
incoming_phone_numbers | array of object | Your lines on this page. | |
incoming_phone_numbers[].sid | string | required | PN + 32 hex. |
incoming_phone_numbers[].account_sid | string | required | |
incoming_phone_numbers[].friendly_name | string | required | |
incoming_phone_numbers[].phone_number | string | required | The line's number, E.164. |
incoming_phone_numbers[].voice_url | string or null | required | Where incoming calls are sent (the |
incoming_phone_numbers[].voice_method | string: GET, POST | required | |
incoming_phone_numbers[].voice_fallback_url | string or null | required | |
incoming_phone_numbers[].voice_fallback_method | string: GET, POST | required | |
incoming_phone_numbers[].status_callback | string or null | required | The line's StatusCallback. An incoming call's status events go here, and so do runtime errors when the Call or Message has none of its own. |
incoming_phone_numbers[].status_callback_method | string: GET, POST | required | |
incoming_phone_numbers[].sms_url | string or null | required | Where incoming texts are sent (the |
incoming_phone_numbers[].sms_method | string: GET, POST | required | |
incoming_phone_numbers[].sms_fallback_url | string or null | required | |
incoming_phone_numbers[].sms_fallback_method | string: GET, POST | required | |
incoming_phone_numbers[].capabilities | object | required | What the line can do. |
incoming_phone_numbers[].capabilities.voice | boolean | required | True only on a calling dongle. |
incoming_phone_numbers[].capabilities.sms | boolean | required | Always true. |
incoming_phone_numbers[].capabilities.mms | boolean | required | Always false (no MMS in v1). |
incoming_phone_numbers[].capabilities.fax | boolean | required | Always false. |
incoming_phone_numbers[].date_created | string | required | RFC 2822. |
incoming_phone_numbers[].date_updated | string | required | RFC 2822. |
incoming_phone_numbers[].uri | string | required | |
incoming_phone_numbers[].dongle ours | object | required | Ours, showing the state of the dongle behind the line. |
incoming_phone_numbers[].dongle.state ours | string: serving, not_serving | required | Whether the line is on the network now. |
incoming_phone_numbers[].dongle.callable ours | boolean | required | Whether it can make or take a call now. |
incoming_phone_numbers[].dongle.why_not ours | string: no_calling_dongle, dongle_offline, line_suspended, temporarily_unavailable or null | required | Why the line can't call now, or null: |
incoming_phone_numbers[].dongle.why_not_text ours | string or null | required | The why_not reason in our fixed words (one per code), or null when the line can call. |
incoming_phone_numbers[].dongle.sip ours | object or null | required | The line's SIP login, or null on a texts-only line (no calling dongle). It's made automatically, and it's for softphones and for PBXs as a "SIP trunk with registration":
TLS 1.2+, SRTP required, digest auth. Its fields: One call per dongle, across API and SIP. The password is never shown back. SIP stays OFF on an
API-mode line until a password is set ( |
incoming_phone_numbers[].dongle.sip.username ours | string | required | The SIP username (made automatically). |
incoming_phone_numbers[].dongle.sip.domain ours | string | required | |
incoming_phone_numbers[].dongle.sip.port ours | integer | required | |
incoming_phone_numbers[].dongle.sip.transport ours | string | required | |
incoming_phone_numbers[].dongle.sip.srtp ours | string | required | |
incoming_phone_numbers[].dongle.sip.state ours | string: not_set, active | required | |
incoming_phone_numbers[].dongle.sip.password_set_at ours | string or null | required | When the SIP password was last set (RFC 2822), or null if it has never been set. |
incoming_phone_numbers[].dww_sip_password ours | string | Only in the response to |
Example request
curl -X GET "https://api.dongleswithwings.com/2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers.json" \
-u "$ACCOUNT_SID:$AUTH_TOKEN"
Example response
{
"page": 0,
"page_size": 50,
"uri": "/2010-04-01/Accounts/ACa61cd972830fc5bc41386edc0a9f8463/IncomingPhoneNumbers.json?PageSize=50&Page=0",
"first_page_uri": "/2010-04-01/Accounts/ACa61cd972830fc5bc41386edc0a9f8463/IncomingPhoneNumbers.json?PageSize=50&Page=0",
"next_page_uri": null,
"previous_page_uri": null,
"incoming_phone_numbers": [
{
"sid": "PN3e18acd0169a00c48635e530c42a01ca",
"account_sid": "ACa61cd972830fc5bc41386edc0a9f8463",
"friendly_name": "Reception",
"phone_number": "+61491570156",
"voice_url": "https://hooks.example.com/voice/incoming",
"voice_method": "POST",
"voice_fallback_url": "https://hooks-backup.example.com/voice/incoming",
"voice_fallback_method": "POST",
"status_callback": "https://hooks.example.com/status",
"status_callback_method": "POST",
"sms_url": "https://hooks.example.com/sms/incoming",
"sms_method": "POST",
"sms_fallback_url": null,
"sms_fallback_method": "POST",
"capabilities": {
"voice": true,
"sms": true,
"mms": false,
"fax": false
},
"date_created": "Fri, 16 Oct 2026 02:15:00 +0000",
"date_updated": "Mon, 02 Nov 2026 01:30:00 +0000",
"uri": "/2010-04-01/Accounts/ACa61cd972830fc5bc41386edc0a9f8463/IncomingPhoneNumbers/PN3e18acd0169a00c48635e530c42a01ca.json",
"dongle": {
"state": "serving",
"callable": true,
"why_not": null,
"why_not_text": null,
"sip": {
"username": "dww-4b7e19c2",
"domain": "voice.dongleswithwings.com",
"port": 5061,
"transport": "tls",
"srtp": "required",
"state": "active",
"password_set_at": "Mon, 02 Nov 2026 01:30:00 +0000"
}
}
},
{
"sid": "PN71a08432464f72a5c066b5937cde1ae7",
"account_sid": "ACa61cd972830fc5bc41386edc0a9f8463",
"friendly_name": "Reminders (texts only)",
"phone_number": "+61491570157",
"voice_url": null,
"voice_method": "POST",
"voice_fallback_url": null,
"voice_fallback_method": "POST",
"status_callback": null,
"status_callback_method": "POST",
"sms_url": "https://hooks.example.com/sms/incoming",
"sms_method": "POST",
"sms_fallback_url": null,
"sms_fallback_method": "POST",
"capabilities": {
"voice": false,
"sms": true,
"mms": false,
"fax": false
},
"date_created": "Fri, 16 Oct 2026 02:20:00 +0000",
"date_updated": "Fri, 16 Oct 2026 02:20:00 +0000",
"uri": "/2010-04-01/Accounts/ACa61cd972830fc5bc41386edc0a9f8463/IncomingPhoneNumbers/PN71a08432464f72a5c066b5937cde1ae7.json",
"dongle": {
"state": "serving",
"callable": false,
"why_not": "no_calling_dongle",
"why_not_text": "this line has no calling dongle: texts only",
"sip": null
}
}
]
}
GET /Accounts/{AccountSid}/IncomingPhoneNumbers/{PNSid}.json
Read one line.
Returns one line with its dongle extension. The SIP password is never shown back.
Responses
| Status | Meaning |
|---|---|
200 | The line. |
401 | Authentication failed (20003), or the account isn't active (20005). This comes before any lookup. |
404 | Not found (20404). That includes a Sid that isn't on the path's account; the answer never hints at another tenant. |
429 | Over a rate (20429, with |
500 | An internal error (799999). |
Response fields
| Field | Type | Always there | Description |
|---|---|---|---|
sid | string | PN + 32 hex. | |
account_sid | string | ||
friendly_name | string | ||
phone_number | string | The line's number, E.164. | |
voice_url | string or null | Where incoming calls are sent (the | |
voice_method | string: GET, POST | ||
voice_fallback_url | string or null | ||
voice_fallback_method | string: GET, POST | ||
status_callback | string or null | The line's StatusCallback. An incoming call's status events go here, and so do runtime errors when the Call or Message has none of its own. | |
status_callback_method | string: GET, POST | ||
sms_url | string or null | Where incoming texts are sent (the | |
sms_method | string: GET, POST | ||
sms_fallback_url | string or null | ||
sms_fallback_method | string: GET, POST | ||
capabilities | object | What the line can do. | |
capabilities.voice | boolean | required | True only on a calling dongle. |
capabilities.sms | boolean | required | Always true. |
capabilities.mms | boolean | required | Always false (no MMS in v1). |
capabilities.fax | boolean | required | Always false. |
date_created | string | RFC 2822. | |
date_updated | string | RFC 2822. | |
uri | string | ||
dongle ours | object | Ours, showing the state of the dongle behind the line. | |
dongle.state ours | string: serving, not_serving | required | Whether the line is on the network now. |
dongle.callable ours | boolean | required | Whether it can make or take a call now. |
dongle.why_not ours | string: no_calling_dongle, dongle_offline, line_suspended, temporarily_unavailable or null | required | Why the line can't call now, or null: |
dongle.why_not_text ours | string or null | required | The why_not reason in our fixed words (one per code), or null when the line can call. |
dongle.sip ours | object or null | required | The line's SIP login, or null on a texts-only line (no calling dongle). It's made automatically, and it's for softphones and for PBXs as a "SIP trunk with registration":
TLS 1.2+, SRTP required, digest auth. Its fields: One call per dongle, across API and SIP. The password is never shown back. SIP stays OFF on an
API-mode line until a password is set ( |
dongle.sip.username ours | string | required | The SIP username (made automatically). |
dongle.sip.domain ours | string | required | |
dongle.sip.port ours | integer | required | |
dongle.sip.transport ours | string | required | |
dongle.sip.srtp ours | string | required | |
dongle.sip.state ours | string: not_set, active | required | |
dongle.sip.password_set_at ours | string or null | required | When the SIP password was last set (RFC 2822), or null if it has never been set. |
dww_sip_password ours | string | Only in the response to |
Example request
curl -X GET "https://api.dongleswithwings.com/2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers/{PNSid}.json" \
-u "$ACCOUNT_SID:$AUTH_TOKEN"
Example response
{
"sid": "PN3e18acd0169a00c48635e530c42a01ca",
"account_sid": "ACa61cd972830fc5bc41386edc0a9f8463",
"friendly_name": "Reception",
"phone_number": "+61491570156",
"voice_url": "https://hooks.example.com/voice/incoming",
"voice_method": "POST",
"voice_fallback_url": "https://hooks-backup.example.com/voice/incoming",
"voice_fallback_method": "POST",
"status_callback": "https://hooks.example.com/status",
"status_callback_method": "POST",
"sms_url": "https://hooks.example.com/sms/incoming",
"sms_method": "POST",
"sms_fallback_url": null,
"sms_fallback_method": "POST",
"capabilities": {
"voice": true,
"sms": true,
"mms": false,
"fax": false
},
"date_created": "Fri, 16 Oct 2026 02:15:00 +0000",
"date_updated": "Mon, 02 Nov 2026 01:30:00 +0000",
"uri": "/2010-04-01/Accounts/ACa61cd972830fc5bc41386edc0a9f8463/IncomingPhoneNumbers/PN3e18acd0169a00c48635e530c42a01ca.json",
"dongle": {
"state": "not_serving",
"callable": false,
"why_not": "dongle_offline",
"why_not_text": "the line's dongle isn't connected right now",
"sip": {
"username": "dww-4b7e19c2",
"domain": "voice.dongleswithwings.com",
"port": 5061,
"transport": "tls",
"srtp": "required",
"state": "active",
"password_set_at": "Mon, 02 Nov 2026 01:30:00 +0000"
}
}
}
POST /Accounts/{AccountSid}/IncomingPhoneNumbers/{PNSid}.json
Change a line's webhook URLs, its name, or its SIP password.
v1 changes only the parameters listed here; DwwSipPassword is ours. Others answer 700009.
Updates the line's settings. Every URL must pass the fetch rules (see the Webhooks guide)
when it's set, and again each time it's used. Any parameter not listed here is refused with code 700009,
which names it, and a value that isn't valid (e.g. VoiceMethod=PUT) with code 700012.
Setting the SIP password (not yet available: today DwwSipPassword answers 700009, like any parameter not listed): it will use DwwSipPassword. The console uses this same API;
there's no other way in. Until then, a line that already has a SIP login keeps it, and a line without one has
SIP off. How it will work:
- DwwSipPassword=generate makes a strong password and returns it ONCE, as dww_sip_password in this
response.
- Rules: ≥ 16 characters of printable ASCII, from 3 of the 4 character classes. It must not contain the
username, the line's number or the AccountSid, and must not be on a deny list. Otherwise the answer is
740001, with reason one of too_short, charset, classes, contains_identity, deny_list.
- ≤ 5 changes per line per hour, otherwise 740002.
- How it's stored: the gateway computes the SIP digest hash (HA1) in memory with your line's SIP username and
realm, then drops the password. Only the HA1 is kept.
- The owner is emailed on every change, and every change is logged as an event with no value.
- SIP stays OFF on an API-mode line until a password is set (dongle.sip.state not_set), unless the
line already had a working login when it entered API mode. That login keeps working until the customer
sets their own.
- There's no recovery: if the password is lost, set a new one. The password is never shown back.
Request body
Form-encoded (application/x-www-form-urlencoded).
| Field | Type | Required | Description |
|---|---|---|---|
FriendlyName | string | A name for the line. | |
VoiceUrl | string | Where incoming calls are sent (the | |
VoiceMethod | string: GET, POST | Errors: 700012 | |
VoiceFallbackUrl | string | Runs only after a FAST failure of VoiceUrl with ≥ 4 s left of the 12 s window. | |
VoiceFallbackMethod | string: GET, POST | Errors: 700012 | |
StatusCallback | string | The line's StatusCallback. An incoming call's status events go here, and so do runtime errors when the Call or Message has none of its own. | |
StatusCallbackMethod | string: GET, POST | Errors: 700012 | |
SmsUrl | string | Where incoming texts are sent (the | |
SmsMethod | string: GET, POST | Errors: 700012 | |
SmsFallbackUrl | string | Asked once within a try of | |
SmsFallbackMethod | string: GET, POST | Errors: 700012 | |
DwwSipPassword ours | one of string | Sets the line's SIP password, or |
Any parameter not listed here is refused with 700009, naming it in parameter: v1 never ignores one.
Responses
| Status | Meaning |
|---|---|
200 | The updated line. |
400 | A value or a parameter was refused: 21205, 700009, 700012, 740001 or 790001. |
401 | Authentication failed (20003), or the account isn't active (20005). This comes before any lookup. |
404 | Not found (20404). That includes a Sid that isn't on the path's account; the answer never hints at another tenant. |
413 | The request body is over 64 KiB (790006). It's refused before parsing. |
429 | Too many requests (20429), or too many SIP password changes on this line (740002). |
500 | An internal error (799999). |
Response fields
| Field | Type | Always there | Description |
|---|---|---|---|
sid | string | PN + 32 hex. | |
account_sid | string | ||
friendly_name | string | ||
phone_number | string | The line's number, E.164. | |
voice_url | string or null | Where incoming calls are sent (the | |
voice_method | string: GET, POST | ||
voice_fallback_url | string or null | ||
voice_fallback_method | string: GET, POST | ||
status_callback | string or null | The line's StatusCallback. An incoming call's status events go here, and so do runtime errors when the Call or Message has none of its own. | |
status_callback_method | string: GET, POST | ||
sms_url | string or null | Where incoming texts are sent (the | |
sms_method | string: GET, POST | ||
sms_fallback_url | string or null | ||
sms_fallback_method | string: GET, POST | ||
capabilities | object | What the line can do. | |
capabilities.voice | boolean | required | True only on a calling dongle. |
capabilities.sms | boolean | required | Always true. |
capabilities.mms | boolean | required | Always false (no MMS in v1). |
capabilities.fax | boolean | required | Always false. |
date_created | string | RFC 2822. | |
date_updated | string | RFC 2822. | |
uri | string | ||
dongle ours | object | Ours, showing the state of the dongle behind the line. | |
dongle.state ours | string: serving, not_serving | required | Whether the line is on the network now. |
dongle.callable ours | boolean | required | Whether it can make or take a call now. |
dongle.why_not ours | string: no_calling_dongle, dongle_offline, line_suspended, temporarily_unavailable or null | required | Why the line can't call now, or null: |
dongle.why_not_text ours | string or null | required | The why_not reason in our fixed words (one per code), or null when the line can call. |
dongle.sip ours | object or null | required | The line's SIP login, or null on a texts-only line (no calling dongle). It's made automatically, and it's for softphones and for PBXs as a "SIP trunk with registration":
TLS 1.2+, SRTP required, digest auth. Its fields: One call per dongle, across API and SIP. The password is never shown back. SIP stays OFF on an
API-mode line until a password is set ( |
dongle.sip.username ours | string | required | The SIP username (made automatically). |
dongle.sip.domain ours | string | required | |
dongle.sip.port ours | integer | required | |
dongle.sip.transport ours | string | required | |
dongle.sip.srtp ours | string | required | |
dongle.sip.state ours | string: not_set, active | required | |
dongle.sip.password_set_at ours | string or null | required | When the SIP password was last set (RFC 2822), or null if it has never been set. |
dww_sip_password ours | string | Only in the response to |
Example request
curl -X POST "https://api.dongleswithwings.com/2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers/{PNSid}.json" \
--data-urlencode "FriendlyName=Reception" \
--data-urlencode "VoiceUrl=https://hooks.example.com/voice/incoming" \
--data-urlencode "VoiceMethod=POST" \
--data-urlencode "SmsUrl=https://hooks.example.com/sms/incoming" \
--data-urlencode "SmsMethod=POST" \
--data-urlencode "DwwSipPassword=generate" \
-u "$ACCOUNT_SID:$AUTH_TOKEN"
Example response
{
"sid": "PN3e18acd0169a00c48635e530c42a01ca",
"account_sid": "ACa61cd972830fc5bc41386edc0a9f8463",
"friendly_name": "Reception",
"phone_number": "+61491570156",
"voice_url": "https://hooks.example.com/voice/incoming",
"voice_method": "POST",
"voice_fallback_url": "https://hooks-backup.example.com/voice/incoming",
"voice_fallback_method": "POST",
"status_callback": "https://hooks.example.com/status",
"status_callback_method": "POST",
"sms_url": "https://hooks.example.com/sms/incoming",
"sms_method": "POST",
"sms_fallback_url": null,
"sms_fallback_method": "POST",
"capabilities": {
"voice": true,
"sms": true,
"mms": false,
"fax": false
},
"date_created": "Fri, 16 Oct 2026 02:15:00 +0000",
"date_updated": "Mon, 02 Nov 2026 01:30:00 +0000",
"uri": "/2010-04-01/Accounts/ACa61cd972830fc5bc41386edc0a9f8463/IncomingPhoneNumbers/PN3e18acd0169a00c48635e530c42a01ca.json",
"dongle": {
"state": "serving",
"callable": true,
"why_not": null,
"why_not_text": null,
"sip": {
"username": "dww-4b7e19c2",
"domain": "voice.dongleswithwings.com",
"port": 5061,
"transport": "tls",
"srtp": "required",
"state": "active",
"password_set_at": "Mon, 02 Nov 2026 01:30:00 +0000"
}
},
"dww_sip_password": "5wf9KFrC7L-GiRUx9WVl1UF-"
}