Call Detail Record Service API

Application Programming Interface (API)

The API for the Call Detail Record (CDR) Service supports a single POST operation. This API can be accessed using the wildcard address (0.0.0.0/::) port 62000 via HTTP.

Endpoints

/{application}

POST - Create a CDR for the specified application. A CDR ID will be automatically generated.

/{application}/{cdr_id}

POST - Create a CDR for the specified application. The cdr_id value, if present, must be identical to the cdr_id in the target URL. If a cdr_id value is not present, the target URL will be used.


The POST body must be of Content-Type application/json. The following is the format of the POST response body:

{
        "data": {
                "cdr_id": "[cdr_id]"
        }
}

Note

It is not guaranteed the CDR is written to persistent storage when the response is returned.


/{application}/rotate

POST - Trigger immediate partitioning of the CDR database file while not interrupting scheduled rotation times.

The body of this request is ignored. The response will be the single text string Rotation request submitted after successful completion of the rotation.

Running the global xpressworkx-cdr-partition command will submit a rotation request for the passed application. It can be as simple as:

#!/bin/bash
exec curl -X POST http://localhost:62000/$1/rotate

Informational data can be provided with the use of a GET request of the following format:

{
        "data": {
                "status": {
                        "events_written_total": "[]",
                        "events_written_error_total": "[]",
                        "http_response_total_seconds": "[]",
                        "start_timestamp": "[]",
                        "<application>": {
                                "events_written_total": "[]",
                                "events_written_error_total": "[]",
                                "events_written_current": "[]",
                                "events_written_total_seconds": "[]",
                                "current_db_timestamp": "[]",
                                "backend_queue_length": "[]",
                        }
                },
        }
        "jsonapi": {
                "contact": "support@imsworkx.com",
                "name": "XpressWorkX Platform CDRService",
                "version": "<platform version>",
                "api_version": 1
        }
}
events_written_total

The number of events written since the CDR Service started.

events_written_error_total

The number of events that failed to write since the CDR Service started.

http_response_total_seconds

The total amount of time spent handling the HTTP submit of events.

start_timestamp

The start time of the CDR Service.

events_written_total (application-specific)

The number of events written for this application since the CDR Service started.

events_written_error_total (application-specific)

The number of events that failed to write for this application since the CDR Service started.

events_written_current

The number of events written for the current sqlite database (if the default sqlite backend is used).

events_written_total_seconds

The number of seconds accumulated actively writing to the database.

current_db_timestamp

The creation time of the current sqlite database (if the default sqlite backend is used).

backend_queue_length

The total number of events waiting for the backend to accept them.


Errors

Errors in handling an HTTP request will produce a generic 500 response with a JSON payload like the following example:

{
        "scope": "cdr-service",
        "code": "400",
        "message": "Received request does not match any known API request."
}

Where the scope property indicates the definer of the code value, and message is a human-readable string describing the nature of the error code and any additional information.


JSON Schema

{
        "type": "object",
        "properties": {
                "cdr_id": {
                        "title": "Unique correlation ID for a set of CDR events (expected to be generated as a random UUID)."
                },
                "call_id": {
                        "title": "A messaging or signaling-related identifier. For SIP events, this is the value of the Call-Id header. For other protocols, this should be a 'conversation' identifier, not a transactional one.",
                        "type": "string"
                },
                "calling_party": {
                        "title": "The logical originator/source of the communication this event relates to. May change between events with the same cdr_id. Expected to be in a normalized format defined by the application but may depend on the value of event.",
                        "type": "string"
                },
                "called_party": {
                        "title": "The logical terminus/destination of the communication this event relates to. May change between events with the same cdr_id. Expected to be in a normalized format defined by the application but may depend on the value of event.",
                        "type": "string"
                },
                "application": {
                        "title": "The high-level service/application that this cdr_id correlates to. Not expected to change within the context of a cdr_id.",
                        "type": "string"
                },
                "event": {
                        "title": "A short descriptive code describing the category of event this record describes. Defined by the emitting entity, which may be an application, protocol agent, etc.",
                        "type": "string"
                },
                "additional_data": {
                        "title": "A list of additional objects attached to this event such as detailed debugging data, trace data, or high-cardinality of values information. Expected to be documented by each emitting source the format of the objects it produces.",
                        "type": "array"
                },
                "disposition": {
                        "title": "A reason code, supplied in the format of a SIP Reason header."
                },
                "timestamp": {
                        "title": "Timestamp of the event, not necessarily when this record was created.",
                        "format": "%Y-%m-%d %H:%M:%S.%f"
                }
        },
        "additionalProperties": false,
        "required": [ "timestamp", "event", "application", "calling_party", "called_party" ]
}

Note

This schema represents the requirements for a fully populated CDR event. The CDR Service API allows omitting both cdr_id and timestamp as these can be generated at write time, when needed.


After processing the submission request, the CDR Service will validate the structure of the object using this schema and return an error if it does not comply.

Note

It is not required that the application label be consistent for all events with the same cdr_id. An application should not use multiple values for the application property when generating CDR events.


