Enabling API Access
To gain access to the API, you will need to generate an access key. To do this you must be logged into the platform securely, that is using 2FA. There will be an option from the Main Menu to generate an API key. You will need to enter your current password and a valid 2FA code. You will then be provided with an API key - please keep this securely as it can be used to access the billing platform under your identity. The key will only be shown once and cannot be recovered if you lose it - you may simply regenerate an API key at any time. Please note you may only have one API key at any time, so creating a new one will automatically invalidate any existing key, so you will need to update any systems using it.
API-Only Users
If you wish to integrate third-party systems to the platform, we recommend setting up a dedicated user for each system, to assist with access control and auditing. To do so, you can create an API-only user - do not give the user login access, then you will be able to generate an API key from the Expert Mode menu when viewing the account. Please note that only users with Primary data protection access have the ability to reset other users’ API keys.
When setting up an API-only user, make sure the user has at least read access to the API in their permissions. You should also grant appropriate customer access (All Customers, Account Manager, or Commission Holder) depending on which customers the integration needs to work with.
Accessing the API
Base URL
The API may be accessed from within your platform using the base URL of /backend/api/v1 using the same domain as you normally use (companyname.callstats.net) and must be accessed over HTTPS.
Authentication
You must present your API access key with every access of the API. This should be done using the bearer authentication scheme using the Authorization header, for example Authorization: Bearer MY_API_KEY . All requests will be logged, and your activity will be visible to other users of the platform, depending on their access level.
MIME Types
All requests should be JSON encoded using UTF-8 encoding, as will all responses. You should add suitable Accept headers to all requests and Content-Type and Content-Length headers where data are being sent to the platform.
HTTP Methods
You should use the appropriate HTTP method for your action: requests for data should use the GET method, updates to existing data should use PATCH, adding new data should use a POST as should performing an action on existing data.
HTTP Headers
As well as setting the required headers details above, we also recommend enabling compression by setting the Accept-Encoding header, for example Accept-Encoding: gzip . This can significantly reduce the size of the response.
Response Headers
The API may return the following headers in responses:
X-API-No-Changes
When you send an update request (PATCH) where all submitted field values match the current values, the API returns this header set to 1. This indicates that no changes were made to the resource.
This is useful for:
- Detecting when an update was unnecessary
- Avoiding redundant processing in your application
- Confirming data synchronisation status
Example Response Headers
HTTP/1.1 200 OK
Content-Type: application/json
X-API-No-Changes: 1
Note: The response body will still contain the full resource as normal.
Endpoints
Within the base URL you may access a specific endpoint (such as /customers) corresponding to resource you would like to access.
Resource Detail
You may request the details of a specific resource by using its ID (for example /customers/1234). All IDs are integers.
Listing Resources
For each endpoint you may issue a request to the bare endpoint (such as /customers) to list all resources of that type, or resources which depend on a different resource, for example numbers belonging to a specific customer (using /customers/1234/numbers). In either case, you may optionally pass parameters to limit the results.
There are some standard parameters which apply to the majority of resources. To obtain recently added or updated resources you may use the idAfter or updatedSince parameters - if you maintain a record of the highest seen ID you may pass this to retreive newer resources (with a higher ID). Similarly, if you maintain a record of when you last requested updates, you may use the updatedSince parameter to retrieve resources modified since then. The updatedSince parameter should be a time passed in ISO 8601 format. Please note that not all automated updates currently update the last-changed date for an resource, so this should not be relied upon during alpha testing.
Resources will be returned in ID order.
You may also paginate the results by using the limit and offest parameters.
Resource Expansion
The API supports expanding related resources using the expand parameter family. This allows you to fetch related data in a single request instead of making multiple API calls.
Usage
Add the relevant expand parameter to include associated resources in the response:
GET /customers/?expandNumbers
Example Response
[
{
"id": "123",
"name": "Acme Corp",
"numbers": [
{
"id": "456",
"number": "+1234567890",
"status": "active"
},
{
"id": "789",
"number": "+1234567891",
"status": "pending"
}
]
}
]
Notes
- Multiple expand parameters can be combined
- Expansion may increase response time
- By default, all expand parameters are
false
Filtering Expanded Data
The standard active parameter filters the main resource but doesn’t affect child objects when expanding. To filter expanded data, use the following parameters:
| Parameter | Type | Description |
|---|---|---|
numberActive | flag | When expanding numbers, only include active numbers |
serviceActive | flag | When expanding services, only include active services |
featureActive | flag | When expanding features, only include active features |
These parameters only take effect when combined with the relevant expand parameter. For example, featureActive=true has no effect unless you also include expandFeatures=true.
Example
GET /customers/123?expandNumbers=true&numberActive=true
This returns the customer with only their active numbers included in the response. Dropped numbers are excluded from the expanded data.
Combined Example
GET /customers/?expandNumbers=true&numberActive=true&expandFeatures=true&featureActive=true
This returns all customers with their active numbers and active features only.
Updating an Existing Resource
You may update an existing resource by issuing a PATCH request with the changes to the endpoint of the specific resource (for example /customers/1234). The current state of the resource will then be returned. Currently, a PUT request will be treated as a PATCH request, but this is subject to change.
Adding a New Resource
You may add a new resource by issuing a POST request to the endpoint of the resource (for example customers ). The current state of the resource will then be returned - this may differ from what was actually submitted as some processing may take place, for example adding default values. To add an resource linked to an existing resource (for example a number to a customer) POST it to the endpoint belonging to that resource (for example /customers/1234/numbers).
Performing Actions on Resources
Beyond standard CRUD operations (Create, Read, Update, Delete), many resources support actions - specific operations that perform business logic on existing resources. Actions allow you to trigger complex workflows, state changes, or calculations through simple API calls.
How Actions Work
Actions are invoked by making a POST request to a specific resource with an action parameter in the URL. Any parameters required by the action are passed in the JSON request body.
General Format:
POST /backend/api/v1/{resource}/{id}?action={action_name}
The action will be performed on the specified resource, and the updated resource will be returned in the response.
Request Structure
Example without parameters:
POST /backend/api/v1/customers/1234?action=generatePIN
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{}
Example with parameters:
POST /backend/api/v1/customers/1234?action=drop
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{
"status": "Ex-Customer",
"dateDrop": "2025-01-24",
"statusReason": "Customer requested cancellation"
}
Common Action Types
While actions are resource-specific, here are some common types you’ll encounter:
State Changes
drop- Deactivate or cancel a resourcereinstate- Reactivate a previously dropped resourceapprove/unapprove- Change approval statuscancel- Cancel a pending operation
Response Format
Unless otherwise stated, actions return the complete updated resource in JSON format, exactly as a GET request would:
{
"id": 1234,
"customerName": "Example Corp",
"statusID": 2,
"updatedDate": "2025-01-24",
"statusReason": "Customer requested cancellation",
// ... other fields
}
This allows you to immediately see the effect of the action without making a separate GET request.
Error Handling
If an action fails, you’ll receive an appropriate HTTP status code with error details:
{
"error": "Invalid Action",
"error_code": 400501,
"hint": "The action 'invalidAction' is not recognised for this resource"
}
Common error scenarios:
400501- Action not recognised for this resource type400502- Action failed to execute (check the hint for details)400503- Missing required parameters for the action403xxx- Permission denied (user lacks necessary access rights)409xxx- Conflict (resource is in wrong state for this action)
Best Practices
Check Resource State - Some actions are only valid in certain states (e.g., can’t reinstate an active customer)
Verify Permissions - Actions often require specific permissions beyond basic read/write access
Handle Responses - Always check the returned resource to confirm the action succeeded as expected
Use Idempotent Actions - Where possible, actions should be safe to retry (e.g., generating a PIN replaces the existing one)
Validate Parameters - Ensure all required parameters are provided with valid values before making the request
Monitor for Side Effects - Some actions may trigger additional processes (emails, webhooks, etc.)
Discovering Available Actions
To find what actions are available for a specific resource:
- Check the resource-specific documentation in the Endpoints section
- Look for an “Actions” subsection listing all available actions
- Each action will document its parameters, permissions, and effects
Note that not all resources support actions - they’re typically available for core business objects like customers, invoices, and payments.
File Uploads
Some resources support file attachments. Files can be uploaded using two different methods depending on your requirements:
- JSON with Base64 encoding - Consistent with the standard API format
- Multipart form data - More efficient for larger files
Both methods support optional file verification and compression to ensure data integrity and reduce bandwidth usage.
JSON Method (Base64)
For consistency with the rest of the API, you can upload files by encoding them as Base64 and including them in a standard JSON request. This method supports both creating new attachments (POST) and updating existing ones (PATCH).
Example Request (Create)
POST /backend/api/v1/notes/1234/noteAttachments
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{
"fileName": "order-details.pdf",
"_fileContent": "JVBERi0xLjQNCiXi48/TDQo...",
"description": "Customer order specifications",
"_compression": "zstd",
"_fileSize": 1048576,
"_fileHash": "da39a3ee5e6b4b0d3255bf...",
"_mimeType": "application/pdf"
}
Example Request (Update)
PATCH /backend/api/v1/noteAttachments/5678
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{
"fileName": "revised-order-details.pdf",
"_fileContent": "JVBERi0xLjQNCiXi48/TDQo...",
"description": "Updated customer order specifications",
"_fileSize": 1253872,
"_fileHash": "f3b8c2a4d5e1a9b8c7d6e5f4..."
}
Multipart Method
For larger files or when bandwidth efficiency is important, you can use multipart form data. The JSON data is sent in a data field, with the file sent as binary data. Note that multipart uploads are only supported for creating new resources (POST).
Example Request
POST /backend/api/v1/notes/1234/noteAttachments
Authorization: Bearer YOUR_API_KEY
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="data"
Content-Type: application/json
{
"fileName": "order-details.pdf",
"description": "Customer order specifications",
"_fileSize": 1048576,
"_fileHash": "da39a3ee5e6b4b0d3255bf...",
"_mimeType": "application/pdf"
}
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="order-details.pdf"
Content-Type: application/pdf
[Binary file data]
------WebKitFormBoundary7MA4YWxkTrZu0gW--
File Verification Parameters
The following optional parameters can be included to verify file integrity:
_fileSize (integer)
Expected file size in bytes of the original file (before compression). If provided, the uploaded file size will be verified against this value and the request will be rejected if they don’t match.
_fileHash (string)
Expected SHA1 hash of the original file content (before compression). If provided, the SHA1 hash of the uploaded file will be calculated and compared. The request will be rejected if the hashes don’t match.
_mimeType (string)
Override the automatically detected MIME type. If provided, this value will be used instead of auto-detection. No verification is performed - the API trusts that you know the correct MIME type for your file.
Compression Support
For the JSON method, you can compress file content before Base64 encoding using the _compression parameter:
Supported compression schemes:
gzip- Good compression with wide compatibilityzlib- Common in PHP applicationsbzip2- Higher compression ratiozstd- Excellent compression and speed balance (recommended)
Example with compression
{
"fileName": "large-order-summary.xlsx",
"_fileContent": "eJzT0yMa...",
"_compression": "zstd",
"description": "Compressed quarterly order summary"
}
Example Response
Both upload methods return the same response format:
{
"id": 5678,
"noteID": 1234,
"storedFileID":9876,
"description": "Customer order summary"
}
Downloading Attachments
When accessing an existing attachment (e.g., GET /backend/api/v1/noteAttachments/5678), the response depends on the Accept header:
Getting attachment metadata (JSON)
GET /backend/api/v1/noteAttachments/5678
Authorization: Bearer YOUR_API_KEY
Accept: application/json
{
"id": 5678,
"noteID": 1234,
"storedFileID":9876,
"description": "Customer order summary"
}
Note: When listing resources with attached files, you can use ?expandStoredFiles to include file metadata (name, size, hash) in the response without needing separate requests.
Downloading the file content
GET /backend/api/v1/noteAttachments/5678
Authorization: Bearer YOUR_API_KEY
Accept: application/octet-stream
Returns the binary file content with appropriate headers for download.
Accept header behaviour:
Accept: application/jsonor noAcceptheader → Returns JSON metadataAccept: application/octet-stream,Accept: */*,Accept: application/pdf, etc. → Returns file content
This allows API clients to easily choose between getting information about the attachment or downloading the actual file.
Notes
- Method choice: Use JSON for consistency and smaller files. Use multipart for larger files or when bandwidth is a concern.
- Creating vs. updating: Multipart uploads only support creating new attachments (POST). Use the JSON method for updating existing attachments (PATCH).
- File verification: File size and hash verification are performed on the original file content, before any compression is applied.
- File size limits: Check with your platform administrator for any file size restrictions.
- Storage limitations: Uploaded files will count against your storage limits.
- Compression: Only applies to the JSON method. Multipart uploads are already efficient for binary data.
- Verification: File verification parameters are optional but recommended for critical uploads.
- Security: All file uploads are logged and subject to the same authentication and authorization controls as other API operations.
Need a billing platform with a comprehensive REST API for custom integrations? discover SAFE Billing Platform's API capabilities for telecommunications billing automation