Transactions API
The Transactions API returns the complete record of every message sent through your account: status, price, operator, and delivery time, across all eight channels. One GET endpoint, one predictable JSON envelope.
| Question | The API answers with |
|---|---|
| Did it arrive? | status and statusdescription, from Sent to Delivered |
| How long did it take? | deliverytime in seconds, plus readbyrecipient where reads are reported |
| What did it cost? | price per message and running totals in message and invoice currency |
| Which operator carried it? | operatorname and the mccmnc operator code |
| Which of my records is this? | your own reference, so the data lines up without a mapping table |
Querying efficiently
Response time tracks how much data your query touches. For fast, reliable responses: keep date ranges small, request only the fields you need with
omit, and fetch pages one after another. See Performance and recommended usage.
Authentication
Authenticate your calls with your product token or with the CM SSO cookie.
| Method | How |
|---|---|
| Product token | Send the header X-CM-PRODUCTTOKEN with your product token as its value. Standard for server-to-server calls. |
| CM SSO | Send your CM.com platform session cookie with the request. |
| Host | Status | Base URL |
|---|---|---|
api.cm.com | Recommended | https://api.cm.com/v1.2/transactions/ |
api.cmtelecom.com | Legacy alias, still works | https://api.cmtelecom.com/v1.2/transactions/ |
Point new integrations at api.cm.com. Anything already running on api.cmtelecom.com keeps working, so there is no rush to migrate.
Quick start
Every call is a GET request. Give the API a date range and read the result out of the envelope.
curl "https://api.cm.com/v1.2/transactions/?startdate=2026-10-22T08:30&enddate=2026-10-22T09:00&channel=WhatsApp" \
-H "X-CM-PRODUCTTOKEN: your-product-token"
{
"status": 1,
"message": "Ok",
"summary": {
"direction": "out",
"totalTransactions": 1,
"totalPrice": 0.04,
"priceCurrency": "EUR"
},
"result": [
{
"recipient": "0031600000000",
"channel": "WhatsApp",
"status": 20,
"statusdescription": "Delivered",
"operatorname": "NL - Vodafone",
"deliverytime": 7
}
]
}
Delivery status
Every outgoing message moves through the same path. Three states are in flight, three are final. The status field carries the numeric code. Incoming messages carry no status.
| Code | Status | State |
|---|---|---|
| 19 | Sent | In transit |
| 37 | Accepted | In transit |
| 20 | Delivered | Delivered |
| 21 | Failed | Failure (final) |
| 40 | Rejected | Failure (final) |
| 22 | Cancelled | Cancelled (final) |
Performance and recommended usage
A few habits keep your queries fast and reliable.
| Habit | Why it helps |
|---|---|
| Keep date ranges tight | One month is the ceiling, but narrower is faster. For large exports, split the work day by day. |
| Ask only for what you use | omit=totalTransactions,totalprice,totaltariff skips the count and sum step entirely. |
| Page one at a time | Request the next page only after the previous one lands. Parallel pages compete and invite timeouts. |
| Avoid deep offsets | Large startat values get slower the deeper they go. Prefer narrower windows. |
Timeouts under heavy load
A request can occasionally return
HTTP 504during peak usage. Build a retry with backoff into your integration, and the habits above keep it rare.
Response
The result set is wrapped in an envelope that carries information about the set. Whatever you filter for, the shape stays the same.
Envelope
| Property | Value Type | Description |
|---|---|---|
status | Integer | 1 for successful request processing |
message | String | Ok, when nothing exceptional happened |
An empty result array means there are no transactions for the given parameters.
Summary
Holds all applied filtering information, plus some aggregate information.
| Property | Value Type | Description |
|---|---|---|
startDate | String | The start date formatted in ISO8601 |
endDate | String | The end date formatted in ISO8601 |
direction | String | Whether the message was in incoming or out outgoing |
totalTransactions | Integer | Amount of transactions in the result array |
totalPrice | Float | Total price of all messages in the result array |
priceCurrency | String | Price currency as ISO 4217 currency code |
localPrice | Float | Price you will pay for the messages in your currency |
localCurrency | String | Currency used for your invoice as ISO 4217 currency code |
totalTariff | Integer | The sum of the Tariff field. This tariff applies to premium SMS messages, where the recipient is charged for receiving the SMS |
Pagination
| Property | Value Type | Description |
|---|---|---|
| currentPage | Integer | Current page number |
| totalPages | Integer | Total available pages |
| pageSize | Integer | Items per page |
Result
The array that holds the result set, one object per message.
| Property | Value Type | Description |
|---|---|---|
| sender | String | Sender's phone number |
| recipient | String | Recipient's phone number |
| message | String | Message content |
| standarderror | Null/String | Standard error code |
| errordescription | String | Error description |
| status | Integer | Message status code (null for incoming messages) |
| statusdescription | String | Status description |
| created | String (ISO 8601) | Message creation timestamp |
| countryiso | String | Country in ISO3166 notation |
| countryname | String | Country name |
| mccmnc | String | Mobile country code and Mobile network code |
| operatorname | String | Mobile operator name |
| deliverytime | Integer | Delivery time in seconds |
| datacodingscheme | Integer | Data coding scheme |
| userdataheader | String | User data header |
| channel | String | Communication channel |
| price | Integer/Float | Message price |
| currency | String | Currency of price |
| localPrice | Integer/Float | Local currency price |
| localCurrency | String | Local currency code |
| tariff | Integer/Float | Tariff rate |
| premium | Boolean | Indicates premium service |
| customgrouping | Null/String | Custom grouping field |
| customgrouping2 | Null/String | Custom grouping field 2 |
| customgrouping3 | Null/String | Custom grouping field 3 |
| reference | String | Unique message reference |
| converted | Null/String | Converted content (if any) |
| direction | String | Message direction (in/out) |
| multipart | Null/Boolean | Indicates multipart message |
| readbyrecipient | String (ISO 8601) | Read timestamp |
Full examples
Outgoing (out) is the default direction: messages sent to a mobile phone. Incoming (?direction=in) covers messages sent from a mobile phone.
{
"status": 1,
"message": "Ok",
"pagination": {
"currentPage": 1,
"totalPages": 1,
"pageSize": 1000
},
"summary": {
"startDate": "2026-10-22T08:30:00+02:00",
"endDate": "2026-10-22T09:00:00+02:00",
"direction": "out",
"totalTransactions": 1,
"totalPrice": 0,
"priceCurrency": "EUR",
"localPrice": 0,
"localCurrency": "USD",
"totalTariff": 0
},
"result": [
{
"sender": "009190000000",
"recipient": "009190000000",
"message": "",
"standarderror": null,
"errordescription": "",
"status": 20,
"statusdescription": "Delivered",
"created": "2026-10-22T08:51:55+02:00",
"countryiso": "IN",
"countryname": "India",
"mccmnc": "404997",
"operatorname": "IN - Unspecified operator",
"deliverytime": 7,
"datacodingscheme": 0,
"userdataheader": "",
"channel": "WhatsApp",
"price": 0,
"currency": "EUR",
"localPrice": 0,
"localCurrency": "USD",
"tariff": 0,
"premium": false,
"customgrouping": null,
"customgrouping2": null,
"customgrouping3": null,
"reference": "47ce194c-7f83-4c05-ad1a-46697dd86a3e",
"converted": null,
"direction": "out",
"multipart": null,
"readbyrecipient": "2026-10-22T09:22:31+02:00"
}
]
}
{
"status": 1,
"message": "Ok",
"pagination": {
"currentPage": 1,
"pageSize": 1000,
"totalPages": 1
},
"summary": {
"direction": "in",
"startDate": "2026-02-01T00:00:00+01:00",
"endDate": "2026-02-02T00:00:00+01:00",
"totalTransactions": 1
},
"result": [
{
"channel": "SMS",
"countryiso": "NL",
"countryname": "Netherlands",
"created": "2026-02-01T23:58:20+01:00",
"customgrouping": null,
"customgrouping2": null,
"customgrouping3": null,
"datacodingscheme": 0,
"direction": "in",
"mccmnc": "20404",
"message": "I love CM.com's transaction API",
"operatorname": "NL - Vodafone",
"premium": false,
"price": 0,
"recipient": "3669",
"reference": "510661747",
"sender": "0031601234567",
"status": null,
"tariff": 0,
"userdataheader": ""
}
]
}
Required filters
Date range
startdate={ISO8601}enddate={ISO8601}
These are the only required parameters. When the time is left off it is filled with zeroes, so 2026-05-05 is read as 2026-05-05T00:00:00. A maximum range of 1 month is allowed.
Optional filters
Channel
Filter transactions per channel.
| Channel | Description |
|---|---|
| SMS | Short Message Service |
| PUSH | Push Notification |
| Voice | Voice Call |
| RCS | Rich Communication Services |
| Viber | Viber Messaging |
| WhatsApp Messaging | |
| iMessage | Apple iMessage |
| Line | Line Messaging |
Usage: channel=WhatsApp
Direction
| Property | Value Type | Description |
|---|---|---|
| direction | String | Defines the message direction (in or out) |
out(default): country code of the recipientin: country code of the sender
Recipient
Filter by the receiver of a transaction.
| Property | Value Type | Description |
|---|---|---|
| recipient | String | Comma-separated list of recipients (phone numbers or shortcodes) |
| reference | String | Comma-separated list of customer-provided references |
- By recipient:
recipient=0031601234567,0031607654321 - By reference:
reference=customer-known-identifier1,customer-known-identifier2
Sender
Filter by the sender name of a transaction.
| Property | Value Type | Description |
|---|---|---|
| sender | String | Comma-separated list of senders (company name, shortcode, or longcode) |
Usage: sender=3669,0031601234567
Message
Filter for specific message content. Works in both directions and supports the * wildcard.
message=This very specific messagemessage=This*message
CountryISO
Filter by recipients in specific countries.
| Property | Value Type | Description |
|---|---|---|
| countryiso | String | Comma-separated list of country ISO codes |
Example: countryiso=NL,DE,FR
MCC MNC
Filter by operator. In 20404, 204 identifies the country (the Netherlands) and 04 identifies the operator (Vodafone).
| Property | Value Type | Description |
|---|---|---|
| mccmnc | String | Comma-separated list of operator identification codes |
Example: mccmnc=20404,20405
Statuscode
Filter by the status code returned by this API.
| Property | Value Type | Description |
|---|---|---|
| statuscode | Integer | Comma-separated list of status codes |
Example: statuscode=20,21,40 (Delivered, Failed, Rejected)
Errorcode
Filter by the error codes this API returns.
| Property | Value Type | Description |
|---|---|---|
| errorcode | Integer | Comma-separated list of error codes |
Analyticserror
Filter by the readable error code the analytics API uses.
| Code | Description |
|---|---|
| 0 | Unknown recipient |
| 1 | Unknown failure |
| 2 | Too many messages to recipient |
| 3 | Recipient temporarily unavailable |
| 4 | Recipient blacklisted |
| 5 | Message rejected by operator |
| 6 | Message malformed |
| 7 | Handset issue |
| 8 | Recipient has no credit |
| 9 | Age verification failed |
| 10 | Delivery time expired |
Converted
Filter by converted messages: converted=true.
Multipart
Handles messages split into several parts (for example an SMS over 160 characters).
- Show only the first part:
multipart=firstpartonly - Show all parts of one message:
multipart={multipart.identifier}(best combined with the recipient filter)
Customgrouping
Filter on the custom groupings you set when sending.
| Property | Value Type | Description |
|---|---|---|
| customgrouping | String | Comma-separated string of custom groupings |
| customgrouping2 | String | Comma-separated string of custom groupings 2 |
| customgrouping3 | String | Comma-separated string of custom groupings 3 |
Example: customgrouping=reference1,reference2,reference3
SMSCampaign
Return every transaction that belongs to a certain SMS campaign.
| Property | Value Type | Description |
|---|---|---|
| smscampaign | String | Filter by SMS campaign identifier (GUID) |
Your campaign identifier is a GUID, found in the URL of the campaign edit view or in the CustomGrouping property of messages.
Omit
Skip properties you do not need. Omitting the aggregates also skips the count and sum step, which makes the response faster.
| Value | Skips |
|---|---|
| totalTransactions | The transaction count for the result set |
| totalprice | The price calculation for the result set |
| totaltariff | The tariff calculation for the result set |
| result | The paginated list of messages |
Example: omit=totalTransactions,totalprice,totaltariff
Pagination
Pagesize
| Property | Value Type | Description |
|---|---|---|
| pagesize | Integer | Number of items per page (default 1000, max 7500) |
Example: pagesize=10
Startat
| Property | Value Type | Description |
|---|---|---|
| startat | Integer | The starting page for pagination (default 1) |
Example: startat=7
Negate
Invert a filter. To pull every message to recipients outside the Netherlands, supply the filter and then negate its key:
?startdate=...&enddate=...&countryiso=nl&negate=countryiso
Extra resources
Alongside /transactions/, five lookups hand you the exact values the filters accept.
| Resource | Returns |
|---|---|
/transactions/countries | Every country with its ISO code |
/transactions/errorcodes | All error codes and their descriptions |
/transactions/mediums | All channels with their numeric medium id |
/transactions/operators | Operators with their MCC MNC identifiers |
/transactions/statuscodes | The six status codes and their labels |
The /transactions/statuscodes resource returns:
[
{ "id": 19, "description": "Sent" },
{ "id": 20, "description": "Delivered" },
{ "id": 21, "description": "Failed" },
{ "id": 22, "description": "Cancelled" },
{ "id": 37, "description": "Accepted" },
{ "id": 40, "description": "Rejected" }
]
Updated about 18 hours ago