Skip to main content

Voice API

Voice over Asterisk/FreePBX

Connect your own Asterisk or FreePBX through SIP trunking and control call routing yourself.

These features are experimental. If you face any issues, please contact support.

SIP Trunking is intended for organizations that already operate their own Asterisk, FreePBX, or another SIP-compatible PBX. You provide the PBX destination and credentials, then manage extension provisioning, dial plans, queues, and routing on your own system.

All requests in this document use:

https://a.loopmessage.com/api/v1/voice/

The API key must be sent in the Authorization header. Keep the returned SIP passwords private and do not commit them to source control.

Create a custom SIP trunk

Create one custom credential set for your PBX. Only one custom credential set is allowed per organization.

POST https://a.loopmessage.com/api/v1/voice/voip/credentials/

Request body

NameTypeDescription
nameStringOptional display name, up to 128 characters.
type*StringMust be custom.
custom_destination*StringSIP destination used for calls sent to your PBX, for example sip:[email protected].
inboundObjectOptional settings for calls sent from your PBX to the service.
inbound.numbersArray or StringOptional allowed dialed numbers. Values may be phone numbers, SIP URIs, or *. A comma-separated string is also accepted.
inbound.allowed_addressesArray or StringOptional source IP addresses or CIDR networks allowed to send calls. A comma-separated string is also accepted.
outbound*ObjectSettings used when the service sends calls to your PBX.
outbound.address*StringPBX host and port in host:port format, for example pbx.example.com:5061.
outbound.transportStringUDP, TCP, or TLS. Default is UDP.
outbound.numbersArray or StringOptional numbers that may be sent through this trunk. A comma-separated string is also accepted.
inbound_enabledBooleanOptional. Enable inbound calls for this credential.
outbound_enabledBooleanOptional. Enable outbound calls for this credential.
{
  "name": "Customer PBX",
  "type": "custom",
  "custom_destination": "sip:[email protected]",
  "inbound": {
    "numbers": ["+13235550100"],
    "allowed_addresses": ["203.0.113.0/24"]
  },
  "outbound": {
    "address": "pbx.example.com:5061",
    "transport": "TLS",
    "numbers": ["*"]
  },
  "inbound_enabled": true,
  "outbound_enabled": true
}

allowed_addresses should contain the public IP addresses or networks from which your PBX sends SIP traffic. Use the narrowest range possible.

Response

Response {.tabset .tabset-fade}

tab 200: Trunk created

{
  "success": true,
  "id": "credential-uuid",
  "name": "Customer PBX",
  "type": "custom",
  "enabled": false,
  "inbound_enabled": true,
  "outbound_enabled": true,
  "inbound": {
    "trunk_id": "inbound-trunk-id",
    "username": "inbound_username",
    "password": "inbound_password",
    "numbers": ["+13235550100"],
    "allowed_addresses": ["203.0.113.0/24"]
  },
  "outbound": {
    "trunk_id": "outbound-trunk-id",
    "username": "outbound_username",
    "password": "outbound_password",
    "address": "pbx.example.com:5061",
    "transport": "SIP_TRANSPORT_TLS",
    "numbers": ["*"]
  },
  "custom_destination": "sip:[email protected]",
  "pbx": {
    "inbound_trunk_id": "",
    "outbound_trunk_id": "",
    "outbound_route_id": "",
    "ring_group_number": "",
    "inbound_route_did": "",
    "outbound_dial_prefix": ""
  },
  "create_date": "2026-01-01T12:00:00Z"
}

The inbound.username and inbound.password values authenticate calls sent from your PBX. The outbound.username and outbound.password values authenticate calls sent to your PBX. Configure each direction with the matching credentials.

tab 400: Invalid request

{
  "success": false,
  "code": 120,
  "message": "One or more required parameters for the request are missing"
}

List custom trunks

GET https://a.loopmessage.com/api/v1/voice/voip/credentials/

This request has no body. Send the API key in the Authorization header.

curl --request GET \
  --url https://a.loopmessage.com/api/v1/voice/voip/credentials/ \
  --header 'Authorization: Bearer YOUR_API_KEY'
{
  "success": true,
  "items": [
    {
      "id": "credential-uuid",
      "name": "Customer PBX",
      "type": "custom",
      "enabled": true,
      "inbound_enabled": true,
      "outbound_enabled": true,
      "inbound": {
        "trunk_id": "inbound-trunk-id",
        "username": "inbound_username",
        "password": "inbound_password",
        "numbers": ["+13235550100"],
        "allowed_addresses": ["203.0.113.0/24"]
      },
      "outbound": {
        "trunk_id": "outbound-trunk-id",
        "username": "outbound_username",
        "password": "outbound_password",
        "address": "pbx.example.com:5061",
        "transport": "SIP_TRANSPORT_TLS",
        "numbers": ["*"]
      },
      "custom_destination": "sip:[email protected]",
      "pbx": {
        "inbound_trunk_id": "",
        "outbound_trunk_id": "",
        "outbound_route_id": "",
        "ring_group_number": "",
        "inbound_route_did": "",
        "outbound_dial_prefix": ""
      },
      "create_date": "2026-01-01T12:00:00Z"
    }
  ],
  "count": 1
}

Get a trunk

GET https://a.loopmessage.com/api/v1/voice/voip/credentials/{credential_id}/

Replace {credential_id} with the id returned by the create or list request. This request has no body.

curl --request GET \
  --url https://a.loopmessage.com/api/v1/voice/voip/credentials/credential-uuid/ \
  --header 'Authorization: Bearer YOUR_API_KEY'

The 200 response is one credential object with the same shape as the create response:

