Custom Telephony Integration in CallAIder: why it matters, how to implement the API in detail, and how to connect it on the platform

A custom integration is the right approach when you need to connect your own telephony stack or internal API to CallAIder instead of relying on a standard connector.

This article follows the practical rollout order:

  1. First, why this integration matters.
  2. Then, full API implementation details.
  3. Finally, connection steps inside the platform UI.

If you are a CTO, tech lead, backend engineer, or integration specialist, you can treat this guide as a production launch checklist.

Why this integration matters

Many teams see integration as “just importing calls.” In reality, custom integration provides stronger control, cleaner analytics, and much better operational predictability.

1) One source of truth for call analytics

You define what a valid call looks like, which attributes are sent, and how metadata is named. This eliminates mismatch between telephony/CRM data and platform analytics.

Benefits:

  • fewer conflicts about numbers;
  • better auditability;
  • stable data quality across teams.

2) Rich business context per call

Through properties, you can pass dimensions often unavailable in standard connectors:

  • department or queue;
  • customer segment;
  • customer city/region;
  • internal status (for example, issue resolved);
  • internal quality score or priority.

This allows meaningful filtering and segmentation instead of raw volume views.

3) Filtering aligned with real workflows

With propertyFilters, you can import only relevant conversations:

  • support calls for VIP clients;
  • inbound calls from selected locations;
  • calls in a target quality range.

This is critical when teams have different KPIs and must analyze different subsets.

4) Security and controlled access

X-Secret-Key provides a strict API access layer. You can combine it with your own controls: IP allowlist, rate limits, request logs, and secret rotation.

5) Scalability without manual work

Once integration is correctly implemented, manual exports/imports disappear. Calls flow automatically, and analytics runs continuously.

6) Better fit for non-standard architecture

Custom API is often the only reliable option when you have:

  • multiple telephony providers;
  • custom identity/normalization logic;
  • CRM/ERP-enriched call metadata;
  • strict recording-access requirements.

When to choose Custom Telephony

Use Custom Telephony when:

  • your provider is not available among built-in connectors;
  • you need additional call metadata for filtering;
  • contract-level control and data quality are mandatory;
  • departments require different import criteria;
  • segmentation depends on internal business fields.

End-to-end flow

At a high level:

  1. You create a Custom API integration in CallAIder.
  2. CallAIder checks /health.
  3. CallAIder reads operators from /contacts.
  4. CallAIder requests calls from /calls using time range and optional filters.
  5. CallAIder reads recordings via /calls/:id/recording.
  6. If /properties/schema is implemented, team setup shows an extra properties filter step.

What to prepare before implementation

Before coding, align internally:

  • public API base URL;
  • secret key policy for X-Secret-Key;
  • field mapping from telephony source to CallAIder contract;
  • naming convention for properties;
  • recording URL policy (permanent or temporary links);
  • logging and monitoring policy.

API implementation details

This is the core technical contract your backend must support.

Global requirements

  • JSON request/response payloads.
  • X-Secret-Key validation on all integration endpoints.
  • predictable status codes (200, 401, 404, 500).
  • unified error shape.
  • only completed calls in /calls.

Recommended error response:

{
  "status": "error",
  "message": "Error details"
}

Authentication with X-Secret-Key

Every request from CallAIder includes:

X-Secret-Key: <your_secret_key>

Your API must:

  • validate key before business logic;
  • return 401 Unauthorized if key is missing/invalid;
  • log rejected requests safely.

Minimal middleware:

function auth(req, res, next) {
  if (req.headers['x-secret-key'] !== process.env.CALLAIDER_SECRET) {
    return res.status(401).json({ status: 'error', message: 'Unauthorized' });
  }
  next();
}

Endpoint 1: GET /health

Purpose: connectivity check used during integration setup.

Request:

GET {your_api_url}/health
X-Secret-Key: <secret>

Response:

{ "status": "success" }

Endpoint 2: GET /contacts

Purpose: return available operators.

Request:

GET {your_api_url}/contacts
X-Secret-Key: <secret>

Response example:

{
  "contacts": [
    {
      "id": "101",
      "phone": "+380971234567",
      "name": "John Carter",
      "department": "Sales"
    }
  ]
}

Fields:

  • id (string, required): stable unique operator ID;
  • phone (string, required): operator phone number;
  • name (string, required): operator name;
  • department (string, optional): department name.

Endpoint 3: POST /calls

Purpose: return completed calls for a time range.

Request:

POST {your_api_url}/calls
Content-Type: application/json
X-Secret-Key: <secret>

Request body example:

{
  "startTime": 1709500000,
  "stopTime": 1709586400,
  "direction": "incoming",
  "operators": ["101", "102"],
  "propertyFilters": {
    "Department": ["sales", "support"],
    "Quality score": {
      "mode": "all",
      "conditions": [
        { "operator": "gt", "value": 2 },
        { "operator": "lt", "value": 8 }
      ]
    }
  }
}

Request fields:

  • startTime (number, required): period start (Unix seconds);
  • stopTime (number, required): period end (Unix seconds);
  • direction (string, optional): incoming or outgoing;
  • operators (string[], optional): filter by operator IDs;
  • propertyFilters (object, optional): additional property filters.

Supported propertyFilters formats

  1. Exact value:
{ "Client city": "Kyiv" }
  1. One-of list:
{ "Department": ["sales", "support"] }
  1. Numeric conditions with all or any mode:
{
  "Quality score": {
    "mode": "any",
    "conditions": [
      { "operator": "gt", "value": 10 },
      { "operator": "lt", "value": 5 }
    ]
  }
}

Numeric operators:

  • eq
  • ne
  • gt
  • gte
  • lt
  • lte

