Guide

Integrate the call center into your software

Click to call from your software, show the caller when a call comes in, keep call history, recordings and summaries. About 30 minutes to a working version.

  1. 1Create an API key with call permissions

    In the app: Settings › Apps & API › API keys → Call group → enable call.partner.originate (dial on behalf), call.calls.view, call.recordings.view. Restrict Call agents this key may act for if the key serves one team only.

    API keys →

  2. 2Click to call from your software

    The agent rings first; once they answer, the customer is dialed. Pick the agent with exactly one of agentId, agentEmail, extension. The customer number can be written any way, results are always E.164. Send an Idempotency-Key so a double click never places two calls. Put your own customer id in contactRef.

    curl
    curl -X POST "https://api.nextx.vn/api/v1/call/partner/calls" \
      -H "X-Api-Key: <API_KEY>" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: 6f1c2b0e-0a7d-4e4f-9b1a-2f3c4d5e6f70" \
      -d '{
        "agentEmail": "agent@your-company.com",
        "to": "0901234567",
        "contactRef": "CUS-00123",
        "metadata": { "ticket": "T-889" }
      }'
    # 201 → { "callId": "…", "status": "initiated", "to": "+84901234567", "agent": { … } }

    API reference →Error codes →

  3. 3Subscribe to Call webhooks and verify signatures

    Settings › Apps & API › Webhooks → Call → paste the URL → Verify and save → enable the events you need. Signatures follow Standard Webhooks, verify them with the ready-made library for Node, Python, Go, PHP, Java or C#. Return 2xx within 5 seconds and queue heavy work.

    Node.js (Express)
    import { Webhook } from "standardwebhooks";   // npm i standardwebhooks
    const wh = new Webhook(process.env.NEXTX_WEBHOOK_SECRET);  // "whsec_…"
    
    app.post("/hooks/call", express.raw({ type: "application/json" }), (req, res) => {
      const raw = req.body.toString("utf8");
      const event = JSON.parse(raw);
      // the URL verification ping uses a one-time secret: answer it BEFORE checking the signature
      if (event.type === "endpoint.verification") return res.json({ challenge: event.challenge });
    
      try {
        wh.verify(raw, req.headers);           // webhook-id / webhook-timestamp / webhook-signature
      } catch {
        return res.sendStatus(401);
      }
      queue.add(event);                        // handle it in the background
      res.sendStatus(200);
    });

    Webhooks →Verify signatures →

  4. 4Incoming call popup and call history

    call.ringing arrives while the customer is calling: look them up by from and show the popup. call.completed / call.missed carry the final status (completed, busy, no-answer, failed, canceled, abandoned) and a Q.850 hangupCause. Webhooks may arrive more than once or out of order: de-duplicate by id, re-order by sequence.

    worker
    async function handle(event) {
      if (await db.processed.exists(event.id)) return;      // delivered at least once
      const call = event.data.object;                        // callId, status, from, to (E.164), agent, sequence…
      if (call.sequence < (await db.callSequence(call.callId))) return;   // a later stage already arrived
    
      switch (event.type) {
        case "call.ringing":   showPopup(call.agent?.email, await customers.findByPhone(call.from)); break;
        case "call.answered":  markInProgress(call.contactRef, call.agent); break;
        case "call.completed":
        case "call.missed":    saveCallLog(call.contactRef, call.status, call.talkSec, call.hangupCause); break;
      }
      await db.processed.insert(event.id);
    }

    call.ringing →call.completed →

  5. 5Recordings and summaries

    On call.recording_ready, call the API for a download link valid for 15 minutes (webhooks never contain the link). On call.summary_ready, read the call summary.

    curl
    # on call.recording_ready — the link is valid for 15 minutes, ask again when it expires
    curl "https://api.nextx.vn/api/v1/call/partner/calls/<callId>/recording" \
      -H "X-Api-Key: <API_KEY>"
    # → { "url": "https://…", "expiresAt": "…", "contentType": "audio/wav" }
    
    # on call.summary_ready
    curl "https://api.nextx.vn/api/v1/call/calls/<callId>/summary" -H "X-Api-Key: <API_KEY>"
  6. 6Try it on sandbox

    Use to = sandbox:echo: the agent rings, after answering they hear a test tone for about 10 seconds, then the call ends without reaching any carrier. You receive the full webhook sequence like a real call. Each key may place up to 60 calls per minute.

    curl (sandbox)
    curl -X POST "<SANDBOX_API_URL>/api/v1/call/partner/calls" \
      -H "X-Api-Key: <SANDBOX_API_KEY>" -H "Content-Type: application/json" \
      -d '{ "extension": "1021", "to": "sandbox:echo", "contactRef": "TEST-1" }'

    Sandbox →Rate limit →