Skip to main content

Click-to-Call

Click-to-call enables your application to initiate phone calls on behalf of users. When triggered, the system first calls the user's registered devices, then connects them to the destination number.

Overview​

Click-to-call allows you to:

  • Add "Call" buttons next to customer phone numbers in your application
  • Enable one-click dialing from CRM records, support tickets, or contact lists
  • Track call activity through the call logs API

How It Works​

When you initiate a click-to-call:

  1. First leg: All of the user's registered devices (desk phones, softphones) ring simultaneously
  2. Answer: The user picks up any of their devices
  3. Second leg: The system dials the destination number
  4. Bridge: Both calls are connected together

This "call-first" approach ensures the user is ready before the customer's phone rings, providing a professional experience.

Initiating a Call​

Using the API​

Make a POST request to /v1/calls with the user ID and dial string:

Bash
curl -X POST https://api.dialstack.ai/v1/calls \
-H "Authorization: Bearer sk_live_YOUR_SECRET_KEY" \
-H "DialStack-Account: acct_01h2xcejqtf2nbrexx3vqjhp41" \
-H "Content-Type: application/json" \
-d '{
"user": "user_01h2xcejqtf2nbrexx3vqjhp42",
"dial_string": "+15551234567"
}'

The API returns 202 Accepted immediately. The call is processed asynchronously.

Using the SDK​

JavaScript
import { loadDialstackAndInitialize } from '@dialstack/sdk-js';

const dialstack = await loadDialstackAndInitialize({
publishableKey: 'pk_test_...',
fetchClientSecret: async () => {
const response = await fetch('/api/dialstack/session');
const { client_secret, expires_at } = await response.json();
return { clientSecret: client_secret, expiresAt: expires_at };
},
});

// Initiate a call
await dialstack.calls.create({
userId: 'user_01h2xcejqtf2nbrexx3vqjhp42',
dialString: '+15551234567',
});

Request Parameters​

FieldTypeRequiredDescription
userstringYesThe user whose devices will ring first. user_id is accepted as a deprecated alias
dial_stringstringYesThe number to dial (see formats below)

Dial String Formats​

The dial_string parameter accepts several formats:

FormatExampleDescription
E.164+15551234567International format (recommended)
Local5551234567Normalized based on account region
Extension1001Internal extension dialing
Emergency911Emergency services

Server-Side Implementation​

Here's how to implement a click-to-call endpoint in your backend:

Node.js / Express​

JavaScript
import express from 'express';

const app = express();
app.use(express.json());

// Your platform's API key
const DIALSTACK_API_KEY = process.env.DIALSTACK_API_KEY;

app.post('/api/call', async (req, res) => {
const { userId, phoneNumber, accountId } = req.body;

try {
const response = await fetch('https://api.dialstack.ai/v1/calls', {
method: 'POST',
headers: {
Authorization: `Bearer ${DIALSTACK_API_KEY}`,
'DialStack-Account': accountId,
'Content-Type': 'application/json',
},
body: JSON.stringify({
user: userId,
dial_string: phoneNumber,
}),
});

if (response.status === 202) {
res.json({ success: true, message: 'Call initiated' });
} else {
const error = await response.json();
res.status(response.status).json(error);
}
} catch (error) {
res.status(500).json({ error: 'Failed to initiate call' });
}
});

Python / Flask​

Python
from flask import Flask, request, jsonify
import requests
import os

app = Flask(__name__)

DIALSTACK_API_KEY = os.environ.get('DIALSTACK_API_KEY')

@app.route('/api/call', methods=['POST'])
def initiate_call():
data = request.json

response = requests.post(
'https://api.dialstack.ai/v1/calls',
headers={
'Authorization': f'Bearer {DIALSTACK_API_KEY}',
'DialStack-Account': data['account_id'],
'Content-Type': 'application/json'
},
json={
'user': data['user_id'],
'dial_string': data['phone_number']
}
)

if response.status_code == 202:
return jsonify({'success': True, 'message': 'Call initiated'})
else:
return jsonify(response.json()), response.status_code

Frontend Integration​

Add click-to-call buttons to your UI:

JavaScript
function CustomerCard({ customer, currentUser, accountId }) {
const [calling, setCalling] = useState(false);

async function handleCall() {
setCalling(true);
try {
const response = await fetch('/api/call', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
userId: currentUser.dialstackUserId,
phoneNumber: customer.phone,
accountId: accountId,
}),
});

if (response.ok) {
// Show "Calling..." indicator
showNotification('Calling ' + customer.name);
}
} finally {
setCalling(false);
}
}

return (
<div className="customer-card">
<h3>{customer.name}</h3>
<p>{customer.phone}</p>
<button onClick={handleCall} disabled={calling}>
{calling ? 'Calling...' : 'Call'}
</button>
</div>
);
}

Error Handling​

StatusErrorDescription
400user_id is requiredMissing user in request
400dial_string is requiredMissing dial_string in request
400Invalid user_id formatuser is not a valid user ID
401UnauthorizedInvalid or missing API key/session
404User not foundUser doesn't exist in the account

Best Practices​

DO​

  • Validate phone numbers before sending to the API
  • Show a loading state while the call is being initiated
  • Use E.164 format for phone numbers when possible
  • Log call attempts for troubleshooting

DON'T​

  • Don't expose your platform API key in client-side code
  • Don't allow arbitrary dial strings from user input without validation
  • Don't retry failed calls automatically (let the user decide)

Monitoring Calls​

After initiating a call, you can track its progress: