This document is for an older version of Kazoo (version 4.3) that is no longer supported. You should upgrade and read the current documentation.

Resources

About Resources

Resources represent external assets such as TDM hardware, SIP trunks, trans-coders, and other remote termination/originating call services or equipment.

There are two levels of resources, global (or system-wide), and per-account (bring your own carrier). The JSON format for both is identical; only their location in the Kazoo database structure defines whether they are globally available or not.

When interacting with an account’s resources, the URL structure is as one would expect: /v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}. To modify the global resources, simply omit /accounts/{ACCOUNT_ID} from the URL (your auth token must have super-duper admin privileges).

To perform bulk resource operations use the collections endpoints.

About Adding Bulk Numbers

It is possible to add numbers, in bulk, to an account using the Jobs API below. If a job fails to run, there is a recovery process that runs periodically to attempt to resume stalled jobs.

You can configure how frequently the system checks for failed jobs in system_config/crossbar.resources, using the job_recover_timeout_s key (defaults to 6 hours).

You can configure how what is considered a ‘stalled’ job by defining how old the job is (the last time the job document was modified) relative to the current time. Configure in system_config/crossbar.resources, using the job_recover_threshold_s key (defaults to 1 hour). If a job is not completed, and hasn’t been modified in over an hour, there’s a good chance the job executor died. A new job executor will be started to pick up where the old one left off.

Schema

Schema for resources