If mode is omitted, default is all.

Critical behavior: backend propertyFilters implementation is optional

There are two valid modes:

  1. Recommended mode: your API applies propertyFilters directly.
  2. Fallback mode: your API ignores propertyFilters and returns calls filtered by startTime, stopTime, direction, operators; CallAIder then performs post-filtering on platform side using calls[].properties.

Important: backend-side implementation of propertyFilters is NOT mandatory. Integration still works if property filtering is performed by CallAIder on platform side.

For fallback mode to work correctly:

  • return properties for calls where additional filtering is needed;
  • if /properties/schema exists, properties keys must match schema keys;
  • if /properties/schema is absent, keys may be arbitrary, but values should be primitive: string, number, boolean.

POST /calls response example

{
  "calls": [
    {
      "id": "call-abc-123",
      "clientNumber": "+380501234567",
      "clientName": "Client One",
      "direction": "incoming",
      "duration": 185,
      "startTime": 1709586000,
      "endTime": 1709586185,
      "operatorId": "101",
      "operatorName": "John Carter",
      "operatorNumber": "7545",
      "whoHungUp": "client",
      "properties": {
        "Department": "support",
        "Client city": "Lviv",
        "Issue resolved": true,
        "Quality score": 72
      }
    }
  ]
}

Call fields:

  • id (string, required): unique call ID;
  • clientNumber (string, optional): client phone;
  • clientName (string, optional): client name;
  • direction (string, required): incoming or outgoing;
  • duration (number, required): call duration in seconds;
  • startTime (number, required): call start timestamp;
  • endTime (number, optional): call end timestamp;
  • operatorId (string, required): operator ID;
  • operatorName (string, required): operator name;
  • operatorNumber (string, required): operator number;
  • whoHungUp (string, optional): operator, client, or system;
  • properties (object, optional): additional call properties.

Important notes:

  • return completed calls only;
  • do not return active/missed calls;
  • properties is optional for basic import;
  • properties is required for additional filtering scenarios.

Endpoint 4: GET /properties/schema (optional; used for flexible import setup)

This endpoint is optional and is only needed for flexible dialog import configuration during team setup.

It is not required for base integration operation.

Request:

GET {your_api_url}/properties/schema
X-Secret-Key: <secret>

Response example:

{
  "properties": [
    { "key": "Department", "type": "enum", "values": ["sales", "support", "retention"] },
    { "key": "Client segment", "type": "enum", "values": ["standard", "vip"] },
    { "key": "Client city", "type": "string" },
    { "key": "Issue resolved", "type": "boolean" },
    { "key": "Quality score", "type": "number" }
  ]
}

Schema fields:

  • key (string, required): property key;
  • type (string, required): string, number, boolean, or enum;
  • values (array, optional): allowed values.

If this endpoint is not implemented:

  • return { "properties": [] }, or
  • return 404 Not Found.

Integration still works. The platform simply skips the advanced properties configuration step.

Schema vs properties:

  • /properties/schema defines available property filters in team setup;
  • calls[].properties carries actual values per call;
  • calls[].properties is used for additional filtering during import and in dialogs filters.

Endpoint 5: GET /calls/:id/recording

Purpose: return recording URL for a call.

Request:

GET {your_api_url}/calls/123/recording
X-Secret-Key: <secret>

Response:

{
  "url": "https://your-storage.com/recordings/call-abc-123.mp3",
  "filename": "call_recording_call-abc-123.mp3",
  "mimeType": "audio/mpeg"
}

Fields:

  • url (required)
  • filename (optional)
  • mimeType (optional)

If recording is unavailable, return 404 Not Found.

Response code matrix

  • 200 success
  • 401 unauthorized
  • 404 not found
  • 500 internal error

Production recommendations

  • deduplicate by call.id;
  • add request/response logs with correlation IDs;
  • enforce timeout and retry strategy;
  • monitor endpoint error rates;
  • alert on sudden import-volume drops.

Connect integration in CallAIder (after API is ready)

Step 1. Create Custom API connection

  1. Open Integrations.
  2. Find Custom API card.
  3. Click Connect.

Custom API card in Integrations

Fill in:

  • Connection name
  • API URL
  • Secret Key

Custom API connection modal

After save, CallAIder validates connectivity via /health.

Step 2. Create a team for this integration

Then create a team that defines operators, analytics rule, and optional property-based constraints.

Detailed wizard walkthrough:

Step 3. Configure properties in team wizard

If /properties/schema is implemented, the wizard shows the Properties step for flexible import filtering.

Properties step in team setup

Additional filtering in Dialogs after import

After calls are imported:

  1. Open Dialogs.
  2. Click the filters button.

How to open dialog filters

The panel includes:

  • standard filters (direction, operator, client, duration);
  • additional Call properties filters based on properties.

Dialogs filters panel with additional properties

Troubleshooting

401 Unauthorized

Check secret key value in both platform settings and backend validation.

404 on /health, /contacts, or /calls/:id/recording

Check base URL, route registration, and external accessibility.

Empty import from /calls

Check timestamp units (seconds), direction values, operator mapping, and filter strictness.

Properties missing in UI

Check /properties/schema availability and exact key match with calls[].properties.

Check URL reachability, TTL, and MIME type.

Final launch checklist

  • /health, /contacts, /calls, /calls/:id/recording implemented.
  • X-Secret-Key validated on all endpoints.
  • /calls returns completed calls only.
  • properties is stable and type-safe.
  • /properties/schema implemented when flexible import filters are needed.
  • schema keys and payload keys are aligned.
  • team import is validated in Dialogs.
  • additional filtering works both during import and in dialogs panel.

If all checks pass, the integration is production-ready and analytics is reliable.