Default Configuration

The following is the default global CDR Service configuration:

<cdr-service
        local-ip-addr="0.0.0.0"
        cdr-backend="sqlite">
        <sqlite-config
                partition-schedule="0 0 * * *"
                partition-retention="30d,10GiB"
                backup-retention="7d,1GiB"
                root-dir="/var/opt/xpressworkx/cdr"/>
</cdr-service>
local-ip-addr

The local IP address can be specified numerically or omitted to bind on all interfaces. If not supplied, this will default to the wildcard/any address.

cdr-backend

The backend used for CDR Service. Allowed values are the default “sqlite”, which writes to partitioned sqlite files and is controlled by the <sqlite-config> tag, and “discard”, which discards all CDR writes immediately and logs a ‘LOUD’ message at startup indicating this state. Using the sqlite backend requires the root-dir attribute to also be specified.

partition-schedule

Specifies the schedule of rotations of the ‘current’ sqlite file storing CDR events. The default schedule rotates the file daily at midnight. An INFO level log message will be generated when the file is rotated. This value is a cron-like schedule string, indicating the “[minute] [hour] [day-of-month] [month] [day-of-week]” that the partitioning step should happen. Each value supports a wildcard (“*”), a single number (“0”), a range or list of values (“2-5,10”), and interval notation (”*/5”).

partition-retention

Specifies how long a partitioned sqlite file will be preserved after it is created and the maximum on-disk size of partitioned files before they will be deleted. The default is to remove files after 30 days or after consuming 20GiB. This is intended only as a protective configuration value as it is expected that partitioned files will be consumed by CDR synchronization and moved to <root-dir>/backup/ on a regular basis. If any files are removed as a result of this setting, an ERROR level log will be generated. The syntax of this property should be as follows:

“[time-interval],[space-limit]”

Where time-interval is any number of repetitions of [number][suffix] where each suffix is unique and one of the following: w (week), d (day), h (hour), or m (minutes). Where space-limit is [number][space-unit] where space-unit can be any of the following: b (bytes), kib (kibibytes), mib (mebibytes), gib (gibibytes), tib (tebibytes), or pib (pebibytes).

backup-retention

Specifies how long files in <root-dir>/backup are preserved before being cleaned up. The default is to keep backup files for 7 days or after consuming 1GiB. If any files are removed as a result of this setting, an DEBUG level log will be generated. The syntax of this property should be as follows:

“[time-interval],[space-limit]”

Where time-interval is any number of repetitions of [number][suffix] where each suffix is unique and one of the following: w (week), d (day), h (hour), or m (minutes). Where space-limit is [number][space-unit] where space-unit can be any of the following: b (bytes), kib (kibibytes), mib (mebibytes), gib (gibibytes), tib (tebibytes), or pib (pebibytes).

root-dir

The writable location where the sqlite files for storing CDR events are stored on a per-application basis. The default location is /var/opt/xpressworkx/cdr/.

Note

Any property that can be left with the default value can be entirely omitted or included with the default explicitly configured. It is recommended to have at least the partition-schedule, partition-retention, and backup-retention values configured.

SQLite Backend Configuration

The SQLite backend, when enabled, writes the events submitted to the CDR API into separate files by application name in the configured directory, determined by the root-dir attribute of the <sqlite-config> tag. These are named <application>.cdr.db when actively being written to. The partitioning interval causes the cdr_service process to close these files and rename them with a timestamp such as <application>.cdr-%Y-%m-%dT%H:%M:%S.%f.db (where the % formatting values are interpreted as for strftime).

The schema of the sqlite database is as follows, and maps directly to the JSON schema description of each CDR event as a single CDR table in that database.

Note

The rowid attribute may be useful only for local debugging of CDR event generation.

CREATE TABLE cdr (
        rowid INTEGER PRIMARY KEY,
        cdr_id BLOB NOT NULL,
        call_id TEXT,
        timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        calling_party TEXT NOT NULL,
        called_party TEXT NOT NULL,
        application TEXT NOT NULL,
        event TEXT NOT NULL,
        disposition INTEGER,
        additional_data BLOB
);
CREATE INDEX cdr_id_index ON cdr (cdr_id); CREATE INDEX cdpid_index ON cdr (called_party); CREATE INDEX cgpid_index ON cdr (calling_party); CREATE INDEX timestamp_index on cdr (timestamp); CREATE INDEX call_id_index ON cdr (call_id);

While accessing the active sqlite partition (without a timestamp in the file name) is possible, it is recommended to consume the partitioned database files for further processing. The cdr_service process will assume it is exclusively responsible for managing the active database. Once the partition is created, the cdr_service process will not open or access the partitioned databases. The only operation it may perform is the protective retention cleanups as configured for file size or age.

Once you have consumed the CDR events in a partitioned database, you may move the database to the <root-dir>/backup directory. This directory is intended to allow for a limited time window to recover from downstream processing errors in consuming CDR events. The backup retention configuration controls how the cdr_service process will clean out the files in this directory. It is also acceptable to consume the CDR partition and the delete it or archive the partitions elsewhere.