KeyDescriptionTypeDefaultRequiredSupport Level
caller_id_options.typeCaller ID type to choose`string(‘internal''external''emergency’)`
caller_id_optionsCaller ID optionsobject()false
cid_rules.[]string()false
cid_rulesRegexps to match against caller IDarray(string())false
classifiers./.+/.emergencyDetermines if the resource represents emergency servicesboolean()falsefalse
classifiers./.+/.enabledDetermines if the resource is currently enabledboolean()truefalse
classifiers./.+/.prefixA string to prepend to the dialed number or capture group of the matching rulestring(0..64)false
classifiers./.+/.regexregexp to match against dialed numberstring()false
classifiers./.+/.suffixA string to append to the dialed number or capture group of the matching rulestring(0..64)false
classifiers./.+/.weight_costA value between 0 and 100 that determines the order of resources when multiple can be usedinteger()50false
classifiers./.+/object()false
classifiersResource classifiers to use as rules when matching against dialed numbersobject()false
emergencyDetermines if the resource represents emergency servicesboolean()falsefalse
enabledDetermines if the resource is currently enabledboolean()truefalse
flags.[]string()false
flagsA list of flags that can be provided on the request and must match for the resource to be eligiblearray(string())[]false
flat_rate_blacklistRegex for determining if a number should not be eligible for flat-rate trunkingstring()false
flat_rate_whitelistRegex for determining if the number is eligible for flat-rate trunkingstring()false
format_from_uriWhen set to true requests to this resource will have a reformatted SIP From Headerboolean()false
formattersSchema for request formattersobject()false
from_account_realmWhen formatting SIP From on outbound requests, use the calling account’s SIP realmboolean()falsefalse
from_uri_realmWhen formatting SIP From on outbound requests this can be used to override the realmstring()false
gateway_strategyThe strategy of choosing gateways from list: sequential or random`string(‘sequential''random’)`false
gateways.[].bypass_mediaThe resource gateway bypass media modeboolean()false
gateways.[].caller_id_typeThe type of caller id to use`string(‘internal''external''emergency’)`
gateways.[].channel_selectionAutomatic selection of the channel within the span: ascending starts at 1 and moves up; descending is the opposite`string(‘ascending''descending’)`ascendingfalse
gateways.[].codecs.[]`string(‘G729''PCMU''PCMA''G722_16'
gateways.[].codecsA list of single list codecs supported by this gateway (to support backward compatibility)`array(string(‘G729''PCMU''PCMA''G722_16'
gateways.[].custom_sip_headers.inCustom SIP Headers to be applied to calls inbound to Kazoo from the endpoint#/definitions/custom_sip_headersfalse
gateways.[].custom_sip_headers.outCustom SIP Headers to be applied to calls outbound from Kazoo to the endpoint#/definitions/custom_sip_headersfalse
gateways.[].custom_sip_headers.^[a-zA-z0-9_\-]+$The SIP header to addstring()false
gateways.[].custom_sip_headersA property list of SIP headersobject()false
gateways.[].custom_sip_interfaceThe name of a custom SIP interfacestring()false
gateways.[].enabledDetermines if the resource gateway is currently enabledboolean()truefalse
gateways.[].endpoint_typeWhat type of endpoint is this gateway`string(‘sip''freetdm''skype''amqp’)`
gateways.[].force_portAllow request only from this portboolean()falsefalse
gateways.[].format_from_uriWhen set to true requests to this resource gateway will have a reformatted SIP From Headerboolean()false
gateways.[].from_uri_realmWhen formatting SIP From on outbound requests this can be used to override the realmstring()false
gateways.[].invite_formatThe format of the DID needed by the underlying hardware/gateway`string(‘route''username''e164''npan'
gateways.[].invite_parameters.dynamic.[]`string()string()string(‘zone’)object()`
gateways.[].invite_parameters.dynamicA list of properties that, if found on the inbound call, should be added as an INVITE parameterarray()false
gateways.[].invite_parameters.static.[]string()false
gateways.[].invite_parameters.staticA list of static values that should be added as INVITE parametersarray(string())false
gateways.[].invite_parametersobject()false
gateways.[].media.fax_optionIs T.38 Supported?boolean()false
gateways.[].media.rtcp_muxRTCP protocol messages mixed with RTP databoolean()false
gateways.[].mediaThe media parameters for the resource gatewayobject()false
gateways.[].passwordSIP authentication passwordstring(0..32)false
gateways.[].portThis resource gateway portinteger()5060false
gateways.[].prefixA string to prepend to the dialed number or capture group of the matching rulestring(0..64)false
gateways.[].progress_timeoutThe progress timeout to apply to the resource gatewayinteger()false
gateways.[].realmThis resource gateway authentication realmstring(0..64)false
gateways.[].routeA statically configured SIP URI to route all call tostring()false
gateways.[].serverThis resource gateway serverstring(1..128)true
gateways.[].skype_interfaceThe name of the Skype interface to route the call overstring()false
gateways.[].skype_rrDetermines whether to round-robin calls amongst all interfaces (overrides “skype_interface” setting)boolean()truefalse
gateways.[].spanThe identity of the hardware on the media serverstring()false
gateways.[].suffixA string to append to the dialed number or capture group of the matching rulestring(0..64)false
gateways.[].usernameSIP authentication usernamestring(0..32)false
gatewaysA list of gateways available for this resourcearray(object())true
grace_periodThe amount of time, in seconds, to wait before starting another resourceinteger()5false
ignore_flagsWhen set to true this resource is used if the rules/classifiers match regardless of flagsboolean()false
mediaMedia options for resources#/definitions/endpoint.mediafalse
nameA friendly name for the resourcestring(1..128)true
require_flagsWhen set to true this resource is ignored if the request does not specify outbound flagsboolean()false
rules.[]string()false
rulesA list of regular expressions of which one must match for the rule to be eligible, they can optionally contain capture groupsarray(string())[]false
rules_test.[]string()false
rules_testA list of regular expressions of which if matched denotes a test rulearray(string())[]false
weight_costA value between 0 and 100 that determines the order of resources when multiple can be usedinteger()50false

custom_sip_headers

Custom SIP headers applied to an INVITE

KeyDescriptionTypeDefaultRequiredSupport Level
^[a-zA-z0-9_\-]+$The SIP header to addstring()false

endpoint.media

Schema for endpoint media options

KeyDescriptionTypeDefaultRequiredSupport Level
audio.codecs.[]`string(‘OPUS''CELT@32000h''G7221@32000h''G7221@16000h'
audio.codecsA list of audio codecs the endpoint supports`array(string(‘OPUS''CELT@32000h''G7221@32000h''G7221@16000h'
audioThe audio media parametersobject(){}false
bypass_mediaDefault bypass media mode (The string type is deprecated, please use this as a boolean)`boolean()string(‘auto''false''true’)`
encryption.enforce_securityIs Encryption Enabled?boolean()falsefalse
encryption.methods.[]`string(‘zrtp''srtp’)`false
encryption.methodsSupported Encryption Types`array(string(‘zrtp''srtp’))`[]false
encryptionEncryption Parametersobject(){}false
fax_optionIs T.38 Supported?boolean()false
ignore_early_mediaThe option to determine if early media from the endpoint should always be ignoredboolean()false
progress_timeoutThe progress timeout to apply to the endpoint (seconds)integer()false
video.codecs.[]`string(‘H261''H263''H264''VP8’)`
video.codecsA list of video codecs the endpoint supports`array(string(‘H261''H263''H264''VP8’))`
videoThe video media parametersobject(){}false

formatters

Schema for request formatters

KeyDescriptionTypeDefaultRequiredSupport Level
^[[:alnum:]_]+$Key to match in the route request JSON`array(#/definitions/formatters.format_options)#/definitions/formatters.format_options`false

formatters.format_options

Schema for formatter options

KeyDescriptionTypeDefaultRequiredSupport Level
directionOnly apply the formatter on the relevant request direction`string(‘inbound''outbound''both’)`
match_invite_formatApplicable on fields with SIP URIs. Will format the username portion to match the invite format of the outbound request.boolean()false
prefixPrepends value against the result of a successful regex matchstring()false
regexMatches against the value, with optional capture groupstring()false
stripIf set to true, the field will be stripped from the payloadboolean()false
suffixAppends value against the result of a successful regex matchstring()false
valueReplaces the current value with the static value definedstring()false

INVITE Parameters

The INVITE parameters object defines both static and dynamic parameters that should be added to the request URI.

Static parameters are added ‘as-is’ and can be any format. However, they should follow the SIP standard for the header field format and should not include a semi-colon.

Dynamic parameters obtain the value from properties of the initiating call (requester) if present, and are ignored if not. Dynamic parameters can be defined either as a string or an object. When defined as a string the property is extracted from the requester and if found the resulting value used without modification as an INVITE parameter. When defined as an object both a tag as well as a key property must be defined. The key property is used to extract the value from the requester and the tag is appended as the INVITE parameter name. By default the INVITE parameter name and value are separated by an equals sign but this can be overridden by providing a separator property.

For example, if a resource gateway contains the following object:

           "invite_parameters": {
               "dynamic": [
                   "custom_channel_vars.pass-through",
                   {
                       "tag": "id",
                       "key": "custom_channel_vars.account_id"
                   }
               ],
               "static": [
                   "npid"
               ]
           }

and assuming the requesting call has pass-through (with value pass-through=0288) as well as account_id (with value XXXX) custom channel variables it will result in an INVITE request URI such as:

INVITE sip:+14158867900@10.26.0.88;npid;id=XXXX;pass-through=0288 SIP/2.0

Formatting the From

Some upstream carriers require the From address’ realm to be formatted. There are a couple toggles you have to control this realm. First, you will need to configure "format_from_uri":true to enable this formatting functionality. Then you have 3 options, evaluated in this order:

  1. Set "from_uri_realm":"{CUSTOM_REALM}" where {CUSTOM_REALM} is the static realm you’d like on the From
  2. Set "from_account_realm":true to use the calling account’s realm
  3. Set "realm":"{CUSTOM_REALM}" on a per-gateway basis (not on the top-level resource)

rules_test.[]

The rules_test object defines an array of regular expressions for test patterns of the given resource.

For example, if the resource handles emergency routes in North America:

  "rules_test": [
      "^\\+{0,1}(933)$"
  ],

defining 933 as a test route, will inform teletype this emergency call is a test and will be reflected as such in the notification.

Fetch

GET /v2/accounts/{ACCOUNT_ID}/resources

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources
{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
         {"enabled": true,
          "id": "{RESOURCE_ID}",
          "name": "Carrier1",
          "weight": "50"
         },
         {"enabled": true,
          "id": "{RESOURCE_ID}",
          "name": "Carrier2",
          "weight": "50"
         }
    ],
    "page_size": 2,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION_ID}",
    "status": "success"
}

Create a new resource

PUT /v2/accounts/{ACCOUNT_ID}/resources

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"name":"Carrier 3", "gateways":[]}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources
{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "emergency": false,
        "enabled": true,
        "flags": [],
        "gateways": [],
        "grace_period": 5,
        "id": "{RESOURCE_ID}",
        "media": {
            "audio": {
                "codecs": ["PCMU"]
             },
             "video": {
                 "codecs": []
             }
         },
         "name": "Carrier 3",
         "rules": [],
         "weight_cost": 50
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION_ID}",
    "status": "success"
}

