iPlum usage logs API documentation
Introduction
iPlum API is built using REST (Representational State Transfer). API calls can be made using all popular programming languages. The API is served exclusively over HTTPS, enabling a wide range of HTTP clients to interact with the iPlum API.
Getting Started
Access to the iPlum API requires that the account has the API feature enabled.
- Log in to your iPlum account with your administrator credentials at https://my.iplum.com.
- Go to Left Menu → API & Call Backs.
- Toggle the API switch to enable it.
API Endpoint
The URL of each resource can be obtained by accessing the API Root endpoint:
https://api.iplum.com/
HTTP Method
All iPlum APIs are accessed over HTTPS and use POST requests for every action. POST is used to get data, create resources, and perform resource actions.
Request Headers
Every request should have the following mandatory headers.
- X-iPlumAuth-AccessKey — The access key generated by the API.
- X-iPlumAuth-Username — The iPlum administrator login ID.
- X-iPlumAuth-EpochDateTime — Date-time in Epoch format.
- X-iPlumAuth-Version — The API version for the request.
- Authorization —
iPlumAuth <signature> - Content-Type —
application/json
Authorization
To generate the iPlum API authorization header, use the HMAC-SHA256 (Hash Message Authentication Code) signing algorithm.
Steps to Generate HMAC Signature
1. Generate access keys:
- Log into the iPlum website at https://my.iplum.com.
- Click on the API & Call Backs link on the left menu bar.
- Toggle the API switch if not enabled.
- Download and save your API access and secret keys.
2. Create a request for the iPlum API:
- Create an HTTP request object:
HttpRequestMessage request = new HttpRequestMessage();
- Create an HTTP header with the API access key:
request.Headers.Add("X-iPlumAuth-AccessKey", "ABCD123ACCESSKEY");
- Create an HTTP header with the iPlum administrator login ID:
request.Headers.Add("X-iPlumAuth-Username", "test@testuser.com");
- Create an HTTP header with the version:
request.Headers.Add("X-iPlumAuth-Version", "v1");
- Create an HTTP header with the epoch date-time:
request.Headers.Add("X-iPlumAuth-EpochDateTime", "1490752181");
3. Create a concatenated string by joining the following values with a newline (\n), in this exact order:
- HTTP method requested (in lower case). E.g.,
post - API access key.
- iPlum administrator login ID (in lower case).
- Version (in lower case). E.g.,
v1 - Epoch date-time.
- The URL path of the requested resource (in lower case). E.g.,
/usage/getusage
Use this concatenated string as the representation:
string representation = String.Join("\n",
httpMethod.ToLower(),
requestMessage.Headers.GetValues("X-iPlumAuth-AccessKey").FirstOrDefault(),
requestMessage.Headers.GetValues("X-iPlumAuth-Username").FirstOrDefault(),
requestMessage.Headers.GetValues("X-iPlumAuth-Version").FirstOrDefault().ToLower(),
requestMessage.Headers.GetValues("X-iPlumAuth-EpochDateTime").FirstOrDefault().ToLower(),
uri.ToLower());
4. Generate an HMAC signature using both the secret key and the representation.
5. Base-64 encode the generated HMAC signature and add it to the Authorization header:
requestMessage.Headers.Add("Authorization", "iPlumAuth" + " " + signature);
6. The final iPlum API request header should look like the example below:
Content-Type: application/json
X-iPlumAuth-AccessKey: ABCD1234ACCESSKEY
X-iPlumAuth-Username: test@testuser.com
X-iPlumAuth-Version: v1
X-iPlumAuth-EpochDateTime: 1490752181
Authorization: iPlumAuth w5tcOEC7xZIQOhxMMGDB6U83fpFReiUYiGVCwZHgaHI=
7. Send content within the body section of the request.
Complete Signing Examples
The following helpers build the full set of authorization headers (including the Authorization signature) for a request, following the steps above.
Node.js:
const crypto = require('crypto');
function buildAuthHeaders({ accessKey, secretKey, username }, method, url) {
const epoch = Math.floor(Date.now() / 1000).toString();
const path = new URL(url).pathname.toLowerCase();
const version = 'v1';
const stringToSign = [
method.toLowerCase(),
accessKey,
username.toLowerCase(),
version,
epoch,
path,
].join('\n');
const signature = crypto
.createHmac('sha256', secretKey)
.update(stringToSign, 'utf8')
.digest('base64');
return {
'X-iPlumAuth-AccessKey': accessKey,
'X-iPlumAuth-Username': username,
'X-iPlumAuth-Version': version,
'X-iPlumAuth-EpochDateTime': epoch,
Authorization: `iPlumAuth ${signature}`,
};
}
Python:
import time, hmac, hashlib, base64
from urllib.parse import urlparse
def build_auth_headers(access_key, secret_key, username, method, url):
epoch = str(int(time.time()))
path = urlparse(url).path.lower()
version = "v1"
string_to_sign = "\n".join(
[method.lower(), access_key, username.lower(), version, epoch, path]
)
signature = base64.b64encode(
hmac.new(secret_key.encode("utf-8"),
string_to_sign.encode("utf-8"),
hashlib.sha256).digest()
).decode("utf-8")
return {
"X-iPlumAuth-AccessKey": access_key,
"X-iPlumAuth-Username": username,
"X-iPlumAuth-Version": version,
"X-iPlumAuth-EpochDateTime": epoch,
"Authorization": f"iPlumAuth {signature}",
}
C#:
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
static Dictionary<string, string> BuildAuthHeaders(
string accessKey, string secretKey, string username, string method, Uri url)
{
var epoch = ((long)(DateTime.UtcNow -
new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds).ToString();
var version = "v1";
var path = url.AbsolutePath.ToLowerInvariant();
var stringToSign = string.Join("\n",
method.ToLowerInvariant(), accessKey, username.ToLowerInvariant(), version, epoch, path);
string signature;
using (var h = new HMACSHA256(Encoding.UTF8.GetBytes(secretKey)))
signature = Convert.ToBase64String(h.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
return new Dictionary<string, string>
{
["X-iPlumAuth-AccessKey"] = accessKey,
["X-iPlumAuth-Username"] = username,
["X-iPlumAuth-Version"] = version,
["X-iPlumAuth-EpochDateTime"] = epoch,
["Authorization"] = $"iPlumAuth {signature}",
};
}
Bash (with curl):
# Requires: bash, openssl, curl. Set these first:
# export IPLUM_BASE_URL="https://api.iplum.com"
# export IPLUM_ACCESS_KEY="your-access-key"
# export IPLUM_SECRET_KEY="your-secret-key"
# export IPLUM_USERNAME="admin@example.com"
#
# Usage: iplum_curl <METHOD> <PATH> [JSON_BODY]
iplum_curl() {
local method="$1" path="$2" body="${3:-}"
local epoch; epoch=$(date +%s)
local m_lc p_lc u_lc
m_lc=$(printf '%s' "$method" | tr '[:upper:]' '[:lower:]')
p_lc=$(printf '%s' "$path" | tr '[:upper:]' '[:lower:]')
u_lc=$(printf '%s' "$IPLUM_USERNAME" | tr '[:upper:]' '[:lower:]')
local sts; sts=$(printf '%s\n%s\n%s\n%s\n%s\n%s' \
"$m_lc" "$IPLUM_ACCESS_KEY" "$u_lc" "v1" "$epoch" "$p_lc")
local sig; sig=$(printf '%s' "$sts" | openssl dgst -sha256 -hmac "$IPLUM_SECRET_KEY" -binary | base64)
curl -sS -X "$method" "$IPLUM_BASE_URL$path" \
-H "X-iPlumAuth-AccessKey: $IPLUM_ACCESS_KEY" \
-H "X-iPlumAuth-Username: $IPLUM_USERNAME" \
-H "X-iPlumAuth-Version: v1" \
-H "X-iPlumAuth-EpochDateTime: $epoch" \
-H "Authorization: iPlumAuth $sig" \
${body:+-H "Content-Type: application/json" -d "$body"}
}
Java:
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public static Map<String, String> buildAuthHeaders(
String accessKey, String secretKey, String username, String method, String url) throws Exception {
String epoch = Long.toString(System.currentTimeMillis() / 1000L);
String path = URI.create(url).getPath().toLowerCase();
String version = "v1";
String stringToSign = String.join("\n",
method.toLowerCase(), accessKey, username.toLowerCase(), version, epoch, path);
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
String signature = Base64.getEncoder().encodeToString(
mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8)));
Map<String, String> headers = new LinkedHashMap<>();
headers.put("X-iPlumAuth-AccessKey", accessKey);
headers.put("X-iPlumAuth-Username", username);
headers.put("X-iPlumAuth-Version", version);
headers.put("X-iPlumAuth-EpochDateTime", epoch);
headers.put("Authorization", "iPlumAuth " + signature);
return headers;
}
PHP:
<?php
function build_auth_headers($accessKey, $secretKey, $username, $method, $url) {
$epoch = (string) time();
$path = strtolower(parse_url($url, PHP_URL_PATH));
$version = 'v1';
$stringToSign = implode("\n", [
strtolower($method), $accessKey, strtolower($username), $version, $epoch, $path,
]);
$signature = base64_encode(hash_hmac('sha256', $stringToSign, $secretKey, true));
return [
'X-iPlumAuth-AccessKey' => $accessKey,
'X-iPlumAuth-Username' => $username,
'X-iPlumAuth-Version' => $version,
'X-iPlumAuth-EpochDateTime' => $epoch,
'Authorization' => "iPlumAuth $signature",
];
}
Response
All iPlum API requests and responses use JSON format.
Response Codes
Responses to requests made to the iPlum API are categorized under the following codes:
- 200 — Success. The request was successfully completed. Requested data is provided in the content section.
- 400 — Bad Request. The request cannot be fulfilled, usually because of a malformed or missing parameter.
- 401 — Unauthorized. Request was rejected because of invalid authentication credentials.
- 403 — Forbidden. The requesting user does not have enough permission to access the resource.
- 404 — URL Not Found. The URL sent is wrong. It's possible that the requested resource has been moved to another URL.
- 405 — Method Not Allowed. The requested resource does not support the HTTP method used. For example, requesting the List of all customers API with
PUTas the HTTP method. - 406 — Not Acceptable. The requested response type is not supported by the client.
- 429 — Too Many Requests. Too many requests within a certain time frame.
- 500 — Server Error. iPlum Developer APIs' server encountered an error which prevents it from fulfilling the request. Although this rarely happens, we recommend you contact us if you receive this error.
Error Response
In case of errors (HTTP status code 400 / 500), the iPlum API returns an error object with the following format. Each endpoint lists its own error codes in an Error Codes section.
- errorCode — Error code. Data Type: String
- errorDescription — Error description in the language specified in the request. Data Type: String
Usage (Call, Text, Fax Logs)
A user can use iPlum for calls, text, and/or fax. This usage information (history) can be requested using the iPlum API.
Get Usage Records
This API call is used to fetch call, text, and/or fax usage logs.
URL: POST /usage/getusage
Request Model
- Lang — ISO 639-1 language code. Data Type: String. Default:
en - Usage — List of all usage types. Multiple values can be provided using a comma-separated list. Data Type: String. Default: Empty. Accepted values:
iPlumtext,externalcall,externaltext,fax— iPlumtext: iPlum secure text, externalcall: External call, externaltext: External text, fax: Fax - StartDate — Start date filter for usage records. Data Type: String. Default: Empty. Accepted format: Date-time in UTC.
- EndDate — End date filter for usage records. Note: The end date should not be greater than 5 days from the start date. Data Type: String. Default: Empty. Accepted format: Date-time in UTC.
Request Header Example
Content-Type: application/jsonX-iPlumAuth-AccessKey: ABCD1234ACCESSKEYX-iPlumAuth-Username: test@testuser.comX-iPlumAuth-Version: v1X-iPlumAuth-EpochDateTime: 1696387075Authorization: iPlumAuth Hn6YFjVUpQCLgH8AWss8Nyp5M6eQiOKnaMZkExTJL/4=
Request Body Example
{"Lang":"en", "Usage":"externalcall,externaltext", "StartDate":"2023-03-20 02:23:06.000", "EndDate":"2023-03-24 02:23:06.000"}
Response Model
The response is a list of usage records.
- source — Source of usage record. Data Type: String. Value:
iPlum - clientReferenceId — Custom value set on the account. Data Type: String
- recordId — Unique usage record ID. Data Type: String
- usage — Usage category. Data Type: String. Possible values:
call,text,fax - type — Usage type. Data Type: String. Possible values:
iplum,external - from — From number. Data Type: String
- to — To number. Data Type: String
- startTimeUTC — Start date and time of usage. Data Type: String. Format: Date-time in UTC.
- endTimeUTC — End date and time of usage. Data Type: String. Format: Date-time in UTC.
- duration — Duration of the call in minutes. For text, this value will be
1. Data Type: String - direction — Direction of usage. Data Type: String. Possible values:
in,out - iPlumCredits — iPlum credits used in this usage. Data Type: Integer
- userId — iPlum user to which the usage belongs. Data Type: String
Response Example
[{"source":"iPlum", "clientReferenceId":"1234", "usage":"call", "recordId":"0UmFAs73cwKbyqUtmz7ltLI.nGlAD.0Dgjdhpm", "from":"+14081231234", "to":"test@iplum.com", "startTimeUTC":"2023-03-24T00:00:00Z", "endTimeUTC":"2023-03-24T00:00:00Z", "duration":"2", "direction":"in", "type":"external", "iPlumCredits":2, "userId":"test@iplum.com"}]
Error Codes
(see Error Response above for the object format)
- ERR_INVALID_DATE — The date range specified in the request is not valid. The end date should not be later than five days from the start date.
- ERR_INVALID_REQUEST — Request has some invalid parameters.
Get Usage Content
This API call is used to fetch the content of sent/received text or fax. It requires the account to have a valid security & backup plan.
URL: POST /usage/getcontent
Request Model
- Lang — ISO 639-1 language code. Data Type: String. Default:
en - RecordID — Usage record ID. Data Type: String
Request Header Example
Content-Type: application/jsonX-iPlumAuth-AccessKey: ABCD1234ACCESSKEYX-iPlumAuth-Username: test@testuser.comX-iPlumAuth-Version: v1X-iPlumAuth-EpochDateTime: 1696387155Authorization: iPlumAuth FvWXGLoxw0zy9tu381IyebstzBVCw8YJtVm+VGmtQIo=
Request Body Example
{"Lang":"en", "RecordID":"0UmFAs73cwKbyqUtmz7ltLI.nGlAD.0Dgjdhpm"}
Response Model
The response is a string representing text/fax content. It could be text, or a URL for a photo/audio/video attachment or fax file.
Text example: Hello
Media URL example: http://media.iplum.com/af542549-527a-47c8-a183-b853455501c0.jpg
Error Codes
- ERR_INVALID_REQUEST — Request has some invalid parameters.
Call Recordings
Users can record incoming and/or outgoing calls. It requires the user to have a valid call recording plan.
Get Recording Records
This API call is used to fetch call recording logs.
URL: POST /callrecordings/getrecordings
Request Model
- Lang — ISO 639-1 language code. Data Type: String. Default:
en - StartDate — Start date filter for usage records. Data Type: String. Default: Empty. Accepted format: Date-time in UTC.
- EndDate — End date filter for usage records. Note: The end date should not be greater than 5 days from the start date. Data Type: String. Default: Empty. Accepted format: Date-time in UTC.
Request Header Example
Content-Type: application/jsonX-iPlumAuth-AccessKey: ABCD1234ACCESSKEYX-iPlumAuth-Username: test@testuser.comX-iPlumAuth-Version: v1X-iPlumAuth-EpochDateTime: 1696387193Authorization: iPlumAuth w5VKuiMqd38g53ksT8xBuoShgu0Xj1NfXx8WHoDCG3A=
Request Body Example
{"Lang":"en", "StartDate":"2023-03-20 02:23:06.000", "EndDate":"2023-03-24 02:23:06.000"}
Response Model
The response is a list of call recording records.
- source — Source of call recording record. Data Type: String. Value:
iPlum - clientReferenceId — Custom value set on the account. Data Type: String
- recordId — Unique usage record ID. Data Type: String
- phonenumber — Phone number of the called/calling party. Data Type: String
- dateTimeUTC — Date and time of recording. Data Type: String. Format: Date-time in UTC.
- duration — Duration of the call recording in minutes. Data Type: String
- length — Length of the recording file in KB or MB. Data Type: String
- direction — Direction of call. Data Type: String. Possible values:
in,out - userId — iPlum user to which the usage belongs. Data Type: String
Response Example
[{"source":"iPlum", "clientReferenceId":"1234", "recordId":"ae0b4e3e-76c0-46d2-8bab-18c8764081dbgjdi2j", "phonenumber":"+14081231234", "dateTimeUTC":"2023-03-22T00:24:00Z", "duration":"3", "direction":"in", "length":"434.2 KB", "userId":"test@iplum.com"}]
Error Codes
- ERR_INVALID_DATE — The date range specified in the request is not valid. The end date should not be greater than 5 days from the start date.
- ERR_INVALID_REQUEST — Request has some invalid parameters.
Get Recording File
This API call is used to fetch the recording file for the given record.
URL: POST /callrecordings/getrecordingfile
Request Model
- Lang — ISO 639-1 language code. Data Type: String. Default:
en - RecordID — Call recording record ID. Data Type: String
Request Header Example
Content-Type: application/jsonX-iPlumAuth-AccessKey: ABCD1234ACCESSKEYX-iPlumAuth-Username: test@testuser.comX-iPlumAuth-Version: v1X-iPlumAuth-EpochDateTime: 1696387235Authorization: iPlumAuth XMFT4svyRWjm/wwK+FfQjEo24jJpMpt99Uv7fs2PN10=
Request Body Example
{"Lang":"en", "RecordID":"ae0b4e3e-76c0-46d2-8bab-18c8764081dbgjdi2j"}
Response Model
The response is a string URL for the recording file.
Example: http://media.iplum.com/ae0b4e3e-76c0-46d2-8bab-18c8764081db.mp3
Error Codes
- ERR_INVALID_REQUEST — Request has some invalid parameters.
Callbacks
Callbacks are real-time, event-driven notifications.
Callback Signature Verification
iPlum signs outbound callback (webhook) requests so your endpoint can verify that a request genuinely originated from iPlum, that the payload was not tampered with in transit, and that it is not a replay of an older delivery. This applies to all callback types – usage callbacks (call, text, and/or fax) and call recording callbacks.
Headers
Every signed callback POST includes the following headers:
- X-iPlum-Access-Key — Your iPlum API Access Key. Identifies which credential was used to sign the request, so you know which Secret Key to use for verification.
- X-iPlum-Timestamp — Unix epoch time, in seconds, at the moment iPlum signed the request.
- X-iPlum-Signature —
sha256=followed by the Base64-encoded HMAC-SHA256 signature.
The request body is sent in JSON format (Content-Type: application/json). If HTTP Basic authentication is also configured for your callback URL, the Authorization: Basic header is included in addition to the headers above.
Signature
iPlum builds a canonical string representation of the request and signs it using HMAC-SHA256 with your iPlum API Secret Key, then Base64-encodes the result. The representation is three lines joined by a newline (\n):
post
<X-iPlum-Timestamp value>
<raw request body>signature = Base64( HMAC_SHA256( SecretKey, "post" + "\n" + timestamp + "\n" + rawBody ) )
- Line 1 is the HTTP method in lower case:
post - Line 2 is exactly the
X-iPlum-Timestampheader value. - Line 3 is the raw request body exactly as received – do not re-serialize, reorder keys, or re-encode the JSON before verifying.
This uses the same HMAC-SHA256 signing method and Secret Key as iPlum's inbound API request signing (see Steps to Generate HMAC Signature above); the outbound representation additionally covers the request body, so payload integrity is protected as well.
Verification Steps
- Read
X-iPlum-TimestampandX-iPlum-Signaturefrom the incoming request. - Reject the request if the timestamp is more than 5 minutes (300 seconds) away from the current time – this is the replay-attack check.
- Rebuild the representation:
"post"+ newline + timestamp + newline + the raw request body. - Compute
Base64(HMAC-SHA256(YourSecretKey, representation)). - Compare the result to the value after
sha256=inX-iPlum-Signatureusing a constant-time comparison. - Accept the callback only if the signatures match.
Use your iPlum API Secret Key – the same key you use to sign requests you send to the iPlum API. The X-iPlum-Access-Key header tells you which key applies.
Reference Implementations
Node.js (Express):
const crypto = require('crypto');
// Ensure you have the RAW body. With Express:
// app.use('/iplum/callback', express.raw({ type: '*/*' }));
function verifyIplumCallback(req, secretKey) {
const timestamp = req.get('X-iPlum-Timestamp');
const header = req.get('X-iPlum-Signature') || '';
const provided = header.startsWith('sha256=') ? header.slice('sha256='.length) : header;
// Replay protection (5 minutes)
const nowSec = Math.floor(Date.now() / 1000);
if (!timestamp || Math.abs(nowSec - Number(timestamp)) > 300) return false;
const rawBody = req.body.toString('utf8'); // Buffer -> exact bytes as received
const representation = ['post', timestamp, rawBody].join('\n');
const expected = crypto.createHmac('sha256', secretKey).update(representation, 'utf8').digest('base64');
const a = Buffer.from(expected);
const b = Buffer.from(provided);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Python (Flask):
import base64, hashlib, hmac, time
def verify_iplum_callback(headers, raw_body: bytes, secret_key: str) -> bool:
timestamp = headers.get("X-iPlum-Timestamp")
header = headers.get("X-iPlum-Signature", "")
provided = header[len("sha256="):] if header.startswith("sha256=") else header
# Replay protection (5 minutes)
if not timestamp or abs(int(time.time()) - int(timestamp)) > 300:
return False
representation = "\n".join(["post", timestamp, raw_body.decode("utf-8")]).encode("utf-8")
digest = hmac.new(secret_key.encode("utf-8"), representation, hashlib.sha256).digest()
expected = base64.b64encode(digest).decode("ascii")
return hmac.compare_digest(expected, provided)
C#:
using System;
using System.Security.Cryptography;
using System.Text;
// Pass the RAW request body string exactly as received (do not re-serialize).
static bool VerifyIplumCallback(string timestamp, string signatureHeader, string rawBody, string secretKey)
{
signatureHeader ??= "";
var provided = signatureHeader.StartsWith("sha256=")
? signatureHeader.Substring("sha256=".Length)
: signatureHeader;
// Replay protection (5 minutes)
var nowSec = (long)(DateTime.UtcNow -
new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
if (!long.TryParse(timestamp, out var ts) || Math.Abs(nowSec - ts) > 300)
return false;
var representation = string.Join("\n", "post", timestamp, rawBody);
string expected;
using (var h = new HMACSHA256(Encoding.UTF8.GetBytes(secretKey)))
expected = Convert.ToBase64String(h.ComputeHash(Encoding.UTF8.GetBytes(representation)));
// Constant-time comparison (returns false if lengths differ)
return CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(expected), Encoding.UTF8.GetBytes(provided));
}
Bash:
# Requires: bash, openssl. Read the raw body from a file to preserve exact bytes.
# Usage: verify_iplum_callback <timestamp> <signature-header> <raw-body-file> <secret-key>
verify_iplum_callback() {
local timestamp="$1" sig_header="$2" body_file="$3" secret="$4"
local provided="${sig_header#sha256=}"
# Replay protection (5 minutes)
[ -z "$timestamp" ] && return 1
local now diff; now=$(date +%s); diff=$(( now - timestamp )); diff=${diff#-}
[ "$diff" -gt 300 ] && return 1
# representation = "post\n<timestamp>\n<raw body>"
local expected
expected=$( { printf 'post\n%s\n' "$timestamp"; cat "$body_file"; } \
| openssl dgst -sha256 -hmac "$secret" -binary | base64 )
# Constant-time comparison via double-HMAC blinding
[ "${#expected}" -ne "${#provided}" ] && return 1
local key he hp; key=$(openssl rand -hex 16)
he=$(printf '%s' "$expected" | openssl dgst -sha256 -hmac "$key" | awk '{print $NF}')
hp=$(printf '%s' "$provided" | openssl dgst -sha256 -hmac "$key" | awk '{print $NF}')
[ "$he" = "$hp" ]
}
Java:
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
// Pass the RAW request body exactly as received (do not re-serialize).
public static boolean verifyIplumCallback(
String timestamp, String signatureHeader, String rawBody, String secretKey) throws Exception {
if (signatureHeader == null) signatureHeader = "";
String provided = signatureHeader.startsWith("sha256=")
? signatureHeader.substring("sha256=".length()) : signatureHeader;
// Replay protection (5 minutes)
if (timestamp == null) return false;
long now = System.currentTimeMillis() / 1000L;
if (Math.abs(now - Long.parseLong(timestamp)) > 300) return false;
String representation = String.join("\n", "post", timestamp, rawBody);
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
String expected = Base64.getEncoder().encodeToString(
mac.doFinal(representation.getBytes(StandardCharsets.UTF_8)));
// Constant-time comparison (returns false if lengths differ)
return MessageDigest.isEqual(
expected.getBytes(StandardCharsets.UTF_8), provided.getBytes(StandardCharsets.UTF_8));
}
PHP:
<?php
// Pass the RAW request body exactly as received, e.g. file_get_contents('php://input').
function verify_iplum_callback($timestamp, $signatureHeader, $rawBody, $secretKey) {
$provided = (strpos((string) $signatureHeader, 'sha256=') === 0)
? substr($signatureHeader, strlen('sha256=')) : (string) $signatureHeader;
// Replay protection (5 minutes)
if ($timestamp === null || $timestamp === '' || abs(time() - (int) $timestamp) > 300) {
return false;
}
$representation = implode("\n", ['post', $timestamp, $rawBody]);
$expected = base64_encode(hash_hmac('sha256', $representation, $secretKey, true));
// Constant-time comparison
return hash_equals($expected, $provided);
}
Notes
- Always serve your callback endpoint over HTTPS – iPlum rejects non-HTTPS callback URLs.
- Verify over the raw body bytes. Many frameworks parse JSON before your handler runs, so capture the raw body before verifying.
- Treat the Secret Key as a credential: store it securely and rotate it if it is ever exposed.
Usage Callbacks
Clients can subscribe to notifications for call, text, and/or fax events. A notification (i.e., a response in the form of a usage record) is sent to the client.
Notifications are sent to the URI provided by the client during the API account setup.
Response
- source — Source of usage record. Data Type: String. Value:
iPlum - clientReferenceId — Custom value set on the account. Data Type: String
- recordId — Unique usage record ID. Data Type: String
- usage — Usage category. Data Type: String. Possible values:
call,text,fax - type — Usage type. Data Type: String. Possible values:
iplum,external - from — From number. Data Type: String
- to — To number. Data Type: String
- startTimeUTC — Start date and time of usage. Data Type: String. Format: Date-time in UTC.
- endTimeUTC — End date and time of usage. Data Type: String. Format: Date-time in UTC.
- duration — Duration of the call in minutes. For text, this value will be
1. Data Type: String - direction — Direction of usage. Data Type: String. Possible values:
in,out - content — Text content (if enabled in callback settings and the user has a valid backup plan). Data Type: String
- iPlumCredits — iPlum credits used in this usage. Data Type: Integer
- userId — iPlum user to which the usage belongs. Data Type: String
Example
{"source": "iPlum", "clientReferenceId": "1234", "usage": "text", "recordId": "0UmFAs73cwKbyqUtmz7ltLI.nGlAD.0Dgjdhpm", "from": "+14081231234", "to": "test@iplum.com", "startTimeUTC": "2023-03-24T00:00:00Z", "endTimeUTC": "2023-03-24T00:00:00Z", "duration": "1", "direction": "in", "type": "external", "content": "Hello", "iPlumCredits": 1, "userId": "test@iplum.com"}
Call Recording Callbacks
Clients can subscribe to a notification when a new call recording is available. A notification (i.e., a response in the form of a call recording record) is sent to the client.
Notifications are sent to the URI provided by the client during the API account setup.
Response
- source — Source of call recording record. Data Type: String. Value:
iPlum - clientReferenceId — Custom value set on the account. Data Type: String
- recordId — Unique usage record ID. Data Type: String
- phonenumber — Phone number of the called/calling party. Data Type: String
- dateTimeUTC — Date and time of recording. Data Type: String. Format: Date-time in UTC.
- duration — Duration of the call recording in minutes. Data Type: String
- length — Length of the recording file in KB or MB. Data Type: String
- direction — Direction of call. Data Type: String. Possible values:
in,out - content — Recording file URL (if enabled in callback settings and the user has a valid call recording plan). Data Type: String
- userId — iPlum user to which the usage belongs. Data Type: String
Example
{"source": "iPlum", "clientReferenceId": "1234", "recordId": "ae0b4e3e-76c0-46d2-8bab-18c8764081dbgjdi2j", "phonenumber": "+14081231234", "dateTimeUTC": "2023-03-22T00:24:00Z", "duration": "3", "direction": "in", "length": "434.2 KB", "content": "http://media.iplum.com/ae0b4e3e-76c0-46d2-8bab-18c8764081db.mp3", "userId": "test@iplum.com"}
Contact Us
For any questions, please use the online form to send your ticket: https://iplum.com/contact-us-iplum/
Your payment has been received and will be process!
%20(1).avif)
.avif)
