Skip to content

API & Docs

Build withTalkify API.

A focused guide from your first API key to call outcomes and webhooks.

# Your first API call — speak a line of Bengali down the phone
curl -X POST https://app.talkify.com.bd/api/v1/outbound/call \
  -H "X-API-Key: $TALKIFY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone": "8801712345678", "text": "আপনার অর্ডারটি পাঠানো হয়েছে।"}'

# Create the key in the dashboard: Developers → API Keys

Built for developers.

  • API key authentication

    Create keys in the dashboard and send one as X-API-Key or a Bearer token. Each key carries its own permission scopes.

  • Swagger / OpenAPI

    An interactive reference for every public endpoint at app.talkify.com.bd/api/docs. Try calls from the browser with your key.

  • Signed webhooks

    Get notified when calls finish or are missed, campaigns start and end, voicemails arrive, and more — each request signed so you can verify it.

  • RESTful JSON API

    Predictable REST endpoints with JSON bodies, readable error messages, and pagination and filters on the call list.

  • Rate limiting

    Requests are rate limited to keep the platform responsive for every workspace.

  • Safe retries

    Send your own externalId with a call. Sending it again returns the original call instead of dialling twice.

Quickstart

Five steps from an API key to a ringing phone. Create your key in the dashboard under Developers → API Keys, paste it in, and run.

The Interactive Calls API is included with Growth, Professional and Enterprise. Keys carry per-permission scopes — outbound:call to place calls, outbound:status to read them back.

1. Authenticate

Create a key under Developers → API Keys in the dashboard. Send it as a header on every request — either form works.

# Either header is accepted
X-API-Key: tk_live_xxxxxxxxxxxxxxxxxxxx
Authorization: Bearer tk_live_xxxxxxxxxxxxxxxxxxxx

# Base URL
https://app.talkify.com.bd/api/v1

2. Place your first call

Speak a line of text to one number. Bengali works as-is — pick a voice and it is read in Bengali.

curl -X POST https://app.talkify.com.bd/api/v1/outbound/call \
  -H "X-API-Key: $TALKIFY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "8801712345678",
    "text": "আপনার অর্ডারটি পাঠানো হয়েছে। ধন্যবাদ।",
    "externalId": "order-10482"
  }'

3. Personalise it

Send a template with variables and each recipient hears their own version. Add dtmfOptions and the call becomes a one-question survey.

curl -X POST https://app.talkify.com.bd/api/v1/outbound/call \
  -H "X-API-Key: $TALKIFY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "8801712345678",
    "template": "{{name}}, আপনার বিল {{amount}} টাকা বকেয়া আছে।",
    "variables": { "name": "রহিম", "amount": "১,২৫০" },
    "dtmfOptions": { "1": "এখনই পরিশোধ", "2": "পরে মনে করিয়ে দিন" },
    "webhookUrl": "https://your-app.com/talkify/webhook"
  }'

4. Call a list

Up to a batch per request, each with its own variables. The response returns an id per call.

curl -X POST https://app.talkify.com.bd/api/v1/outbound/bulk \
  -H "X-API-Key: $TALKIFY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "calls": [
      { "phone": "8801712345678", "text": "প্রথম বার্তা", "externalId": "a-1" },
      { "phone": "8801812345678", "text": "দ্বিতীয় বার্তা", "externalId": "a-2" }
    ]
  }'

5. Check what happened

Poll by id, or filter the list by status and your own externalId. Or skip polling and let the webhook tell you.

# One call
curl https://app.talkify.com.bd/api/v1/outbound/call/CALL_ID \
  -H "X-API-Key: $TALKIFY_API_KEY"

# Everything you sent for one order
curl "https://app.talkify.com.bd/api/v1/outbound/calls?externalId=order-10482" \
  -H "X-API-Key: $TALKIFY_API_KEY"

In your language

No package to install — Node uses the built-in fetch, PHP uses cURL. The webhook example is the one worth reading twice: the signature covers the raw bytes, so parsing the body before verifying it makes every check fail.

Place a call

No dependencies — fetch is built in from Node 18. Reusing an externalId returns the original call instead of dialling twice.

const BASE = 'https://app.talkify.com.bd/api/v1';

async function talkify(path, options = {}) {
  const res = await fetch(BASE + path, {
    ...options,
    headers: {
      'X-API-Key': process.env.TALKIFY_API_KEY,
      'Content-Type': 'application/json',
      ...options.headers,
    },
  });

  const body = await res.json();
  if (!res.ok) {
    // A validation failure returns message as an array of strings;
    // an auth failure returns a single string.
    const detail = Array.isArray(body.message) ? body.message.join('; ') : body.message;
    throw new Error(`Talkify ${res.status}: ${detail}`);
  }
  return body;
}

const { data } = await talkify('/outbound/call', {
  method: 'POST',
  body: JSON.stringify({
    phone: '8801712345678',
    text: 'আপনার অর্ডারটি পাঠানো হয়েছে। ধন্যবাদ।',
    externalId: 'order-10482',      // your id — send it again and you get the
    webhookUrl: 'https://your-app.com/talkify/webhook',
  }),                                // same call back, never a second dial
});

console.log(data.callId, data.status);   // '…-…' 'QUEUED'

Check what happened

Poll one call by id, or fetch everything you sent under one of your own ids. Note the list endpoint has no success wrapper.

// One call
const { data: call } = await talkify('/outbound/call/' + callId);
console.log(call.status, call.dtmfPressed, call.duration);

// Everything for one order — this endpoint returns { data, pagination }
const { data: calls, pagination } = await talkify(
  '/outbound/calls?externalId=order-10482'
);
console.log(`${calls.length} of ${pagination.total}`);

Verify a webhook

Sign-and-compare on the RAW body. Parsing the JSON first and re-serialising it changes the bytes and every signature fails.

import express from 'express';
import crypto from 'node:crypto';

const app = express();

// express.raw, not express.json — the signature covers the exact bytes sent.
app.post('/talkify/webhook',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const expected = 'sha256=' + crypto
      .createHmac('sha256', process.env.TALKIFY_WEBHOOK_SECRET)
      .update(req.body)                       // the Buffer, untouched
      .digest('hex');

    const got = req.get('X-Talkify-Signature') || '';
    // Constant-time: a plain === leaks the answer one byte at a time.
    const ok = got.length === expected.length &&
      crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected));

    if (!ok) return res.status(401).send('bad signature');

    const event = JSON.parse(req.body.toString());
    console.log(event.externalId, event.status, event.dtmfLabel);

    res.sendStatus(200);   // anything else and Talkify retries: 2s, 4s, 8s
  });

API endpoints

Everything an API key opens, in full.

  • POST/api/v1/outbound/call

    Place one call — play audio, speak text, or hand it to an AI agent

  • POST/api/v1/outbound/bulk

    Place a batch of calls from a JSON list of numbers

  • POST/api/v1/outbound/bulk-file

    Place a batch of calls from an uploaded CSV

  • GET/api/v1/outbound/call/:id

    Status, duration and outcome of one call

  • GET/api/v1/outbound/calls

    List calls placed through the API, with filters

Webhook events

Subscribe to these events and react to platform activity.

  • call.completed

    A call finished — direction, duration, billsec and outcome

  • call.missed

    An inbound call was not answered

  • campaign.started

    A broadcasting campaign began dialling

  • campaign.completed

    A broadcasting campaign finished

  • voicemail.new

    A voicemail was recorded and stored

  • contact.created

    A contact was added, by hand or by import

  • payment.received

    An invoice was settled

  • subscription.expiring

    A subscription period is about to end