Remove a resource

DELETE /v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}

curl -v -X DELETE \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}
{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "caller_id_options": {
            "type": "external"
        },
        "emergency": false,
        "enabled": true,
        "flags": [],
        "gateways": [
            {
                "channel_selection": "ascending",
                "codecs": ["PCMU", "PCMA"],
                "custom_sip_headers": {},
                "emergency": false,
                "enabled": true,
                "endpoint_type": "sip",
                "format_from_uri": false,
                "invite_format": "route",
                "password": "DrWoody",
                "prefix": "+1",
                "progress_timeout": "6",
                "realm": "carrier1.com",
                "server": "carrier1.com",
                "skype_rr": true,
                "suffix": "100",
                "username": "blazemore"
            }
        ],
        "grace_period": 5,
        "id": "{RESOURCE_ID}",
        "media": {
            "audio": {
                "codecs": ["PCMU"]
            },
            "video": {
                "codecs": []
            }
        },
        "name": "Carrier 3",
        "peer": false,
        "rules": [
            "^\\+{0,1}1{0,1}(\\d{10})$"
        ],
        "type": "local",
        "weight_cost": "50"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION_ID}",
    "status": "success"
}

Fetch a resource

GET /v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}
{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "caller_id_options": {
            "type": "external"
        },
        "emergency": false,
        "enabled": true,
        "flags": [],
        "gateways": [
            {
                "channel_selection": "ascending",
                "codecs": ["PCMU", "PCMA"],
                "custom_sip_headers": {},
                "emergency": false,
                "enabled": true,
                "endpoint_type": "sip",
                "format_from_uri": false,
                "invite_format": "route",
                "password": "DrWoody",
                "prefix": "+1",
                "progress_timeout": "6",
                "realm": "carrier1.com",
                "server": "carrier1.com",
                "skype_rr": true,
                "suffix": "100",
                "username": "blazemore"
            }
        ],
        "grace_period": 5,
        "id": "{RESOURCE_ID}",
        "media": {
            "audio": {
                "codecs": ["PCMU"]
            },
            "video": {
                "codecs": []
            }
        },
        "name": "Carrier 3",
        "peer": false,
        "rules": [
            "^\\+{0,1}1{0,1}(\\d{10})$"
        ],
        "type": "local",
        "weight_cost": "50"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION_ID}",
    "status": "success"
}