{
  "success": true,
  "id": "credential-uuid",
  "name": "Customer PBX",
  "type": "custom",
  "enabled": true,
  "inbound_enabled": true,
  "outbound_enabled": true,
  "inbound": {
    "trunk_id": "inbound-trunk-id",
    "username": "inbound_username",
    "password": "inbound_password",
    "numbers": ["+13235550100"],
    "allowed_addresses": ["203.0.113.0/24"]
  },
  "outbound": {
    "trunk_id": "outbound-trunk-id",
    "username": "outbound_username",
    "password": "outbound_password",
    "address": "pbx.example.com:5061",
    "transport": "SIP_TRANSPORT_TLS",
    "numbers": ["*"]
  },
  "custom_destination": "sip:[email protected]",
  "pbx": {
    "inbound_trunk_id": "",
    "outbound_trunk_id": "",
    "outbound_route_id": "",
    "ring_group_number": "",
    "inbound_route_did": "",
    "outbound_dial_prefix": ""
  },
  "create_date": "2026-01-01T12:00:00Z"
}

Update a custom trunk

PATCH https://a.loopmessage.com/api/v1/voice/voip/credentials/{credential_id}/

Only custom credentials can be changed after provisioning. You may update name, custom_destination, inbound, outbound, inbound_enabled, and outbound_enabled. Nested inbound and outbound objects follow the same rules as creation; omitted fields keep their current values.

{
  "outbound": {
    "address": "new-pbx.example.com:5061",
    "transport": "TLS"
  },
  "outbound_enabled": true
}

Activate or deactivate a trunk

POST https://a.loopmessage.com/api/v1/voice/voip/credentials/{credential_id}/activate/

POST https://a.loopmessage.com/api/v1/voice/voip/credentials/{credential_id}/deactivate/

Both requests have no body. Only the URL action changes.

Activate

curl --request POST \
  --url https://a.loopmessage.com/api/v1/voice/voip/credentials/credential-uuid/activate/ \
  --header 'Authorization: Bearer YOUR_API_KEY'
{
  "success": true,
  "id": "credential-uuid",
  "name": "Customer PBX",
  "type": "custom",
  "enabled": true,
  "inbound_enabled": true,
  "outbound_enabled": true,
  "inbound": {
    "trunk_id": "inbound-trunk-id",
    "username": "inbound_username",
    "password": "inbound_password",
    "numbers": ["+13235550100"],
    "allowed_addresses": ["203.0.113.0/24"]
  },
  "outbound": {
    "trunk_id": "outbound-trunk-id",
    "username": "outbound_username",
    "password": "outbound_password",
    "address": "pbx.example.com:5061",
    "transport": "SIP_TRANSPORT_TLS",
    "numbers": ["*"]
  },
  "custom_destination": "sip:[email protected]",
  "pbx": {
    "inbound_trunk_id": "",
    "outbound_trunk_id": "",
    "outbound_route_id": "",
    "ring_group_number": "",
    "inbound_route_did": "",
    "outbound_dial_prefix": ""
  },
  "create_date": "2026-01-01T12:00:00Z"
}

Deactivate

curl --request POST \
  --url https://a.loopmessage.com/api/v1/voice/voip/credentials/credential-uuid/deactivate/ \
  --header 'Authorization: Bearer YOUR_API_KEY'

The response has the same object shape, with enabled set to false:

{
  "success": true,
  "id": "credential-uuid",
  "name": "Customer PBX",
  "type": "custom",
  "enabled": false,
  "inbound_enabled": true,
  "outbound_enabled": true,
  "inbound": {
    "trunk_id": "inbound-trunk-id",
    "username": "inbound_username",
    "password": "inbound_password",
    "numbers": ["+13235550100"],
    "allowed_addresses": ["203.0.113.0/24"]
  },
  "outbound": {
    "trunk_id": "outbound-trunk-id",
    "username": "outbound_username",
    "password": "outbound_password",
    "address": "pbx.example.com:5061",
    "transport": "SIP_TRANSPORT_TLS",
    "numbers": ["*"]
  },
  "custom_destination": "sip:[email protected]",
  "pbx": {
    "inbound_trunk_id": "",
    "outbound_trunk_id": "",
    "outbound_route_id": "",
    "ring_group_number": "",
    "inbound_route_did": "",
    "outbound_dial_prefix": ""
  },
  "create_date": "2026-01-01T12:00:00Z"
}

Only one credential set can be active for an organization at a time. Activating a credential disables another active credential set.

The response uses the same credential object shape as the create response, with enabled set to the new value.

Configure your PBX

Use the values from the create response to configure two SIP directions:

DirectionPBX configuration
Calls from your PBXSend SIP to the service using the inbound credentials and an allowed source address.
Calls to your PBXListen on the configured outbound.address, accept the selected transport, and authenticate with the outbound credentials.

For TLS, use a certificate valid for the hostname in your PBX address and open the selected SIP and RTP ports in your firewall. Do not expose management interfaces to the public internet.

Dialing from your PBX

Dial the recipient in international format, for example +13235550123. To select a sender explicitly, dial:

*{sender_number}*{recipient_number}

Example:

*+13235550100*+13235550123

When no sender is supplied, the service selects an eligible sender based on the organization’s conversation and sender settings.

Calling your PBX from the API

When an outbound voice call is created for a custom trunk, the service sends the SIP call to outbound.address and uses custom_destination as the destination inside your PBX. Route that destination to the extension, queue, or ring group that should answer the call.

Delete a trunk

There is currently no delete route in the public voice URL configuration. Deactivate a trunk when it should no longer receive or place calls, and contact support if the credential must be removed permanently.