Change a resource

POST /v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{...ResourceData...}}'
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}
{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "caller_id_options": {
            "type": "external"
        },
        "emergency": false,
        "enabled": true,
        "flags": [],
        "gateways": [
            {
                "channel_selection": "ascending",
                "codecs": ["PCMU", "PCMA"],
                "custom_sip_headers": {},
                "emergency": false,
                "enabled": true,
                "endpoint_type": "sip",
                "format_from_uri": false,
                "invite_format": "route",
                "password": "DrWoody",
                "prefix": "+1",
                "progress_timeout": "6",
                "realm": "carrier1.com",
                "server": "carrier1.com",
                "skype_rr": true,
                "suffix": "100",
                "username": "blazemore"
            }
        ],
        "grace_period": 5,
        "id": "{RESOURCE_ID}",
        "media": {
            "audio": {
                "codecs": ["PCMU"]
            },
            "video": {
                "codecs": []
            }
        },
        "name": "Carrier 3",
        "peer": false,
        "rules": [
            "^\\+{0,1}1{0,1}(\\d{10})$"
        ],
        "type": "local",
        "weight_cost": "50"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION_ID}",
    "status": "success"
}

Patch a resource

PATCH /v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}

curl -v -X PATCH \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"custom_sip_headers":{"X-Reseller-ID":"a1b2c3"}}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/{RESOURCE_ID}
{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "caller_id_options": {
            "type": "external"
        },
        "custom_sip_headers": {
            "X-Reseller-ID": "a1b2c3"
        },
        "emergency": false,
        "enabled": true,
        "flags": [],
        "gateways": [
            {
                "channel_selection": "ascending",
                "codecs": ["PCMU", "PCMA"],
                "custom_sip_headers": {},
                "emergency": false,
                "enabled": true,
                "endpoint_type": "sip",
                "format_from_uri": false,
                "invite_format": "route",
                "password": "DrWoody",
                "prefix": "+1",
                "progress_timeout": "6",
                "realm": "carrier1.com",
                "server": "carrier1.com",
                "skype_rr": true,
                "suffix": "100",
                "username": "blazemore"
            }
        ],
        "grace_period": 5,
        "id": "{RESOURCE_ID}",
        "media": {
            "audio": {
                "codecs": ["PCMU"]
            },
            "video": {
                "codecs": []
            }
        },
        "name": "Carrier 3",
        "peer": false,
        "rules": [
            "^\\+{0,1}1{0,1}(\\d{10})$"
        ],
        "type": "local",
        "weight_cost": "50"
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION_ID}",
    "status": "success"
}

Fetch a listing of jobs

Do note you can use the created_from and created_to flags to change to time period queried.

The keys failures and successes represent the count of how many numbers failed and succeeded, respectively.

GET /v2/accounts/{ACCOUNT_ID}/resources/jobs

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/jobs
{
    "auth_token": "{AUTH_TOKEN}",
    "data": [
        {
            "failures": 0,
            "successes": 2,
            "id": "201408-394de70ecf6f8252",
            "status": "pending",
            "timestamp": 63575950041,
            "resource_id":"{RESOURCE_ID}"
        },
        {
            "failures": 0,
            "successes": 1,
            "id": "201408-70766ed00a24",
            "status": "pending",
            "timestamp": 63575878379,
            "resource_id":"{RESOURCE_ID}"
        }
    ]
    "page_size": 2,
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "start_key": 63573276761,
    "status": "success"
}

Create a new job

PUT /v2/accounts/{ACCOUNT_ID}/resources/jobs

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"numbers":["+12223334444", "+23334445555"], "resource_id":"{RESOURCE_ID}"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/jobs
{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "errors": {},
        "id": "201408-39512771f9d2d499",
        "resource_id":"{RESOURCE_ID}",
        "numbers": [
            "+12223334444"
        ],
        "successes": {}
     },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Fetch a job’s status

GET /v2/accounts/{ACCOUNT_ID}/resources/jobs/{JOB_ID}

curl -v -X GET \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/jobs/{JOB_ID}
{
    "auth_token": "{AUTH_TOKEN}",
    "data": {
        "resource_id": "{RESOURCE_ID}",
        "errors": {},
        "id": "201408-394de70ecf6f8252",
        "numbers": [
            "3148096310"
        ],
        "status": "pending",
        "successes": {},
        "timestamp": 63575950041
    },
    "request_id": "{REQUEST_ID}",
    "revision": "{REVISION}",
    "status": "success"
}

Create a new collection of resources

PUT /v2/accounts/{ACCOUNT_ID}/resources/collection

curl -v -X PUT \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":[{...RESOURCE...}, {...RESOURCE...}]}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/collection

Change a collection

POST /v2/accounts/{ACCOUNT_ID}/resources/collection

curl -v -X POST \
    -H "X-Auth-Token: {AUTH_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"data":{"numbers":["+12223334444", "+23334445555"], "resource_id":"{RESOURCE_ID}"}}' \
    http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/resources/collection
{
    "auth_token": "{AUTH_TOKEN}",
    "data":{
        "errors":{
            "{RESOURCE_ID}": "{ERROR_MESSAGE}"
        },
        "successes":{
            "{RESOURCE_ID}": "{RESOURCE_DOC}"
        }
    }
}