All API requests should be made to the following base URL:
https://api.texttorrent.comExample Complete URL:
https://api.texttorrent.com/api/v1/user/auth/me/api/v1The Text Torrent API uses standard HTTP response codes to indicate the success or failure of an API request.
| Code | Status | Description | Use Case |
|---|---|---|---|
200 | OK | The request was successful | GET, PUT, PATCH requests that successfully retrieve or update data |
201 | Created | The resource was successfully created | POST requests that create new resources (contacts, messages, sub-accounts) |
| Code | Status | Description | Common Causes |
|---|---|---|---|
400 | Bad Request | The request was malformed or invalid | Missing required parameters, invalid data format, malformed JSON |
401 | Unauthorized | Authentication failed or credentials are missing | Missing or invalid X-API-SID or X-API-PUBLIC-KEY headers |
403 | Forbidden | Valid authentication but insufficient permissions | Trying to access resources you don't have permission for, disabled API access |
404 | Not Found | The requested resource does not exist | Invalid endpoint URL, resource ID doesn't exist, deleted resource |
422 | Unprocessable Entity | The request was well-formed but contains semantic errors | Validation errors, insufficient credits, business logic violations |
429 | Too Many Requests | Rate limit exceeded | More than 60 requests per minute from the same API key |
| Code | Status | Description | What to Do |
|---|---|---|---|
500 | Internal Server Error | An error occurred on the server | Retry the request after a few moments. Contact support if the issue persists |
503 | Service Unavailable | The server is temporarily unavailable | Wait and retry. The service may be under maintenance |
All API responses follow a consistent JSON structure:
Success Response Format:
1{
2 "code": 200,
3 "success": true,
4 "message": "Operation completed successfully",
5 "data": {
6 // Response data here
7 },
8 "errors": null
9}Error Response Format:
1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "field_name": [
8 "Error message for this field"
9 ]
10 }
11}1. Insufficient Credits (422):
1{
2 "code": 422,
3 "success": false,
4 "message": "You do not have enough credits to perform this action.",
5 "data": null,
6 "errors": null
7}2. Resource Not Found (404):
1{
2 "code": 404,
3 "success": false,
4 "message": "Sub Account Not Found",
5 "data": null,
6 "errors": []
7}3. Trial Limitation (422):
1{
2 "code": 422,
3 "success": false,
4 "message": "You cannot purchase numbers while on a trial subscription. Please upgrade to a paid plan.",
5 "data": null,
6 "errors": null
7}4. Rate Limit Exceeded (429):
1{
2 "code": 429,
3 "success": false,
4 "message": "Too Many Requests. Please try again in 60 seconds.",
5 "data": null,
6 "errors": null
7}success field to determine if the request was successfulcode field for programmatic error handlingmessage field to users for user-friendly error messageserrors object for detailed validation error messagesThe Text Torrent API uses API Key authentication to secure all API requests.
All API requests must be authenticated using your API credentials by including X-API-SID andX-API-PUBLIC-KEY headers. Unauthenticated requests will receive a 401 Unauthorizedresponse.
Rate Limiting: There is a rate limit of 60 requests per minute. If you exceed this limit, you will receive a 429 Too Many Requests status with a Retry-After header indicating when you can try again.
All API requests must be authenticated using two headers: X-API-SID andX-API-PUBLIC-KEY. These credentials are unique to your account and provide secure access to the Text Torrent API.
Your API credentials (SID and Public Key) are generated automatically when your account is created. You can find them in your account settings or API dashboard.
SID....................................)PK.....................................)Include both X-API-SID and X-API-PUBLIC-KEY headers in all API requests:
Request Headers:
1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/jsonThe /me endpoint returns information about the authenticated user.
Endpoint:
GET https://api.texttorrent.com/api/v1/user/auth/meExample cURL Request:
1curl -X GET https://api.texttorrent.com/api/v1/user/auth/me
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json""Success Response (200 OK):
1{
2 "code": 200,
3 "success": true,
4 "message": "Successfully fetched user information",
5 "data": {
6 "id": 1,
7 "first_name": "John",
8 "last_name": "Doe",
9 "username": "johndoe",
10 "email": "john@example.com",
11 "credits": 1000,
12 "plan": {
13 "package_name": "pro",
14 "status": "Active"
15 }
16 }
17}Example in PHP:
1$sid = 'SID....................................';
2$publicKey = 'PK.....................................';
3
4$ch = curl_init('https://api.texttorrent.com/api/v1/user/auth/me');
5curl_setopt($ch, CURLOPT_HTTPHEADER, [
6 'X-API-SID: ' . $sid,
7 'X-API-PUBLIC-KEY: ' . $publicKey,
8 'Content-Type: application/json',
9 'Accept: application/json'
10]);
11curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
12
13$response = curl_exec($ch);
14curl_close($ch);
15
16$data = json_decode($response, true);Example in JavaScript:
1const sid = 'SID....................................';
2const publicKey = 'PK.....................................';
3
4fetch('https://api.texttorrent.com/api/v1/user/auth/me', {
5 method: 'GET',
6 headers: {
7 'X-API-SID': sid,
8 'X-API-PUBLIC-KEY': publicKey,
9 'Content-Type': 'application/json',
10 'Accept': 'application/json'
11 }
12})
13.then(response => response.json())
14.then(data => console.log(data))
15.catch(error => console.error('Error:', error));This section describes the critical X-ACT-AS-USER header that allows parent users to perform API requests on behalf of their sub accounts. Ensure you understand the authentication flow and error handling before implementing this feature.
Parent users can authenticate as their sub accounts by including theX-ACT-AS-USER header in addition to the standard authentication headers. This allows parent users to perform actions on behalf of their sub accounts.
X-API-SID and X-API-PUBLIC-KEY)X-ACT-AS-USER header with the sub account's email addressInclude the following headers to login as a sub user:
1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3X-ACT-AS-USER: subuser@example.com
4Content-Type: application/json
5Accept: application/jsonThe following example demonstrates how to fetch user information as a sub account:
cURL Request:
1curl -X GET https://api.texttorrent.com/api/v1/user/auth/me
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "X-ACT-AS-USER: subuser@example.com"
5 -H "Content-Type: application/json"
6 -H "Accept: application/json"PHP Example:
1$sid = 'SID....................................';
2$publicKey = 'PK.....................................';
3$subUserEmail = 'subuser@example.com';
4
5$ch = curl_init('https://api.texttorrent.com/api/v1/user/auth/me');
6curl_setopt($ch, CURLOPT_HTTPHEADER, [
7 'X-API-SID: ' . $sid,
8 'X-API-PUBLIC-KEY: ' . $publicKey,
9 'X-ACT-AS-USER: ' . $subUserEmail,
10 'Content-Type: application/json',
11 'Accept: application/json'
12]);
13curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
14
15$response = curl_exec($ch);
16curl_close($ch);
17
18$data = json_decode($response, true);JavaScript Example:
1const sid = 'SID....................................';
2const publicKey = 'PK.....................................';
3const subUserEmail = 'subuser@example.com';
4
5fetch('https://api.texttorrent.com/api/v1/user/auth/me', {
6 method: 'GET',
7 headers: {
8 'X-API-SID': sid,
9 'X-API-PUBLIC-KEY': publicKey,
10 'X-ACT-AS-USER': subUserEmail,
11 'Content-Type': 'application/json',
12 'Accept': 'application/json'
13 }
14})
15.then(response => response.json())
16.then(data => console.log(data))
17.catch(error => console.error('Error:', error));Success Response (200 OK):
1{
2 "code": 200,
3 "success": true,
4 "message": "Successfully fetched user information",
5 "data": {
6 "id": 5,
7 "first_name": "Jane",
8 "last_name": "Smith",
9 "username": "janesmith",
10 "email": "subuser@example.com",
11 "credits": 500,
12 "plan": {
13 "package_name": "basic",
14 "status": "Active"
15 }
16 }
17}X-ACT-AS-USER email must belong to the parent user's sub accountX-ACT-AS-USER are logged under the sub account for complianceX-ACT-AS-USER header must contain a valid sub account email address401 Unauthorized response will be returnedX-ACT-AS-USER are logged under the sub accountX-ACT-AS-USER header is optional. If not provided, the API will operate as the parent userIf authentication fails, the API will return appropriate HTTP status codes and error messages:
| Status Code | Error | Description |
|---|---|---|
| 401 | Unauthorized | No authentication credentials provided, invalid credentials, or invalid sub-user email in X-ACT-AS-USER header |
| 403 | Forbidden | Valid credentials but insufficient permissions for the requested resource |
| 429 | Too Many Requests | Rate limit exceeded (60 requests per minute) |
401 Unauthorized Response Example:
1{
2 "status": false,
3 "message": "Unauthorized",
4 "errors": []
5}401 Invalid Sub-User Response Example:
1{
2 "status": false,
3 "message": "The provided user is not your sub account",
4 "errors": []
5}429 Rate Limit Response Example:
1{
2 "status": false,
3 "message": "Too Many Requests. Please try again in 60 seconds.",
4 "errors": []
5}403 Forbidden Response Example:
1{
2 "status": false,
3 "message": "Unauthorized",
4 "errors": []
5}The Contact Module provides a comprehensive set of APIs to manage all aspects of your contacts, including list management, individual contact operations, validation, folders, notes, and more. This module is the foundation for organizing and maintaining your contact database.
Module Capabilities:
The Contact List Management API provides comprehensive endpoints to organize and manage your contact lists. Contact lists help you group contacts for better organization and targeted messaging campaigns.
Key Features:
Retrieve all contact lists associated with your account, including the total count of non-blacklisted contacts in each list.
GET: /api/v1/contact/list1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
email | string | No | Base64 encoded email address of a sub-user. Main accounts can use this to retrieve contact lists belonging to their sub-users. |
email parameter. If the provided email does not belong to a sub-user associated with your main account, a 403 Forbidden response will be returned.1curl -X GET "https://api.texttorrent.com/api/v1/contact/list"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1# First, encode the sub-user's email in Base64
2# Example: subuser@example.com → c3VidXNlckBleGFtcGxlLmNvbQ==
3
4curl -X GET "https://api.texttorrent.com/api/v1/contact/list?email=c3VidXNlckBleGFtcGxlLmNvbQ=="
5 -H "X-API-SID: SID...................................."
6 -H "X-API-PUBLIC-KEY: PK....................................."
7 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Contact list retrieved",
5 "data": [
6 {
7 "id": 45,
8 "user_id": 1,
9 "name": "VIP Customers",
10 "bookmarked": 1,
11 "created_at": "2025-01-15T10:30:00.000000Z",
12 "total_contacts": 150
13 },
14 {
15 "id": 44,
16 "user_id": 1,
17 "name": "Newsletter Subscribers",
18 "bookmarked": 0,
19 "created_at": "2025-01-14T14:22:00.000000Z",
20 "total_contacts": 523
21 },
22 {
23 "id": 43,
24 "user_id": 1,
25 "name": "Event Attendees",
26 "bookmarked": 0,
27 "created_at": "2025-01-10T09:15:00.000000Z",
28 "total_contacts": 89
29 }
30 ],
31 "errors": null
32}1{
2 "code": 403,
3 "success": false,
4 "message": "Unauthorized access",
5 "data": null
6}| Field | Type | Description |
|---|---|---|
id | integer | Unique identifier for the contact list |
user_id | integer | ID of the user who owns this list |
name | string | Name of the contact list (max 15 characters) |
bookmarked | boolean | Whether the list is bookmarked/favorited (1 = true, 0 = false) |
created_at | string | Timestamp when the list was created (ISO 8601 format) |
total_contacts | integer | Total number of non-blacklisted contacts in this list |
total_contactsemail parameter is providedemail parameterecho -n "subuser@example.com" | base64 (Linux/Mac) or use an online Base64 encoderCreate a new contact list to organize your contacts. List names must be unique within your account and cannot exceed 15 characters.
POST: /api/v1/contact/list/add/bookmark1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Name of the contact list (max 15 characters, must be unique) |
1curl -X POST "https://api.texttorrent.com/api/v1/contact/list/add/bookmark"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "name": "New Leads 2025"
8 }'1{
2 "code": 200,
3 "success": true,
4 "message": "Contact list created successfully",
5 "data": {
6 "id": 46,
7 "user_id": 1,
8 "name": "New Leads 2025",
9 "bookmarked": 0,
10 "created_at": "2025-10-17T12:34:56.000000Z",
11 "updated_at": "2025-10-17T12:34:56.000000Z"
12 },
13 "errors": null
14}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "name": [
8 "The name field is required."
9 ]
10 }
11}Update the name of an existing contact list. The new name must be unique within your account and cannot exceed 15 characters.
PUT: /api/v1/contact/list/update/bookmark1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
list_id | integer | Yes | ID of the contact list to update (must exist) |
name | string | Yes | New name for the contact list (max 15 characters, must be unique) |
1curl -X PUT "https://api.texttorrent.com/api/v1/contact/list/update/bookmark" -H "X-API-SID: SID...................................." -H "X-API-PUBLIC-KEY: PK....................................." -H "Content-Type: application/json" -H "Accept: application/json" -d '{
2 "list_id": 46,
3 "name": "Hot Leads 2025"
4 }'1{
2 "code": 200,
3 "success": true,
4 "message": "Contact list updated successfully",
5 "data": {
6 "id": 46,
7 "user_id": 1,
8 "name": "Hot Leads 2025",
9 "bookmarked": 0,
10 "created_at": "2025-10-17T12:34:56.000000Z",
11 "updated_at": "2025-10-17T13:45:22.000000Z"
12 },
13 "errors": null
14}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "list_id": [
8 "The selected list id is invalid."
9 ]
10 }
11}Toggle the bookmark/favorite status of a contact list. Bookmarked lists can be displayed prominently in your application for quick access to frequently used lists.
PUT: /api/v1/contact/list/toggle/bookmark/{id}1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | ID of the contact list to toggle |
1curl -X PUT "https://api.texttorrent.com/api/v1/contact/list/toggle/bookmark/46"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Contact list updated successfully",
5 "data": {
6 "id": 46,
7 "user_id": 1,
8 "name": "Hot Leads 2025",
9 "bookmarked": 1,
10 "created_at": "2025-10-17T12:34:56.000000Z",
11 "updated_at": "2025-10-17T13:45:22.000000Z"
12 },
13 "errors": null
14}1{
2 "code": 400,
3 "success": false,
4 "message": "Failed to update contact list",
5 "data": null,
6 "errors": []
7}bookmarked is 0, it will be set to 1 (bookmarked)bookmarked is 1, it will be set to 0 (not bookmarked)Delete a specific contact list and all its associated contacts, or delete all contacts of a specific type (default, blacklisted, or unlisted). This operation is irreversible.
DELETE: /api/v1/contact/list/delete/bookmark1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
list_id | integer | Conditional | ID of the contact list to delete (required if type is not provided) |
type | string | Conditional | Type of contacts to delete: default, blacklisted, orunlisted |
list_id to delete a specific list and all its contacts1curl -X DELETE "https://api.texttorrent.com/api/v1/contact/list/delete/bookmark"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "list_id": 46
8 }'1{
2 "code": 200,
3 "success": true,
4 "message": "Contact list deleted successfully",
5 "data": null,
6 "errors": null
7}1curl -X DELETE "https://api.texttorrent.com/api/v1/contact/list/delete/bookmark"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "type": "default"
8 }'1{
2 "code": 200,
3 "success": true,
4 "message": "All default contacts deleted successfully",
5 "data": null,
6 "errors": null
7}1curl -X DELETE "https://api.texttorrent.com/api/v1/contact/list/delete/bookmark"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "type": "blacklisted"
8 }'1{
2 "code": 200,
3 "success": true,
4 "message": "All blacklisted contacts deleted successfully",
5 "data": null,
6 "errors": null
7}1curl -X DELETE "https://api.texttorrent.com/api/v1/contact/list/delete/bookmark"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "type": "unlisted"
8 }'1{
2 "code": 200,
3 "success": true,
4 "message": "All unlisted contacts deleted successfully",
5 "data": null,
6 "errors": null
7}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "list_id": [
8 "The selected list id is invalid."
9 ]
10 }
11}list_id OR type, but not necessarily bothtype = "blacklisted", contacts blacklisted by you or blacklisted by your sub-accounts will be deletedRetrieve all contacts within a specific contact list with support for pagination, search, and filtering. This endpoint is perfect for displaying contacts in your application with advanced query capabilities.
GET: /api/v1/contact/{listId}/contacts1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
listId | integer | Yes | ID of the contact list to retrieve contacts from |
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
limit | integer | No | 10 | Number of contacts per page (pagination) |
search | string | No | - | Search term to filter contacts by first name, last name, or phone number |
sort_list_id | string/integer | No | - | Filter by list type: specific list ID, unlisted, or blacklisted |
page | integer | No | 1 | Page number for pagination |
1curl -X GET "https://api.texttorrent.com/api/v1/contact/46/contacts"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1curl -X GET "https://api.texttorrent.com/api/v1/contact/46/contacts?limit=25&page=2"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1curl -X GET "https://api.texttorrent.com/api/v1/contact/46/contacts?search=john"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1curl -X GET "https://api.texttorrent.com/api/v1/contact/46/contacts?sort_list_id=unlisted"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1curl -X GET "https://api.texttorrent.com/api/v1/contact/46/contacts?sort_list_id=blacklisted"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Contacts retrieved",
5 "data": {
6 "current_page": 1,
7 "data": [
8 {
9 "id": 1234,
10 "first_name": "John",
11 "last_name": "Doe",
12 "number": "+19292223344",
13 "email": "john.doe@example.com",
14 "company": "Tech Solutions Inc",
15 "valid": 1,
16 "validation_process": 1,
17 "folder_id": null
18 },
19 {
20 "id": 1235,
21 "first_name": "Jane",
22 "last_name": "Smith",
23 "number": "+19293334455",
24 "email": "jane.smith@example.com",
25 "company": "Marketing Pro",
26 "valid": 1,
27 "validation_process": 1,
28 "folder_id": 5
29 }
30 ],
31 "first_page_url": "https://api.texttorrent.com/api/v1/contact/46/contacts?page=1",
32 "from": 1,
33 "last_page": 5,
34 "last_page_url": "https://api.texttorrent.com/api/v1/contact/46/contacts?page=5",
35 "links": [
36 {
37 "url": null,
38 "label": "« Previous",
39 "active": false
40 },
41 {
42 "url": "https://api.texttorrent.com/api/v1/contact/46/contacts?page=1",
43 "label": "1",
44 "active": true
45 },
46 {
47 "url": "https://api.texttorrent.com/api/v1/contact/46/contacts?page=2",
48 "label": "2",
49 "active": false
50 }
51 ],
52 "next_page_url": "https://api.texttorrent.com/api/v1/contact/46/contacts?page=2",
53 "path": "https://api.texttorrent.com/api/v1/contact/46/contacts",
54 "per_page": 10,
55 "prev_page_url": null,
56 "to": 10,
57 "total": 50
58 },
59 "errors": null
60}| Field | Type | Description |
|---|---|---|
id | integer | Unique identifier for the contact |
first_name | string | Contact's first name |
last_name | string | Contact's last name |
number | string | Contact's phone number |
email | string | Contact's email address (nullable) |
company | string | Contact's company name (nullable) |
valid | boolean | Whether the Contact's phone number is valid (1 = valid, 0 = invalid) |
validation_process | boolean | Whether the contact has been through validation (1 = yes, 0 = no) |
folder_id | integer | ID of the folder this contact belongs to (nullable) |
first_name, last_name, and number fieldscurrent_page: Current page numberlast_page: Total number of pagesper_page: Number of items per pagetotal: Total number of contacts matching the queryfrom and to: Range of items on current pageThe Contact Number Management API provides endpoints to validate phone numbers, verify contact information, and retrieve detailed contact data. These endpoints help ensure data quality and maintain accurate contact records.
Key Features:
This endpoint performs a pre-validation check to calculate how many credits will be required to validate the selected contact numbers. It identifies which contacts have already been validated and calculates the cost before proceeding with actual validation.
POST: /api/v1/contact/number/verify/confirmation1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
contact_id | array | Yes | Array of contact IDs to verify (each ID must exist in contacts table) |
1curl -X POST "https://api.texttorrent.com/api/v1/contact/number/verify/confirmation"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "contact_id": [1234, 1235, 1236, 1237, 1238]
8 }'1{
2 "code": 200,
3 "success": true,
4 "message": "Number validation result",
5 "data": {
6 "total_numbers_selected": 5,
7 "already_validated": 2,
8 "available_validation": 3,
9 "credits": 997
10 },
11 "errors": null
12}| Field | Type | Description |
|---|---|---|
total_numbers_selected | integer | Total number of contacts selected for verification |
already_validated | integer | Count of contacts that have already been validated |
available_validation | integer | Count of contacts that need validation (not yet validated) |
credits | integer | Your remaining credits after validation (each validation costs 1 credit) |
1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "contact_id": [
8 "The contact id field is required."
9 ]
10 }
11}Validates selected phone numbers to verify their validity, detect mobile vs landline, and clean up your contact database. This process requires an active subscription and sufficient credits.
POST: /api/v1/contact/number/validate1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
contact_ids | array | Yes | Array of contact IDs to validate (each ID must exist) |
validator_credits | number | Yes | Total credits required for validation (minimum: 0) |
available_validation | integer | Yes | Number of contacts available for validation (from verify endpoint) |
1curl -X POST "https://api.texttorrent.com/api/v1/contact/number/validate"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "contact_ids": [1234, 1235, 1236],
8 "validator_credits": 3,
9 "available_validation": 3
10 }'1{
2 "code": 200,
3 "success": true,
4 "message": "Number validation started successfully",
5 "data": {
6 "total_numbers_selected": 3,
7 "available_validation": 3,
8 "credits": 997
9 },
10 "errors": null
11}| Field | Type | Description |
|---|---|---|
total_numbers_selected | integer | Total number of contacts selected for validation |
available_validation | integer | Number of contacts that will be validated |
credits | integer | Your remaining credits after validation |
1{
2 "code": 422,
3 "success": false,
4 "message": "You do not have enough credits to perform this action.",
5 "data": null,
6 "errors": []
7}1{
2 "code": 422,
3 "success": false,
4 "message": "You do not have an active subscription to perform this action.",
5 "data": null,
6 "errors": []
7}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "contact_ids": [
8 "The contact ids field is required."
9 ]
10 }
11}/verify/confirmation endpoint to get credit calculation1// Step 1: Verify numbers and get cost estimate
2const verifyResponse = await fetch('https://api.texttorrent.com/api/v1/contact/number/verify/confirmation', {
3 method: 'POST',
4 headers: {
5 'X-API-SID': 'SID....................................',
6 'X-API-PUBLIC-KEY': 'PK.....................................',
7 'Content-Type': 'application/json'
8 },
9 body: JSON.stringify({
10 contact_id: [1234, 1235, 1236]
11 })
12});
13
14const verifyData = await verifyResponse.json();
15console.log('Credits after validation:', verifyData.data.credits);
16console.log('Contacts to validate:', verifyData.data.available_validation);
17
18// Step 2: If user confirms, proceed with validation
19if (confirm('Validate "$"{verifyData.data.available_validation} contacts for "$"{verifyData.data.available_validation} credits?')) {
20 const validateResponse = await fetch('https://api.texttorrent.com/api/v1/contact/number/validate', {
21 method: 'POST',
22 headers: {
23 'X-API-SID': 'SID....................................',
24 'X-API-PUBLIC-KEY': 'PK.....................................',
25 'Content-Type': 'application/json'
26 },
27 body: JSON.stringify({
28 contact_ids: [1234, 1235, 1236],
29 validator_credits: verifyData.data.available_validation,
30 available_validation: verifyData.data.available_validation
31 })
32 });
33
34 const validateData = await validateResponse.json();
35 console.log('Validation started:', validateData.message);
36}Retrieve detailed information about a specific contact, including personal information, associated notes, and formatted display data. This endpoint provides a complete view of a Contact's profile.
GET: /api/v1/contact/number/{contactId}1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
contactId | integer | Yes | Unique identifier of the contact to retrieve |
1curl -X GET "https://api.texttorrent.com/api/v1/contact/number/1234"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Contact details retrieved successfully",
5 "data": {
6 "id": 1234,
7 "first_name": "John",
8 "last_name": "Doe",
9 "full_name": "John Doe",
10 "imgWords": "JD",
11 "email": "john.doe@example.com",
12 "number": "+19292223344",
13 "company": "Tech Solutions Inc",
14 "notes": [
15 {
16 "id": 1,
17 "contact_id": 1234,
18 "note": "Follow up on product demo",
19 "created_at": "2025-10-15T10:30:00.000000Z",
20 "updated_at": "2025-10-15T10:30:00.000000Z"
21 },
22 {
23 "id": 2,
24 "contact_id": 1234,
25 "note": "Interested in enterprise plan",
26 "created_at": "2025-10-16T14:20:00.000000Z",
27 "updated_at": "2025-10-16T14:20:00.000000Z"
28 }
29 ]
30 },
31 "errors": null
32}| Field | Type | Description |
|---|---|---|
id | integer | Unique identifier for the contact |
first_name | string | Contact's first name |
last_name | string | Contact's last name |
full_name | string | Contact's full name (first + last) |
imgWords | string | Initials for avatar display (first letter of first + last name) |
email | string | Contact's email address (nullable) |
number | string | Contact's phone number with country code |
company | string | Contact's company name (nullable) |
notes | array | Array of note objects associated with this contact |
1{
2 "code": 200,
3 "success": true,
4 "message": "Contact not found",
5 "data": null,
6 "errors": null
7}Each note object in the notes array contains:
| Field | Type | Description |
|---|---|---|
id | integer | Unique identifier for the note |
contact_id | integer | ID of the contact this note belongs to |
note | string | The note text content |
created_at | string | Timestamp when note was created (ISO 8601) |
updated_at | string | Timestamp when note was last updated (ISO 8601) |
imgWords field)null data if contact doesn't exist (not a 404 error)imgWords is pre-calculated for avatar display (e.g., "JD" for "John Doe")phone field (not number field in database)The Contact Notes API allows you to add, update, and delete notes associated with contacts. Notes help you track interactions, important information, and follow-up tasks for each contact.
Key Features:
Create a new note for a specific contact. Notes can contain important information, follow-up reminders, conversation summaries, or any other relevant details about the contact.
POST: /api/v1/contact/number/note/add1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
contact_id | integer | Yes | ID of the contact to add note to (must exist in contacts table) |
note | string | Yes | The note content (max 2000 characters) |
1curl -X POST "https://api.texttorrent.com/api/v1/contact/number/note/add"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "contact_id": 1234,
8 "note": "Follow up on product demo scheduled for next week. Customer interested in enterprise plan."
9 }'1{
2 "code": 201,
3 "success": true,
4 "message": "Note added successfully",
5 "data": {
6 "id": 567,
7 "contact_id": 1234,
8 "note": "Follow up on product demo scheduled for next week. Customer interested in enterprise plan.",
9 "created_at": "2025-10-17T14:30:22.000000Z",
10 "updated_at": "2025-10-17T14:30:22.000000Z"
11 },
12 "errors": null
13}| Field | Type | Description |
|---|---|---|
id | integer | Unique identifier for the note |
contact_id | integer | ID of the contact this note belongs to |
note | string | The note content |
created_at | string | Timestamp when the note was created (ISO 8601 format) |
updated_at | string | Timestamp when the note was last updated (ISO 8601 format) |
1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "contact_id": [
8 "The contact id field is required."
9 ],
10 "note": [
11 "The note field is required."
12 ]
13 }
14}1async function addContactNote(contactId, noteText) {
2 const response = await fetch('https://api.texttorrent.com/api/v1/contact/number/note/add', {
3 method: 'POST',
4 headers: {
5 'X-API-SID': 'SID....................................',
6 'X-API-PUBLIC-KEY': 'PK.....................................',
7 'Content-Type': 'application/json',
8 'Accept': 'application/json'
9 },
10 body: JSON.stringify({
11 contact_id: contactId,
12 note: noteText
13 })
14 });
15
16 const data = await response.json();
17
18 if (data.success) {
19 console.log('Note added:', data.data);
20 return data.data;
21 } else {
22 console.error('Error:', data.message);
23 throw new Error(data.message);
24 }
25}Update the content of an existing contact note. This allows you to modify or add information to previously created notes.
PUT: /api/v1/contact/number/note/update1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
contact_note_id | integer | Yes | ID of the note to update (must exist in contact_notes table) |
note | string | Yes | The updated note content (max 2000 characters) |
1curl -X PUT "https://api.texttorrent.com/api/v1/contact/number/note/update"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "contact_note_id": 567,
8 "note": "Product demo completed successfully. Customer confirmed interest in enterprise plan. Scheduled follow-up for contract review on Oct 25."
9 }'1{
2 "code": 200,
3 "success": true,
4 "message": "Note updated successfully",
5 "data": null,
6 "errors": null
7}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "contact_note_id": [
8 "The selected contact note id is invalid."
9 ]
10 }
11}1{
2 "code": 400,
3 "success": false,
4 "message": "Failed to update note",
5 "data": null,
6 "errors": []
7}GET /contact/number/"$"{id} updated_at timestamp is automatically updated1$noteId = 567;
2$updatedNote = 'Product demo completed successfully...';
3
4$ch = curl_init('https://api.texttorrent.com/api/v1/contact/number/note/update');
5curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
6curl_setopt($ch, CURLOPT_HTTPHEADER, [
7 'X-API-SID: SID....................................',
8 'X-API-PUBLIC-KEY: PK.....................................',
9 'Content-Type: application/json',
10 'Accept: application/json'
11]);
12curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
13 'contact_note_id' => $noteId,
14 'note' => $updatedNote
15]));
16curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
17
18$response = curl_exec($ch);
19curl_close($ch);
20
21$data = json_decode($response, true);
22if ($data['success']) {
23 echo 'Note updated successfully';
24}Permanently delete a contact note. This operation cannot be undone, so use it carefully.
DELETE: /api/v1/contact/number/note/{id}1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | ID of the note to delete |
1curl -X DELETE "https://api.texttorrent.com/api/v1/contact/number/note/567"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Note deleted successfully",
5 "data": null,
6 "errors": null
7}1{
2 "code": 400,
3 "success": false,
4 "message": "Failed to delete note",
5 "data": null,
6 "errors": []
7}}1async function deleteContactNote(noteId) {
2 // Show confirmation dialog
3 if (!confirm('Are you sure you want to delete this note?')) {
4 return;
5 }
6
7 const response = await fetch('https://api.texttorrent.com/api/v1/contact/number/note/"$"{noteId}', {
8 method: 'DELETE',
9 headers: {
10 'X-API-SID': 'SID....................................',
11 'X-API-PUBLIC-KEY': 'PK.....................................',
12 'Accept': 'application/json'
13 }
14 });
15
16 const data = await response.json();
17
18 if (data.success) {
19 console.log('Note deleted successfully');
20 // Refresh the contact details to show updated notes list
21 await refreshContactDetails();
22 } else {
23 console.error('Error:', data.message);
24 alert('Failed to delete note');
25 }
26}
27
28// Usage
29deleteContactNote(567)Each note supports up to 2000 characters. If you need to store more information:
Notes are automatically included when you call the Get Contact Details endpoint
GET: /api/v1/contact/number/{contactId}notes array.1// 1. Add a new note
2const newNote = await addContactNote(1234, 'Initial contact - interested in demo');
3
4// 2. Later, update the note with more details
5await updateContactNote(newNote.id, 'Initial contact - demo scheduled for Oct 20, 2PM EST');
6
7// 3. Retrieve all notes for the contact
8const contact = await getContactDetails(1234);
9console.log('All notes:', contact.notes);
10
11// 4. If needed, delete outdated notes
12await deleteContactNote(oldNoteId);The Contact Folders API provides endpoints to organize contacts into custom folders (also called tags in some responses). Folders help you categorize and group contacts for better organization and targeted communication.
Key Features:
Retrieve all contact folders associated with your account. Optionally filter folders using a search query to find specific folders by name.
GET: /api/v1/contact/number/folder/list1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
search | string | No | Search term to filter folders by name (partial match) |
1curl -X GET "https://api.texttorrent.com/api/v1/contact/number/folder/list"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1curl -X GET "https://api.texttorrent.com/api/v1/contact/number/folder/list?search=VIP"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Folders retrieved successfully",
5 "data": [
6 {
7 "id": 5,
8 "name": "VIP Clients",
9 "created_at": "2025-09-10T08:30:00.000000Z"
10 },
11 {
12 "id": 8,
13 "name": "Hot Leads",
14 "created_at": "2025-10-01T14:22:00.000000Z"
15 },
16 {
17 "id": 12,
18 "name": "Newsletter Subscribers",
19 "created_at": "2025-10-15T09:15:00.000000Z"
20 }
21 ],
22 "errors": null
23}| Field | Type | Description |
|---|---|---|
id | integer | Unique identifier for the folder |
name | string | Name of the folder |
created_at | string | Timestamp when the folder was created (ISO 8601 format) |
1{
2 "code": 400,
3 "success": false,
4 "message": "Failed to retrieve folders",
5 "data": null,
6 "errors": []
7}Create a new folder to organize your contacts. Folder names can be up to 255 characters and help you categorize contacts for better management.
POST: /api/v1/contact/number/folder/create1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
folder_name | string | Yes | Name of the folder to create (max 255 characters) |
1curl -X POST "https://api.texttorrent.com/api/v1/contact/number/folder/create"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "folder_name": "VIP Clients"
8 }'1{
2 "code": 201,
3 "success": true,
4 "message": "Folder created successfully",
5 "data": {
6 "id": 15,
7 "name": "VIP Clients",
8 "user_id": 1,
9 "created_at": "2025-10-17T15:30:45.000000Z",
10 "updated_at": "2025-10-17T15:30:45.000000Z"
11 },
12 "errors": null
13}| Field | Type | Description |
|---|---|---|
id | integer | Unique identifier for the newly created folder |
name | string | Name of the folder |
user_id | integer | ID of the user who owns this folder |
created_at | string | Timestamp when the folder was created (ISO 8601 format) |
updated_at | string | Timestamp when the folder was last updated (ISO 8601 format) |
1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "folder_name": [
8 "The folder name field is required."
9 ]
10 }
11}1{
2 "code": 400,
3 "success": false,
4 "message": "Failed to create folder",
5 "data": null,
6 "errors": []
7}1async function createFolder(folderName) {
2 const response = await fetch('https://api.texttorrent.com/api/v1/contact/number/folder/create', {
3 method: 'POST',
4 headers: {
5 'X-API-SID': 'SID....................................',
6 'X-API-PUBLIC-KEY': 'PK.....................................',
7 'Content-Type': 'application/json',
8 'Accept': 'application/json'
9 },
10 body: JSON.stringify({
11 folder_name: folderName
12 })
13 });
14
15 const data = await response.json();
16
17 if (data.success) {
18 console.log('Folder created:', data.data);
19 return data.data;
20 } else {
21 console.error('Error:', data.message);
22 throw new Error(data.message);
23 }
24}
25
26// Usage
27createFolder('VIP Clients');Update the name of an existing contact folder. Note that folder responses may show "Tag" in messages (folders and tags are used interchangeably in the system).
PUT: /api/v1/contact/number/folder/{id}1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | ID of the folder to update |
| Parameter | Type | Required | Description |
|---|---|---|---|
folder_name | string | Yes | New name for the folder (max 255 characters) |
1curl -X PUT "https://api.texttorrent.com/api/v1/contact/number/folder/15"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "folder_name": "Premium VIP Clients"
8 }'1{
2 "code": 200,
3 "success": true,
4 "message": "Tag updated successfully",
5 "data": {
6 "id": 15,
7 "name": "Premium VIP Clients",
8 "user_id": 1,
9 "created_at": "2025-10-17T15:30:45.000000Z",
10 "updated_at": "2025-10-17T16:20:30.000000Z"
11 },
12 "errors": null
13}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "folder_name": [
8 "The folder name field is required."
9 ]
10 }
11}1{
2 "code": 400,
3 "success": false,
4 "message": "Failed to update folder",
5 "data": null,
6 "errors": []
7}updated_at timestamp is automatically updatedPermanently delete a contact folder. Contacts assigned to this folder will not be deleted, but their folder assignment will be removed.
DELETE: /api/v1/contact/number/folder/{id}1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | ID of the folder to delete |
1curl -X DELETE "https://api.texttorrent.com/api/v1/contact/number/folder/15"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Tag deleted successfully",
5 "data": null,
6 "errors": null
7}1{
2 "code": 400,
3 "success": false,
4 "message": "Failed to delete tag",
5 "data": null,
6 "errors": []
7}folder_id set to null1async function deleteFolder(folderId) {
2 if (!confirm('Are you sure you want to delete this folder? Contacts will not be deleted.')) {
3 return;
4 }
5
6 const response = await fetch('https://api.texttorrent.com/api/v1/contact/number/folder/"$"{folderId}', {
7 method: 'DELETE',
8 headers: {
9 'X-API-SID': 'SID....................................',
10 'X-API-PUBLIC-KEY': 'PK.....................................',
11 'Accept': 'application/json'
12 }
13 });
14
15 const data = await response.json();
16
17 if (data.success) {
18 console.log('Folder deleted successfully');
19 // Refresh folder list
20 await loadFolders();
21 } else {
22 console.error('Error:', data.message);
23 alert('Failed to delete folder');
24 }
25}
26
27// Usage
28deleteFolder(15)Assign a contact to a specific folder for better organization. You can also use this endpoint to move a contact from one folder to another by providing a different folder ID.
POST: /api/v1/contact/add/folder1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
contact_id | integer | Yes | ID of the contact to assign (must exist in contacts table) |
folder_id | integer | Yes | ID of the folder to assign contact to (must exist in contact_folders table) |
1curl -X POST "https://api.texttorrent.com/api/v1/contact/add/folder"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "contact_id": 1234,
8 "folder_id": 15
9 }'1{
2 "code": 200,
3 "success": true,
4 "message": "Contact added to folder successfully",
5 "data": {
6 "id": 1234,
7 "first_name": "John",
8 "last_name": "Doe",
9 "number": "+19292223344",
10 "email": "john.doe@example.com",
11 "company": "Tech Solutions Inc",
12 "list_id": 45,
13 "folder_id": 15,
14 "user_id": 1,
15 "blacklisted": 0,
16 "valid": 1,
17 "validation_process": 1,
18 "created_at": "2025-09-15T10:30:00.000000Z",
19 "updated_at": "2025-10-17T16:45:22.000000Z"
20 },
21 "errors": null
22}Returns the complete updated contact object with all fields including the new folder_id.
1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "contact_id": [
8 "The selected contact id is invalid."
9 ],
10 "folder_id": [
11 "The selected folder id is invalid."
12 ]
13 }
14}1{
2 "code": 400,
3 "success": false,
4 "message": "Contact removed from folder successfully",
5 "data": null,
6 "errors": []
7}Note: The error message says "Contact removed from folder successfully" even though it's an error response. This appears to be a bug in the implementation.
updated_at timestamp is automatically updated1function assignContactToFolder($contactId, $folderId) {
2 $ch = curl_init('https://api.texttorrent.com/api/v1/contact/add/folder');
3
4 curl_setopt($ch, CURLOPT_POST, true);
5 curl_setopt($ch, CURLOPT_HTTPHEADER, [
6 'X-API-SID: SID....................................',
7 'X-API-PUBLIC-KEY: PK.....................................',
8 'Content-Type: application/json',
9 'Accept: application/json'
10 ]);
11 curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
12 'contact_id' => $contactId,
13 'folder_id' => $folderId
14 ]));
15 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
16
17 $response = curl_exec($ch);
18 curl_close($ch);
19
20 $data = json_decode($response, true);
21
22 if ($data['success']) {
23 echo 'Contact assigned to folder successfully';
24 return $data['data'];
25 } else {
26 echo 'Error: ' . $data['message'];
27 return null;
28 }
29}
30
31// Usage
32assignContactToFolder(1234, 15);When to use Folders:
When to use Lists:
1List: Customers
2├── Folder: VIP Clients
3├── Folder: Regular Customers
4└── Folder: Churned Customers
5
6List: Leads
7├── Folder: Hot Leads
8├── Folder: Warm Leads
9└── Folder: Cold Leads
10
11List: Event Contacts
12├── Folder: 2025 Q1 Webinar
13├── Folder: 2025 Q2 Conference
14└── Folder: 2025 Q3 Workshop1// Complete folder workflow
2async function organizeFolderWorkflow() {
3 // 1. Create folders for organization
4 const vipFolder = await createFolder('VIP Clients');
5 const hotLeadsFolder = await createFolder('Hot Leads');
6
7 // 2. List all folders to verify
8 const allFolders = await listFolders();
9 console.log('All folders:', allFolders);
10
11 // 3. Assign contacts to folders
12 await assignContactToFolder(1234, vipFolder.id);
13 await assignContactToFolder(1235, hotLeadsFolder.id);
14
15 // 4. Search for specific folder
16 const vipFolders = await listFolders('VIP');
17 console.log('VIP folders:', vipFolders);
18
19 // 5. Update folder name if needed
20 await updateFolder(vipFolder.id, 'Premium VIP Clients');
21
22 // 6. Clean up unused folders
23 // await deleteFolder(oldFolderId);
24}The Contact CRUD (Create, Read, Update, Delete) API provides comprehensive endpoints to manage individual contacts in your account. These endpoints allow you to add new contacts, update existing contact information, delete contacts, and import contacts in bulk from CSV files.
Key Features:
Create a new contact in your account. Contacts can be added to a specific list, marked as blacklisted, or kept as unlisted. Phone numbers must be valid North American numbers and will be automatically formatted with the +1 prefix.
POST: /api/v1/contact/add1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
first_name | string | Yes | Contact's first name (max 50 characters) |
last_name | string | No | Contact's last name (max 50 characters) |
number | string | Yes | Phone number (7-14 digits, automatically prefixed with +1) |
email | string | No | Email address (max 100 characters, must be valid email format) |
company | string | No | Company name (max 100 characters) |
list_id | integer | Yes | ID of the contact list (must exist in contact_lists table) |
type | string | No | Contact type: default, blacklisted, or unlisted |
1curl -X POST "https://api.texttorrent.com/api/v1/contact/add"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "first_name": "John",
8 "last_name": "Doe",
9 "number": "2025551234",
10 "email": "john.doe@example.com",
11 "company": "Tech Solutions Inc",
12 "list_id": 45,
13 "type": "default"
14 }'1{
2 "code": 201,
3 "success": true,
4 "message": "Contact added successfully",
5 "data": {
6 "id": 5678,
7 "first_name": "John",
8 "last_name": "Doe",
9 "number": "+12025551234",
10 "email": "john.doe@example.com",
11 "company": "Tech Solutions Inc",
12 "list_id": 45,
13 "folder_id": null,
14 "user_id": 1,
15 "blacklisted": 0,
16 "valid": 0,
17 "validation_process": 0,
18 "created_at": "2025-10-17T18:30:45.000000Z",
19 "updated_at": "2025-10-17T18:30:45.000000Z"
20 },
21 "errors": null
22}| Field | Type | Description |
|---|---|---|
id | integer | Unique identifier for the contact |
number | string | Phone number with +1 prefix automatically added |
list_id | integer|null | ID of the list (null for blacklisted/unlisted contacts) |
blacklisted | integer | 1 if blacklisted, 0 otherwise |
valid | integer | 1 if number validated, 0 otherwise |
validation_process | integer | 1 if validation completed, 0 otherwise |
1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "first_name": [
8 "The first name field is required."
9 ],
10 "number": [
11 "The number field is required.",
12 "The number format is invalid."
13 ],
14 "list_id": [
15 "The selected list id is invalid."
16 ]
17 }
18}1{
2 "code": 422,
3 "success": false,
4 "message": "List can't have more than 10000 contacts.",
5 "data": null,
6 "errors": null
7}1{
2 "code": 400,
3 "success": false,
4 "message": "Failed to add contact",
5 "data": null,
6 "errors": []
7}1async function addContact(contactData) {
2 const response = await fetch('https://api.texttorrent.com/api/v1/contact/add', {
3 method: 'POST',
4 headers: {
5 'X-API-SID': 'SID....................................',
6 'X-API-PUBLIC-KEY': 'PK.....................................',
7 'Content-Type': 'application/json',
8 'Accept': 'application/json'
9 },
10 body: JSON.stringify({
11 first_name: contactData.firstName,
12 last_name: contactData.lastName,
13 number: contactData.phoneNumber,
14 email: contactData.email,
15 company: contactData.company,
16 list_id: contactData.listId,
17 type: contactData.type || 'default'
18 })
19 });
20
21 const data = await response.json();
22
23 if (data.success) {
24 console.log('Contact added:', data.data);
25 return data.data;
26 } else {
27 console.error('Error:', data.message);
28 if (data.errors) {
29 Object.entries(data.errors).forEach(([field, messages]) => {
30 console.error('"$"{field}:', messages.join(', '));
31 });
32 }
33 throw new Error(data.message);
34 }
35}
36
37// Usage
38addContact({
39 firstName: 'John',
40 lastName: 'Doe',
41 phoneNumber: '2025551234',
42 email: 'john.doe@example.com',
43 company: 'Tech Solutions Inc',
44 listId: 45
45});^\+?[0-9]{1,4}?[0-9]{7,14}$Update an existing Contact's information. You can modify the Contact's name, phone number, email, and company details. The phone number will be automatically formatted with the +1 prefix.
PUT: /api/v1/contact/{id}1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | ID of the contact to update |
| Parameter | Type | Required | Description |
|---|---|---|---|
first_name | string | Yes | Contact's first name (max 50 characters) |
last_name | string | No | Contact's last name (max 50 characters) |
mobile_number | string | Yes | Phone number (max 20 characters, automatically prefixed with +1) |
email | string | No | Email address (max 100 characters, must be valid email format) |
company | string | No | Company name (max 100 characters) |
1curl -X PUT "https://api.texttorrent.com/api/v1/contact/5678"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................." -H "Content-Type: application/json"
4 -H "Accept: application/json"
5 -d '{
6 "first_name": "John",
7 "last_name": "Smith",
8 "mobile_number": "2025559999",
9 "email": "john.smith@newcompany.com",
10 "company": "New Tech Corp"
11 }'1{
2 "code": 200,
3 "success": true,
4 "message": "Contact updated successfully",
5 "data": {
6 "id": 5678,
7 "first_name": "John",
8 "last_name": "Smith",
9 "number": "+12025559999",
10 "email": "john.smith@newcompany.com",
11 "company": "New Tech Corp",
12 "list_id": 45,
13 "folder_id": null,
14 "user_id": 1,
15 "blacklisted": 0,
16 "valid": 0,
17 "validation_process": 0,
18 "created_at": "2025-10-17T18:30:45.000000Z",
19 "updated_at": "2025-10-17T19:15:22.000000Z"
20 },
21 "errors": null
22}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "first_name": [
8 "The first name field is required."
9 ],
10 "mobile_number": [
11 "The mobile number field is required."
12 ],
13 "email": [
14 "The email must be a valid email address."
15 ]
16 }
17}1{
2 "code": 400,
3 "success": false,
4 "message": "Failed to update contact",
5 "data": null,
6 "errors": []
7}mobile_number (not number like in create)updated_at timestamp is automatically updated1function updateContact($contactId, $contactData) {
2 $ch = curl_init('https://api.texttorrent.com/api/v1/contact/' . $contactId);
3
4 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
5 curl_setopt($ch, CURLOPT_HTTPHEADER, [
6 'X-API-SID: SID....................................',
7 'X-API-PUBLIC-KEY: PK.....................................',
8 'Content-Type: application/json',
9 'Accept: application/json'
10 ]);
11 curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
12 'first_name' => $contactData['first_name'],
13 'last_name' => $contactData['last_name'] ?? null,
14 'mobile_number' => $contactData['mobile_number'],
15 'email' => $contactData['email'] ?? null,
16 'company' => $contactData['company'] ?? null
17 ]));
18 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
19
20 $response = curl_exec($ch);
21 curl_close($ch);
22
23 $data = json_decode($response, true);
24
25 if ($data['success']) {
26 echo 'Contact updated successfully';
27 return $data['data'];
28 } else {
29 echo 'Error: ' . $data['message'];
30 return null;
31 }
32}
33
34// Usage
35updateContact(5678, [
36 'first_name' => 'John',
37 'last_name' => 'Smith',
38 'mobile_number' => '2025559999',
39 'email' => 'john.smith@newcompany.com',
40 'company' => 'New Tech Corp'
41]);Delete one or more contacts from your account. This endpoint supports bulk deletion by accepting an array of contact IDs. Deleted contacts are permanently removed and cannot be recovered.
DELETE: /api/v1/contact/1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
contact_ids | array | Yes | Array of contact IDs to delete (all must exist in contacts table) |
1curl -X DELETE "https://api.texttorrent.com/api/v1/contact/"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "contact_ids": [5678]
8 }'1curl -X DELETE "https://api.texttorrent.com/api/v1/contact/"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "contact_ids": [5678, 5679, 5680, 5681]
8 }'1{
2 "code": 200,
3 "success": true,
4 "message": "Contacts deleted successfully",
5 "data": null,
6 "errors": null
7}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "contact_ids": [
8 "The contact ids field is required.",
9 "The contact ids must be an array."
10 ],
11 "contact_ids.0": [
12 "The selected contact ids.0 is invalid."
13 ]
14 }
15}1{
2 "code": 400,
3 "success": false,
4 "message": "Failed to delete contacts",
5 "data": null,
6 "errors": []
7}1async function deleteContacts(contactIds) {
2 // Confirm deletion
3 if (!confirm('Are you sure you want to delete "$"{contactIds.length} contact(s)? This cannot be undone.')) {
4 return;
5 }
6
7 const response = await fetch('https://api.texttorrent.com/api/v1/contact/', {
8 method: 'DELETE',
9 headers: {
10 'X-API-SID': 'SID....................................',
11 'X-API-PUBLIC-KEY': 'PK.....................................',
12 'Content-Type': 'application/json',
13 'Accept': 'application/json'
14 },
15 body: JSON.stringify({
16 contact_ids: contactIds
17 })
18 });
19
20 const data = await response.json();
21
22 if (data.success) {
23 console.log('Contacts deleted:', contactIds.length);
24 // Refresh contact list
25 await loadContacts();
26 } else {
27 console.error('Error:', data.message);
28 alert('Failed to delete contacts');
29 }
30}
31
32// Usage - Delete single contact
33deleteContacts([5678]);
34
35// Usage - Bulk delete
36deleteContacts([5678, 5679, 5680, 5681]);Import contacts in bulk from CSV files. This endpoint supports custom column mapping, allowing you to specify which CSV columns contain first name, last name, phone number, email, company, and up to 3 additional custom fields. The import process is chunked for large files.
POST: /api/v1/contact/importGET: /api/v1/contact/sample/download1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: multipart/form-data
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
chunks[] | file[] | Yes | Array of CSV file chunks (for large file uploads) |
file_names[] | string[] | Yes | Array of file names corresponding to each chunk (max 255 chars each) |
total_records | integer | Yes | Total number of records in the CSV file |
file_size | numeric | Yes | Total file size in bytes |
list_id | integer | No | ID of existing list to import contacts into (creates new list if omitted) |
first_name_column | string | Yes | CSV column name containing first names (max 255 chars) |
last_name_column | string | No | CSV column name containing last names (max 255 chars) |
email_address_column | string | No | CSV column name containing email addresses (max 255 chars) |
company_column | string | No | CSV column name containing company names (max 255 chars) |
phone_number_column | string | Yes | CSV column name containing phone numbers (max 255 chars) |
additional_1_column | string | No | CSV column name for additional field 1 (max 255 chars) |
additional_2_column | string | No | CSV column name for additional field 2 (max 255 chars) |
additional_3_column | string | No | CSV column name for additional field 3 (max 255 chars) |
list_id is provided and valid, contacts are added to that listlist_id is omitted, a new list is created using the filename (without extension)1curl -X POST "https://api.texttorrent.com/api/v1/contact/import" -H "X-API-SID: SID...................................."
2 -H "X-API-PUBLIC-KEY: PK....................................."
3 -H "Accept: application/json"
4 -F "chunks[]=@contacts.csv"
5 -F "file_names[]=contacts.csv"
6 -F "total_records=150"
7 -F "file_size=25600"
8 -F "list_id=45"
9 -F "first_name_column=First Name"
10 -F "last_name_column=Last Name"
11 -F "email_address_column=Email"
12 -F "company_column=Company"
13 -F "phone_number_column=Phone"1{
2 "code": 200,
3 "success": true,
4 "message": "Contacts imported successfully",
5 "data": {
6 "imported_count": 150,
7 "list_id": 45,
8 "list_name": "Customers"
9 },
10 "errors": null
11}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "chunks": [
8 "The chunks field is required.",
9 "The chunks must be an array."
10 ],
11 "first_name_column": [
12 "The first name column field is required."
13 ],
14 "phone_number_column": [
15 "The phone number column field is required."
16 ],
17 "list_id": [
18 "The selected list id is invalid."
19 ]
20 }
21}Get a sample CSV file to understand the expected format:
1curl -X GET "https://api.texttorrent.com/api/v1/contact/sample/download"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Sample file download",
5 "data": {
6 "url": "https://api.texttorrent.com/files/Sample Contact File.csv"
7 },
8 "errors": null
9}1async function importContacts(file, columnMapping, listId = null) {
2 const formData = new FormData();
3
4 // Add file chunks (simplified - single file in this example)
5 formData.append('chunks[]', file);
6 formData.append('file_names[]', file.name);
7 formData.append('total_records', 100); // Calculate from CSV
8 formData.append('file_size', file.size);
9
10 // Add list ID if provided
11 if (listId) {
12 formData.append('list_id', listId);
13 }
14
15 // Add column mappings
16 formData.append('first_name_column', columnMapping.firstName);
17 formData.append('phone_number_column', columnMapping.phoneNumber);
18
19 // Optional columns
20 if (columnMapping.lastName) {
21 formData.append('last_name_column', columnMapping.lastName);
22 }
23 if (columnMapping.email) {
24 formData.append('email_address_column', columnMapping.email);
25 }
26 if (columnMapping.company) {
27 formData.append('company_column', columnMapping.company);
28 }
29
30 const response = await fetch('https://api.texttorrent.com/api/v1/contact/import', {
31 method: 'POST',
32 headers: {
33 'X-API-SID': 'SID....................................',
34 'X-API-PUBLIC-KEY': 'PK.....................................',
35 'Accept': 'application/json'
36 // Note: Don't set Content-Type for FormData, browser sets it automatically
37 },
38 body: formData
39 });
40
41 const data = await response.json();
42
43 if (data.success) {
44 console.log('Import successful:', data.data);
45 alert('Successfully imported "$"{data.data.imported_count} contacts');
46 return data.data;
47 } else {
48 console.error('Import failed:', data.message);
49 throw new Error(data.message);
50 }
51}
52
53// Usage with file input
54document.getElementById('csvFile').addEventListener('change', (e) => {
55 const file = e.target.files[0];
56 importContacts(file, {
57 firstName: 'First Name',
58 lastName: 'Last Name',
59 phoneNumber: 'Phone',
60 email: 'Email',
61 company: 'Company'
62 }, 45);
63});first_name_column and phone_number_column are requiredlist_id provided, creates new list from filenamemultipart/form-data content type for file uploadsfirst_name_column: "First Name"1// Complete contact management workflow
2class ContactManager {
3 constructor(apiSid, apiKey) {
4 this.apiSid = apiSid;
5 this.apiKey = apiKey;
6 this.baseUrl = 'https://api.texttorrent.com/api/v1';
7 }
8
9 async addContact(contact) {
10 // Validate before adding
11 if (!this.validatePhone(contact.number)) {
12 throw new Error('Invalid phone number format');
13 }
14
15 // Check list size
16 const listSize = await this.getListSize(contact.list_id);
17 if (listSize >= 10000) {
18 throw new Error('List is full (10,000 contacts max)');
19 }
20
21 // Add contact
22 return await this.apiCall('POST', '/contact/add', contact);
23 }
24
25 async updateContact(id, updates) {
26 return await this.apiCall('PUT', '/contact/"$"{id}', updates);
27 }
28
29 async deleteContacts(contactIds) {
30 if (!confirm('Delete "$"{contactIds.length} contact(s)?')) {
31 return;
32 }
33 return await this.apiCall('DELETE', '/contact/', { contact_ids: contactIds });
34 }
35
36 async importFromCSV(file, mapping, listId) {
37 const formData = new FormData();
38 formData.append('chunks[]', file);
39 formData.append('file_names[]', file.name);
40 formData.append('total_records', await this.countCSVRows(file));
41 formData.append('file_size', file.size);
42
43 if (listId) formData.append('list_id', listId);
44
45 Object.entries(mapping).forEach(([key, value]) => {
46 if (value) formData.append(key, value);
47 });
48
49 return await this.apiCall('POST', '/contact/import', formData, true);
50 }
51
52 validatePhone(number) {
53 return /^[0-9]{10}$/.test(number);
54 }
55
56 async getListSize(listId) {
57 const result = await this.apiCall('GET', '/contact/"$"{listId}/contacts');
58 return result.data.total || 0;
59 }
60
61 async countCSVRows(file) {
62 const text = await file.text();
63 return text.split('
64').length - 1; // Exclude header
65 }
66
67 async apiCall(method, endpoint, data = null, isFormData = false) {
68 const headers = {
69 'X-API-SID': this.apiSid,
70 'X-API-PUBLIC-KEY': this.apiKey,
71 'Accept': 'application/json'
72 };
73
74 if (!isFormData) {
75 headers['Content-Type'] = 'application/json';
76 }
77
78 const options = {
79 method,
80 headers,
81 body: data ? (isFormData ? data : JSON.stringify(data)) : null
82 };
83
84 const response = await fetch(this.baseUrl + endpoint, options);
85 return await response.json();
86 }
87}
88
89// Usage
90const manager = new ContactManager('SID....................................', 'PK.....................................');
91
92// Add contact
93await manager.addContact({
94 first_name: 'John',
95 last_name: 'Doe',
96 number: '2025551234',
97 email: 'john@example.com',
98 list_id: 45
99});
100
101// Update contact
102await manager.updateContact(5678, {
103 first_name: 'John',
104 mobile_number: '2025559999',
105 email: 'john.new@example.com'
106});
107
108// Delete contacts
109await manager.deleteContacts([5678, 5679]);
110
111// Import from CSV
112const fileInput = document.getElementById('csvFile');
113await manager.importFromCSV(fileInput.files[0], {
114 first_name_column: 'First Name',
115 phone_number_column: 'Phone',
116 email_address_column: 'Email'
117}, 45);The Contact Validator API allows you to view and manage phone number validation results. After validating numbers (using the validation endpoint in section 3.2.2), you can retrieve validation history, cleanup invalid numbers from lists, export validation results, and delete validation records.
Key Features:
Retrieve a paginated list of all phone number validation records for your account, including validation statistics such as total numbers, mobile numbers, landline numbers, invalid numbers, and credits used. Results include validations from both your account and any sub-accounts.
GET: /api/v1/contact/validator/1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
search | string | No | Search term to filter by list name (partial match) |
limit | integer | No | Number of results per page (default: 10) |
page | integer | No | Page number for pagination (default: 1) |
1curl -X GET "https://api.texttorrent.com/api/v1/contact/validator/"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1curl -X GET "https://api.texttorrent.com/api/v1/contact/validator/?search=VIP&limit=20&page=1"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Validator Credits",
5 "data": {
6 "current_page": 1,
7 "data": [
8 {
9 "id": 123,
10 "list_name": "VIP Clients",
11 "total_credits": 150,
12 "total_number": 150,
13 "total_mobile_numbers": 120,
14 "total_landline_numbers": 25,
15 "total_other_numbers": 5,
16 "invalid_numbers": 0,
17 "created_at": "2025-10-15T14:30:00.000000Z",
18 "status": "Completed",
19 "cleaned": 0
20 },
21 {
22 "id": 122,
23 "list_name": "Hot Leads",
24 "total_credits": 200,
25 "total_number": 200,
26 "total_mobile_numbers": 185,
27 "total_landline_numbers": 10,
28 "total_other_numbers": 3,
29 "invalid_numbers": 2,
30 "created_at": "2025-10-14T10:15:00.000000Z",
31 "status": "Completed",
32 "cleaned": 1
33 }
34 ],
35 "first_page_url": "https://api.texttorrent.com/api/v1/contact/validator?page=1",
36 "from": 1,
37 "last_page": 5,
38 "last_page_url": "https://api.texttorrent.com/api/v1/contact/validator?page=5",
39 "next_page_url": "https://api.texttorrent.com/api/v1/contact/validator?page=2",
40 "path": "https://api.texttorrent.com/api/v1/contact/validator",
41 "per_page": 10,
42 "prev_page_url": null,
43 "to": 10,
44 "total": 48
45 },
46 "errors": null
47}| Field | Type | Description |
|---|---|---|
id | integer | Unique identifier for the validation record |
list_name | string | Name of the contact list that was validated |
total_credits | integer | Number of credits used for this validation |
total_number | integer | Total number of phone numbers validated |
total_mobile_numbers | integer | Count of valid mobile phone numbers |
total_landline_numbers | integer | Count of landline phone numbers |
total_other_numbers | integer | Count of other number types (VoIP, toll-free, etc.) |
invalid_numbers | integer | Count of invalid or unverifiable numbers |
status | string | Validation status (e.g., "Completed", "Processing") |
cleaned | integer | 1 if cleanup processed (non-mobile numbers removed), 0 otherwise |
created_at | string | Timestamp when validation was created (ISO 8601 format) |
1async function getValidationHistory(page = 1, limit = 10, search = '') {
2 const params = new URLSearchParams({
3 page: page,
4 limit: limit
5 });
6
7 if (search) {
8 params.append('search', search);
9 }
10
11 const response = await fetch('https://api.texttorrent.com/api/v1/contact/validator/?"$"{params}', {
12 method: 'GET',
13 headers: {
14 'X-API-SID': 'SID....................................',
15 'X-API-PUBLIC-KEY': 'PK.....................................',
16 'Accept': 'application/json'
17 }
18 });
19
20 const data = await response.json();
21
22 if (data.success) {
23 console.log('Validation history:', data.data.data);
24 console.log('Page "$"{data.data.current_page} of "$"{data.data.last_page}');
25 console.log('Total validations: "$"{data.data.total}');
26 return data.data;
27 } else {
28 console.error('Error:', data.message);
29 throw new Error(data.message);
30 }
31}
32
33// Usage - Get first page
34getValidationHistory();
35
36// Usage - Search with pagination
37getValidationHistory(1, 20, 'VIP')cleaned field to identify if cleanup has been processedProcess cleanup for a validation record to remove all non-mobile numbers (landline, invalid, and other number types) from the contact list. This helps maintain a clean list of valid mobile numbers for SMS campaigns.
POST: /api/v1/contact/validator/process-cleanup/{id}1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | ID of the validation record to cleanup |
1curl -X POST "https://api.texttorrent.com/api/v1/contact/validator/process-cleanup/123"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Cleanup Successful",
5 "data": null,
6 "errors": null
7}1async function processCleanup(validationId) {
2 // Confirm cleanup action
3 const confirmed = confirm(
4 'This will permanently delete all non-mobile numbers from the list. ' +
5 'Only mobile numbers will remain. Continue?'
6 );
7
8 if (!confirmed) {
9 return;
10 }
11
12 const response = await fetch(
13 'https://api.texttorrent.com/api/v1/contact/validator/process-cleanup/"$"{validationId}',
14 {
15 method: 'POST',
16 headers: {
17 'X-API-SID': 'SID....................................',
18 'X-API-PUBLIC-KEY': 'PK.....................................',
19 'Accept': 'application/json'
20 }
21 }
22 );
23
24 const data = await response.json();
25
26 if (data.success) {
27 console.log('Cleanup completed successfully');
28 alert('Non-mobile numbers have been removed from the list');
29 // Refresh validation history
30 await getValidationHistory();
31 } else {
32 console.error('Cleanup failed:', data.message);
33 alert('Failed to process cleanup');
34 }
35}
36
37// Usage
38processCleanup(123);cleaned: 1Export validation results to an Excel (.xlsx) file. You can export one or multiple validation records at once. The export includes detailed information about each validated number, including line type, carrier, and validation status.
POST: /api/v1/contact/validator/export1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
validation_ids | array | Yes | Array of validation IDs to export (all must exist in number_validations table) |
1curl -X POST "https://api.texttorrent.com/api/v1/contact/validator/export"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "validation_ids": [123]
8 }'1curl -X POST "https://api.texttorrent.com/api/v1/contact/validator/export"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "validation_ids": [123, 124, 125]
8 }'1{
2 "code": 200,
3 "success": true,
4 "message": "Exported successfully",
5 "data": {
6 "url": "https://your-storage.digitaloceanspaces.com/validation/validation_export_1729180845.xlsx"
7 },
8 "errors": null
9}| Field | Type | Description |
|---|---|---|
url | string | Direct download URL for the exported Excel file |
1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "validation_ids": [
8 "The validation ids field is required.",
9 "The validation ids must be an array."
10 ],
11 "validation_ids.0": [
12 "The selected validation ids.0 is invalid."
13 ]
14 }
15}1async function exportValidations(validationIds) {
2 const response = await fetch('https://api.texttorrent.com/api/v1/contact/validator/export', {
3 method: 'POST',
4 headers: {
5 'X-API-SID': 'SID....................................',
6 'X-API-PUBLIC-KEY': 'PK.....................................',
7 'Content-Type': 'application/json',
8 'Accept': 'application/json'
9 },
10 body: JSON.stringify({
11 validation_ids: validationIds
12 })
13 });
14
15 const data = await response.json();
16
17 if (data.success) {
18 console.log('Export successful, downloading...');
19 // Trigger download
20 window.open(data.data.url, '_blank');
21 return data.data.url;
22 } else {
23 console.error('Export failed:', data.message);
24 throw new Error(data.message);
25 }
26}
27
28// Usage - Export single validation
29exportValidations([123]);
30
31// Usage - Export multiple validations
32exportValidations([123, 124, 125]);1function exportValidations($validationIds) {
2 $ch = curl_init('https://api.texttorrent.com/api/v1/contact/validator/export');
3
4 curl_setopt($ch, CURLOPT_POST, true);
5 curl_setopt($ch, CURLOPT_HTTPHEADER, [
6 'X-API-SID: SID....................................',
7 'X-API-PUBLIC-KEY: PK.....................................',
8 'Content-Type: application/json',
9 'Accept: application/json'
10 ]);
11 curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
12 'validation_ids' => $validationIds
13 ]));
14 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
15
16 $response = curl_exec($ch);
17 curl_close($ch);
18
19 $data = json_decode($response, true);
20
21 if ($data['success']) {
22 echo 'Download URL: ' . $data['data']['url'];
23 return $data['data']['url'];
24 } else {
25 echo 'Error: ' . $data['message'];
26 return null;
27 }
28}
29
30// Usage
31exportValidations([123, 124, 125]);The exported Excel file typically includes:
validation_export_ timestamp here .xlsxPermanently delete a validation record and all associated validation item data. This removes the validation history but does not affect the contacts in the list (unless you've already processed cleanup).
DELETE: /api/v1/contact/validator/{id}1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | ID of the validation record to delete |
number_validations tablenumber_validation_items table1curl -X DELETE "https://api.texttorrent.com/api/v1/contact/validator/123"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Validation deleted successfully",
5 "data": null,
6 "errors": null
7}1async function deleteValidation(validationId) {
2 // Confirm deletion
3 if (!confirm('Delete this validation record? This cannot be undone.')) {
4 return;
5 }
6
7 const response = await fetch(
8 'https://api.texttorrent.com/api/v1/contact/validator/"$"{validationId}',
9 {
10 method: 'DELETE',
11 headers: {
12 'X-API-SID': 'SID....................................',
13 'X-API-PUBLIC-KEY': 'PK.....................................',
14 'Accept': 'application/json'
15 }
16 }
17 );
18
19 const data = await response.json();
20
21 if (data.success) {
22 console.log('Validation record deleted');
23 alert('Validation record deleted successfully');
24 // Refresh validation history
25 await getValidationHistory();
26 } else {
27 console.error('Delete failed:', data.message);
28 alert('Failed to delete validation record');
29 }
30}
31
32// Usage
33deleteValidation(123);1// Complete validation management workflow
2class ValidationManager {
3 constructor(apiSid, apiKey) {
4 this.apiSid = apiSid;
5 this.apiKey = apiKey;
6 this.baseUrl = 'https://api.texttorrent.com/api/v1';
7 }
8
9 async getHistory(page = 1, search = '') {
10 const params = new URLSearchParams({ page, limit: 20 });
11 if (search) params.append('search', search);
12
13 return await this.apiCall('GET', '/contact/validator/?"$"{params}');
14 }
15
16 async processCleanup(validationId) {
17 const confirmed = confirm(
18 'Remove all non-mobile numbers? This cannot be undone.'
19 );
20 if (!confirmed) return;
21
22 return await this.apiCall('POST', '/contact/validator/process-cleanup/"$"{validationId}');
23 }
24
25 async exportResults(validationIds) {
26 const result = await this.apiCall('POST', '/contact/validator/export', {
27 validation_ids: validationIds
28 });
29
30 if (result.success) {
31 // Auto-download
32 window.open(result.data.url, '_blank');
33 }
34
35 return result;
36 }
37
38 async deleteRecord(validationId) {
39 const confirmed = confirm('Delete validation record?');
40 if (!confirmed) return;
41
42 return await this.apiCall('DELETE', '/contact/validator/"$"{validationId}');
43 }
44
45 async completeWorkflow(validationId) {
46 try {
47 // 1. Get validation details
48 const history = await this.getHistory();
49 const validation = history.data.data.find(v => v.id === validationId);
50
51 console.log('Validation stats:', {
52 total: validation.total_number,
53 mobile: validation.total_mobile_numbers,
54 landline: validation.total_landline_numbers,
55 invalid: validation.invalid_numbers
56 });
57
58 // 2. Export results
59 console.log('Exporting results...');
60 await this.exportResults([validationId]);
61
62 // 3. Cleanup if needed
63 if (validation.total_landline_numbers > 0) {
64 console.log('Processing cleanup...');
65 await this.processCleanup(validationId);
66 }
67
68 // 4. Delete old record
69 console.log('Cleaning up history...');
70 await this.deleteRecord(validationId);
71
72 console.log('Workflow completed successfully');
73 } catch (error) {
74 console.error('Workflow failed:', error);
75 }
76 }
77
78 async apiCall(method, endpoint, data = null) {
79 const headers = {
80 'X-API-SID': this.apiSid,
81 'X-API-PUBLIC-KEY': this.apiKey,
82 'Accept': 'application/json'
83 };
84
85 if (data) {
86 headers['Content-Type'] = 'application/json';
87 }
88
89 const response = await fetch(this.baseUrl + endpoint, {
90 method,
91 headers,
92 body: data ? JSON.stringify(data) : null
93 });
94
95 return await response.json();
96 }
97}
98
99// Usage
100const validator = new ValidationManager('SID....................................', 'PK.....................................');
101
102// Get validation history
103await validator.getHistory(1, 'VIP');
104
105// Process cleanup
106await validator.processCleanup(123);
107
108// Export results
109await validator.exportResults([123, 124]);
110
111// Delete record
112await validator.deleteRecord(123);
113
114// Complete workflow
115await validator.completeWorkflow(123);The Contact Import History API provides endpoints to view and manage the history of contact imports performed via CSV file uploads. Track import status, view statistics, and download reports of skipped numbers during the import process.
Key Features:
Retrieve a paginated list of all contact import records for your account. The response includes detailed information about each import including status, file name, total records, skipped numbers, and timestamps.
GET: /api/v1/contact/import1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
limit | integer | No | Number of results per page (default: 10) |
page | integer | No | Page number for pagination (default: 1) |
1curl -X GET "https://api.texttorrent.com/api/v1/contact/import/"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1curl -X GET "https://api.texttorrent.com/api/v1/contact/import/?limit=20&page=1"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Contact imports fetched successfully",
5 "data": {
6 "current_page": 1,
7 "data": [
8 {
9 "id": 45,
10 "name": "customers.csv",
11 "file": "imports/customers_1729180845.csv",
12 "status": 2,
13 "status_text": "Completed",
14 "remarks": null,
15 "list_name": "Customers",
16 "total_records": 500,
17 "skipped": 12,
18 "skipped_file_url": "https://your-storage.digitaloceanspaces.com/skipped/customers_skipped_1729180845.xlsx",
19 "stored": 488,
20 "created_at": "2025-10-15T14:30:00.000000Z"
21 },
22 {
23 "id": 44,
24 "name": "leads.csv",
25 "file": "imports/leads_1729094400.csv",
26 "status": 2,
27 "status_text": "Completed",
28 "remarks": null,
29 "list_name": "Hot Leads",
30 "total_records": 250,
31 "skipped": 0,
32 "skipped_file_url": null,
33 "stored": 250,
34 "created_at": "2025-10-14T10:15:00.000000Z"
35 },
36 {
37 "id": 43,
38 "name": "prospects.csv",
39 "file": "imports/prospects_1729008000.csv",
40 "status": 3,
41 "status_text": "Failed",
42 "remarks": "Invalid CSV format",
43 "list_name": null,
44 "total_records": 0,
45 "skipped": 0,
46 "skipped_file_url": null,
47 "stored": 0,
48 "created_at": "2025-10-13T08:00:00.000000Z"
49 },
50 {
51 "id": 42,
52 "name": "contacts.csv",
53 "file": "imports/contacts_1728921600.csv",
54 "status": 1,
55 "status_text": "Processing",
56 "remarks": null,
57 "list_name": "Contacts",
58 "total_records": 1000,
59 "skipped": 0,
60 "skipped_file_url": null,
61 "stored": 450,
62 "created_at": "2025-10-12T06:00:00.000000Z"
63 }
64 ],
65 "first_page_url": "https://api.texttorrent.com/api/v1/contact/import?page=1",
66 "from": 1,
67 "last_page": 3,
68 "last_page_url": "https://api.texttorrent.com/api/v1/contact/import?page=3",
69 "next_page_url": "https://api.texttorrent.com/api/v1/contact/import?page=2",
70 "path": "https://api.texttorrent.com/api/v1/contact/import",
71 "per_page": 10,
72 "prev_page_url": null,
73 "to": 10,
74 "total": 28
75 },
76 "errors": null
77}| Field | Type | Description |
|---|---|---|
id | integer | Unique identifier for the import record |
name | string | Original filename of the uploaded CSV |
file | string | Stored file path in the system |
status | integer | Status code: 0=Pending, 1=Processing, 2=Completed, 3=Failed |
status_text | string | Human-readable status: "Pending", "Processing", "Completed", "Failed" |
remarks | string|null | Error message or notes (populated for failed imports) |
list_name | string|null | Name of the contact list where contacts were imported |
total_records | integer | Total number of records in the CSV file |
skipped | integer | Number of records that were skipped during import |
skipped_file_url | string|null | URL to download Excel file containing skipped records (null if no skips) |
stored | integer | Number of contacts successfully imported and stored |
created_at | string | Timestamp when import was initiated (ISO 8601 format) |
| Status Code | Status Text | Description |
|---|---|---|
0 | Pending | Import queued but not yet started |
1 | Processing | Import currently in progress |
2 | Completed | Import finished successfully |
3 | Failed | Import encountered an error and failed |
Records may be skipped during import for various reasons:
1async function getImportHistory(page = 1, limit = 10) {
2 const params = new URLSearchParams({
3 page: page,
4 limit: limit
5 });
6
7 const response = await fetch('https://api.texttorrent.com/api/v1/contact/import/?"$"{params}', {
8 method: 'GET',
9 headers: {
10 'X-API-SID': 'SID....................................',
11 'X-API-PUBLIC-KEY': 'PK.....................................',
12 'Accept': 'application/json'
13 }
14 });
15
16 const data = await response.json();
17
18 if (data.success) {
19 console.log('Import history:', data.data.data);
20
21 // Display summary
22 data.data.data.forEach(importRecord => {
23 console.log('Import "$"{importRecord.id}: "$"{importRecord.name}');
24 console.log('Status: "$"{importRecord.status_text}');
25 console.log(' Total: "$"{importRecord.total_records}, Stored: "$"{importRecord.stored}, Skipped: "$"{importRecord.skipped}');
26
27 if (importRecord.skipped > 0) {
28 console.log('Skipped file: "$"{importRecord.skipped_file_url}');
29 }
30
31 if (importRecord.status === 3) {
32 console.log(' Error: "$"{importRecord.remarks}');
33 }
34 });
35
36 console.log('Page "$"{data.data.current_page} of "$"{data.data.last_page}');
37 console.log('Total imports: "$"{data.data.total}');
38
39 return data.data;
40 } else {
41 console.error('Error:', data.message);
42 throw new Error(data.message);
43 }
44}
45
46// Usage - Get first page
47getImportHistory();
48
49// Usage - Get with pagination
50getImportHistory(1, 20)remarks field1// Poll import status until completion
2async function monitorImport(importId) {
3 let status = 1; // Processing
4
5 while (status === 0 || status === 1) {
6 const history = await getImportHistory();
7 const importRecord = history.data.find(imp => imp.id === importId);
8
9 if (!importRecord) {
10 throw new Error('Import record not found');
11 }
12
13 status = importRecord.status;
14
15 console.log('Import "$"{importId}: "$"{importRecord.status_text} - "$"{importRecord.stored}/"$"{importRecord.total_records} records');
16
17 if (status === 2) {
18 console.log('Import completed successfully');
19 if (importRecord.skipped > 0) {
20 console.log('"$"{importRecord.skipped} records were skipped');
21 console.log('Download skipped records: "$"{importRecord.skipped_file_url}');
22 }
23 return importRecord;
24 } else if (status === 3) {
25 console.error('Import failed:', importRecord.remarks);
26 throw new Error(importRecord.remarks);
27 }
28
29 // Wait 5 seconds before checking again
30 await new Promise(resolve => setTimeout(resolve, 5000));
31 }
32}
33
34// Usage
35monitorImport(45);Download a PDF report of contacts that were skipped during a CSV import. The report includes details about why each record was skipped, helping you identify and fix data issues for re-import.
POST: /api/v1/contact/import/pdf/download1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/pdf| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | Yes | URL of the skipped file (obtained from import history skipped_file_url) |
1curl -X POST "https://api.texttorrent.com/api/v1/contact/import/pdf/download"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/pdf"
6 -d '{
7 "url": "https://your-storage.digitaloceanspaces.com/skipped/customers_skipped_1729180845.xlsx"
8 }' --output skipped_numbers.pdfReturns a PDF file for download. Content-Type: application/pdf
1HTTP/1.1 200 OK
2Content-Type: application/pdf
3Content-Disposition: attachment; filename="skipped_numbers.pdf"
4Content-Length: 456781{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "url": [
8 "The url field is required.",
9 "The url must be a valid URL."
10 ]
11 }
12}The generated PDF typically includes:
1async function downloadSkippedPDF(skippedFileUrl) {
2 const response = await fetch('https://api.texttorrent.com/api/v1/contact/import/pdf/download', {
3 method: 'POST',
4 headers: {
5 'X-API-SID': 'SID....................................',
6 'X-API-PUBLIC-KEY': 'PK.....................................',
7 'Content-Type': 'application/json',
8 'Accept': 'application/pdf'
9 },
10 body: JSON.stringify({
11 url: skippedFileUrl
12 })
13 });
14
15 if (response.ok) {
16 // Convert response to blob
17 const blob = await response.blob();
18
19 // Create download link
20 const downloadUrl = window.URL.createObjectURL(blob);
21 const link = document.createElement('a');
22 link.href = downloadUrl;
23 link.download = 'skipped_numbers_"$"{Date.now()}.pdf';
24 document.body.appendChild(link);
25 link.click();
26 document.body.removeChild(link);
27
28 // Clean up
29 window.URL.revokeObjectURL(downloadUrl);
30
31 console.log('PDF downloaded successfully');
32 } else {
33 const error = await response.json();
34 console.error('Download failed:', error.message);
35 throw new Error(error.message);
36 }
37}
38
39// Usage
40downloadSkippedPDF('https://your-storage.digitaloceanspaces.com/skipped/customers_skipped_1729180845.xlsx');1function downloadSkippedPDF($skippedFileUrl, $outputPath) {
2 $ch = curl_init('https://api.texttorrent.com/api/v1/contact/import/pdf/download');
3
4 curl_setopt($ch, CURLOPT_POST, true);
5 curl_setopt($ch, CURLOPT_HTTPHEADER, [
6 'X-API-SID: SID....................................',
7 'X-API-PUBLIC-KEY: PK.....................................',
8 'Content-Type: application/json',
9 'Accept: application/pdf'
10 ]);
11 curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
12 'url' => $skippedFileUrl
13 ]));
14 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
15
16 $pdfContent = curl_exec($ch);
17 $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
18 curl_close($ch);
19
20 if ($httpCode === 200) {
21 file_put_contents($outputPath, $pdfContent);
22 echo "PDF saved to: $outputPath";
23 return true;
24 } else {
25 $error = json_decode($pdfContent, true);
26 echo "Error: " . ($error['message'] ?? 'Unknown error');
27 return false;
28 }
29}
30
31// Usage
32downloadSkippedPDF(
33 'https://your-storage.digitaloceanspaces.com/skipped/customers_skipped_1729180845.xlsx',
34 'skipped_numbers.pdf'
35);1// Complete workflow: Get import history and download skipped reports
2async function reviewImportResults(importId) {
3 // 1. Get import history
4 const history = await getImportHistory();
5 const importRecord = history.data.find(imp => imp.id === importId);
6
7 if (!importRecord) {
8 throw new Error('Import not found');
9 }
10
11 console.log('Import: "$"{importRecord.name}');
12 console.log('Status: "$"{importRecord.status_text}');
13 console.log('Total: "$"{importRecord.total_records}');
14 console.log('Stored: "$"{importRecord.stored}');
15 console.log('Skipped: "$"{importRecord.skipped}');
16
17 // 2. Download skipped numbers PDF if any were skipped
18 if (importRecord.skipped > 0 && importRecord.skipped_file_url) {
19 console.log('Downloading skipped numbers report...');
20 await downloadSkippedPDF(importRecord.skipped_file_url);
21 console.log('Review the PDF to see why records were skipped');
22 } else {
23 console.log('No records were skipped - import was 100% successful!');
24 }
25
26 // 3. Handle failed imports
27 if (importRecord.status === 3) {
28 console.error('Import failed:', importRecord.remarks);
29 console.log('Please fix the issues and try importing again');
30 }
31}
32
33// Usage
34reviewImportResults(45);Accept: application/pdf header to receive PDF--output or equivalent to save file in cURL/HTTP clients| Skip Reason | Fix |
|---|---|
| Duplicate Number | Remove duplicates from CSV or choose different list |
| Invalid Phone Format | Ensure numbers are 10 digits without +1 prefix |
| Missing First Name | Add first names or use placeholder like "Contact" |
| List Full (10,000 limit) | Create new list or cleanup existing list |
| Invalid Email Format | Fix email syntax or leave blank |
1// Complete import management system
2class ImportManager {
3 constructor(apiSid, apiKey) {
4 this.apiSid = apiSid;
5 this.apiKey = apiKey;
6 this.baseUrl = 'https://api.texttorrent.com/api/v1';
7 }
8
9 async getHistory(page = 1, limit = 10) {
10 const params = new URLSearchParams({ page, limit });
11 return await this.apiCall('GET', '/contact/import/?"$"{params}');
12 }
13
14 async monitorImport(importId, onProgress) {
15 let status = 1; // Processing
16 let lastStored = 0;
17
18 while (status === 0 || status === 1) {
19 const history = await this.getHistory();
20 const imp = history.data.data.find(i => i.id === importId);
21
22 if (!imp) throw new Error('Import not found');
23
24 status = imp.status;
25
26 // Report progress if changed
27 if (imp.stored !== lastStored) {
28 lastStored = imp.stored;
29 const progress = (imp.stored / imp.total_records * 100).toFixed(1);
30 onProgress?.({
31 status: imp.status_text,
32 progress: progress,
33 stored: imp.stored,
34 total: imp.total_records,
35 skipped: imp.skipped
36 });
37 }
38
39 if (status === 2) {
40 // Completed
41 return {
42 success: true,
43 import: imp,
44 message: 'Import completed: "$"{imp.stored} contacts imported, "$"{imp.skipped} skipped'
45 };
46 } else if (status === 3) {
47 // Failed
48 return {
49 success: false,
50 import: imp,
51 message: 'Import failed: "$"{imp.remarks}'
52 };
53 }
54
55 // Wait 5 seconds before checking again
56 await new Promise(resolve => setTimeout(resolve, 5000));
57 }
58 }
59
60 async downloadSkippedPDF(skippedUrl) {
61 const response = await fetch('"$"{this.baseUrl}/contact/import/pdf/download', {
62 method: 'POST',
63 headers: {
64 'X-API-SID': this.apiSid,
65 'X-API-PUBLIC-KEY': this.apiKey,
66 'Content-Type': 'application/json',
67 'Accept': 'application/pdf'
68 },
69 body: JSON.stringify({ url: skippedUrl })
70 });
71
72 if (response.ok) {
73 const blob = await response.blob();
74 const url = window.URL.createObjectURL(blob);
75 const link = document.createElement('a');
76 link.href = url;
77 link.download = 'skipped_"$"{Date.now()}.pdf';
78 link.click();
79 window.URL.revokeObjectURL(url);
80 return true;
81 }
82
83 throw new Error('Failed to download PDF');
84 }
85
86 async handleImportComplete(importId) {
87 const history = await this.getHistory();
88 const imp = history.data.find(i => i.id === importId);
89
90 if (!imp) throw new Error('Import not found');
91
92 console.log('Import completed: "$"{imp.status_text}');
93 console.log('Stored: "$"{imp.stored}, Skipped: "$"{imp.skipped}');
94
95 // Download skipped report if any
96 if (imp.skipped > 0 && imp.skipped_file_url) {
97 console.log('Downloading skipped records report...');
98 await this.downloadSkippedPDF(imp.skipped_file_url);
99 alert('"$"{imp.skipped} records were skipped. Check the downloaded PDF for details.');
100 } else {
101 alert('Import completed successfully with no skipped records!');
102 }
103
104 return imp;
105 }
106
107 async apiCall(method, endpoint, data = null) {
108 const headers = {
109 'X-API-SID': this.apiSid,
110 'X-API-PUBLIC-KEY': this.apiKey,
111 'Accept': 'application/json'
112 };
113
114 if (data) headers['Content-Type'] = 'application/json';
115
116 const response = await fetch(this.baseUrl + endpoint, {
117 method,
118 headers,
119 body: data ? JSON.stringify(data) : null
120 });
121
122 return await response.json();
123 }
124}
125
126// Usage
127const importMgr = new ImportManager('SID....................................', 'PK.....................................');
128
129// Monitor import with progress updates
130await importMgr.monitorImport(45, (progress) => {
131 console.log('"$"{progress.status}: "$"{progress.progress}% ("$"{progress.stored}/"$"{progress.total})');
132 // Update UI progress bar
133 updateProgressBar(progress.progress);
134});
135
136// Handle completion
137await importMgr.handleImportComplete(45);status field before assuming successremarks field to user for failed importsThe Blocked List Management API allows you to manage contacts who should not receive messages from your account. Add numbers to the blocked list, view blocked contacts, remove them from the list, export blocked contacts, or permanently delete blocked contact records.
Key Features:
added_byfilter to distinguish between the two.Retrieve a paginated list of all blocked contacts for your account. Results can be searched by phone number and filtered by how the contact was blocked (auto-blocked via opt-out words or manually added).
GET: /api/v1/contact/blocked-list/1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
limit | integer | No | Number of results per page (default: 10) |
page | integer | No | Page number for pagination (default: 1) |
search | string | No | Search term to filter by phone number (partial match) |
added_by | string | No | Filter by blocking method: auto (opt-out words) or manual(manually added) |
1curl -X GET "https://api.texttorrent.com/api/v1/contact/blocked-list/"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1curl -X GET "https://api.texttorrent.com/api/v1/contact/blocked-list/?search=555&added_by=manual&limit=20&page=1"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Blocked list fetched successfully",
5 "data": {
6 "blocked_list": {
7 "current_page": 1,
8 "data": [
9 {
10 "id": 1234,
11 "first_name": "John",
12 "last_name": "Doe",
13 "number": "+12025551234",
14 "email": "john.doe@example.com",
15 "company": null,
16 "user_id": 1,
17 "list_id": null,
18 "folder_id": null,
19 "blacklisted": 1,
20 "blacklisted_by": 1,
21 "blacklisted_at": "2025-10-15T14:30:00.000000Z",
22 "valid": 0,
23 "validation_process": 0,
24 "created_at": "2025-10-15T14:30:00.000000Z",
25 "updated_at": "2025-10-15T14:30:00.000000Z"
26 },
27 {
28 "id": 1235,
29 "first_name": "Jane",
30 "last_name": "Smith",
31 "number": "+12025555678",
32 "email": null,
33 "company": null,
34 "user_id": 1,
35 "list_id": null,
36 "folder_id": null,
37 "blacklisted": 1,
38 "blacklisted_by": null,
39 "blacklisted_at": "2025-10-14T10:15:00.000000Z",
40 "valid": 0,
41 "validation_process": 0,
42 "created_at": "2025-10-14T10:15:00.000000Z",
43 "updated_at": "2025-10-14T10:15:00.000000Z"
44 }
45 ],
46 "first_page_url": "https://api.texttorrent.com/api/v1/contact/blocked-list?page=1",
47 "from": 1,
48 "last_page": 5,
49 "last_page_url": "https://api.texttorrent.com/api/v1/contact/blocked-list?page=5",
50 "next_page_url": "https://api.texttorrent.com/api/v1/contact/blocked-list?page=2",
51 "path": "https://api.texttorrent.com/api/v1/contact/blocked-list",
52 "per_page": 10,
53 "prev_page_url": null,
54 "to": 10,
55 "total": 48
56 },
57 "total": 48,
58 "total_otp_out": 12
59 },
60 "errors": null
61}| Field | Type | Description |
|---|---|---|
blocked_list | object | Paginated list of blocked contacts |
total | integer | Total number of blocked contacts |
total_otp_out | integer | Total number of opt-out words configured |
| Field | Type | Description |
|---|---|---|
id | integer | Unique identifier for the contact |
number | string | Blocked phone number (with +1 prefix) |
blacklisted | integer | Always 1 for blocked contacts |
blacklisted_by | integer|null | User ID who manually blocked (null if auto-blocked via opt-out words) |
blacklisted_at | string | Timestamp when contact was blocked (ISO 8601 format) |
list_id | integer|null | Always null for blocked contacts (removed from lists) |
1async function getBlockedList(options = {}) {
2 const params = new URLSearchParams({
3 page: options.page || 1,
4 limit: options.limit || 10
5 });
6
7 if (options.search) {
8 params.append('search', options.search);
9 }
10
11 if (options.addedBy) {
12 params.append('added_by', options.addedBy);
13 }
14
15 const response = await fetch(
16 'https://api.texttorrent.com/api/v1/contact/blocked-list/?"$"{params}',
17 {
18 method: 'GET',
19 headers: {
20 'X-API-SID': 'SID....................................',
21 'X-API-PUBLIC-KEY': 'PK.....................................',
22 'Accept': 'application/json'
23 }
24 }
25 );
26
27 const data = await response.json();
28
29 if (data.success) {
30 console.log('Blocked contacts:', data.data.blocked_list.data);
31 console.log('Total blocked: "$"{data.data.total}');
32 console.log('Opt-out words configured: "$"{data.data.total_otp_out}');
33
34 // Categorize by blocking method
35 const manual = data.data.blocked_list.data.filter(c => c.blacklisted_by !== null);
36 const auto = data.data.blocked_list.data.filter(c => c.blacklisted_by === null);
37
38 console.log('Manually blocked: "$"{manual.length}, Auto-blocked: "$"{auto.length}');
39
40 return data.data;
41 } else {
42 console.error('Error:', data.message);
43 throw new Error(data.message);
44 }
45}
46
47// Usage - Get all blocked contacts
48getBlockedList();
49
50// Usage - Search for specific number
51getBlockedList({ search: '555' });
52
53// Usage - Get only manually blocked contacts
54getBlockedList({ addedBy: 'manual', limit: 20 });blacklisted_at in descending order (newest first)list_id set to null (not in any list)blacklisted_by null = auto-blocked via opt-out wordsblacklisted_by not null = manually added to blocked listAdd one or more phone numbers to the blocked list. Blocked numbers will not receive any messages from your account. This is useful for manually blocking contacts who request to opt-out or for compliance purposes.
POST: /api/v1/contact/blocked-list/1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
numbers | array | Yes | Array of phone numbers to block (10 digits, without +1 prefix) |
1curl -X POST "https://api.texttorrent.com/api/v1/contact/blocked-list/"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "numbers": ["2025551234"]
8 }'1curl -X POST "https://api.texttorrent.com/api/v1/contact/blocked-list/"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "numbers": ["2025551234", "2025555678", "2025559999"]
8 }'1{
2 "code": 201,
3 "success": true,
4 "message": "Blocklist create",
5 "data": true,
6 "errors": null
7}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "numbers": [
8 "The numbers field is required.",
9 "The numbers must be an array."
10 ],
11 "numbers.0": [
12 "The numbers.0 must be a string."
13 ]
14 }
15}blacklisted=1blacklisted_by is set to your user ID (indicates manual blocking)blacklisted_at is set to current timestamp1async function addToBlockedList(phoneNumbers) {
2 // Validate phone numbers
3 const validNumbers = phoneNumbers.filter(num => /^d{10}$/.test(num));
4
5 if (validNumbers.length === 0) {
6 throw new Error('No valid 10-digit phone numbers provided');
7 }
8
9 if (validNumbers.length !== phoneNumbers.length) {
10 console.warn('Some numbers were invalid and skipped');
11 }
12
13 const response = await fetch('https://api.texttorrent.com/api/v1/contact/blocked-list/', {
14 method: 'POST',
15 headers: {
16 'X-API-SID': 'SID....................................',
17 'X-API-PUBLIC-KEY': 'PK.....................................',
18 'Content-Type': 'application/json',
19 'Accept': 'application/json'
20 },
21 body: JSON.stringify({
22 numbers: validNumbers
23 })
24 });
25
26 const data = await response.json();
27
28 if (data.success) {
29 console.log('Successfully blocked "$"{validNumbers.length} number(s)');
30 alert('"$"{validNumbers.length} number(s) added to blocked list');
31 // Refresh blocked list
32 await getBlockedList();
33 return data;
34 } else {
35 console.error('Error:', data.message);
36 throw new Error(data.message);
37 }
38}
39
40// Usage - Block single number
41addToBlockedList(['2025551234']);
42
43// Usage - Block multiple numbers
44addToBlockedList(['2025551234', '2025555678', '2025559999']);1function addToBlockedList($phoneNumbers) {
2 $ch = curl_init('https://api.texttorrent.com/api/v1/contact/blocked-list/');
3
4 curl_setopt($ch, CURLOPT_POST, true);
5 curl_setopt($ch, CURLOPT_HTTPHEADER, [
6 'X-API-SID: SID....................................',
7 'X-API-PUBLIC-KEY: PK.....................................',
8 'Content-Type: application/json',
9 'Accept: application/json'
10 ]);
11 curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
12 'numbers' => $phoneNumbers
13 ]));
14 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
15
16 $response = curl_exec($ch);
17 curl_close($ch);
18
19 $data = json_decode($response, true);
20
21 if ($data['success']) {
22 echo 'Numbers blocked successfully';
23 return true;
24 } else {
25 echo 'Error: ' . $data['message'];
26 return false;
27 }
28}
29
30// Usage
31addToBlockedList(['2025551234', '2025555678']);blacklisted_by = your user IDRemove one or more contacts from the blocked list (unblock them). This sets their blacklistedstatus to 0, allowing them to receive messages again. The contact record remains in the system.
POST: /api/v1/contact/blocked-list/remove1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
contact_ids | array | Yes | Array of contact IDs to unblock (must exist in contacts table) |
1curl -X POST "https://api.texttorrent.com/api/v1/contact/blocked-list/remove"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "contact_ids": [1234]
8 }'1curl -X POST "https://api.texttorrent.com/api/v1/contact/blocked-list/remove"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "contact_ids": [1234, 1235, 1236]
8 }'1{
2 "code": 200,
3 "success": true,
4 "message": "Contact(s) removed from blocked list successfully",
5 "data": null,
6 "errors": null
7}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "contact_ids": [
8 "The contact ids field is required.",
9 "The contact ids must be an array."
10 ],
11 "contact_ids.0": [
12 "The selected contact ids.0 is invalid."
13 ]
14 }
15}blacklisted is set to 0 (contact is no longer blocked)blacklisted_by and blacklisted_at remain unchanged (for history)1async function removeFromBlockedList(contactIds) {
2 // Confirm unblocking
3 if (!confirm('Unblock "$"{contactIds.length} contact(s)? They will be able to receive messages again.')) {
4 return;
5 }
6
7 const response = await fetch('https://api.texttorrent.com/api/v1/contact/blocked-list/remove', {
8 method: 'POST',
9 headers: {
10 'X-API-SID': 'SID....................................',
11 'X-API-PUBLIC-KEY': 'PK.....................................',
12 'Content-Type': 'application/json',
13 'Accept': 'application/json'
14 },
15 body: JSON.stringify({
16 contact_ids: contactIds
17 })
18 });
19
20 const data = await response.json();
21
22 if (data.success) {
23 console.log('Contacts unblocked successfully');
24 alert('"$"{contactIds.length} contact(s) removed from blocked list');
25 // Refresh blocked list
26 await getBlockedList();
27 } else {
28 console.error('Error:', data.message);
29 alert('Failed to unblock contacts');
30 }
31}
32
33// Usage - Unblock single contact
34removeFromBlockedList([1234]);
35
36// Usage - Unblock multiple contacts
37removeFromBlockedList([1234, 1235, 1236]);blacklisted=0 but keeps contact recordPermanently delete blocked contact records from the system. This removes the contacts entirely, including all their information. Use this to clean up old blocked contacts you no longer need to track.
DELETE: /api/v1/contact/blocked-list/delete1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
contact_ids | array | Yes | Array of contact IDs to delete permanently (must exist in contacts table) |
1curl -X DELETE "https://api.texttorrent.com/api/v1/contact/blocked-list/delete" -H "X-API-SID: SID...................................." -H "X-API-PUBLIC-KEY: PK....................................." -H "Content-Type: application/json" -H "Accept: application/json" -d '{
2 "contact_ids": [1234, 1235]
3 }'1{
2 "code": 200,
3 "success": true,
4 "message": "Contact(s) deleted successfully",
5 "data": null,
6 "errors": null
7}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "contact_ids": [
8 "The contact ids field is required."
9 ],
10 "contact_ids.0": [
11 "The selected contact ids.0 is invalid."
12 ]
13 }
14}1async function deleteBlockedContacts(contactIds) {
2 // Confirm deletion
3 if (!confirm(
4 'Permanently delete "$"{contactIds.length} blocked contact(s)? ' +
5 'This cannot be undone and all contact information will be lost.'
6 )) {
7 return;
8 }
9
10 const response = await fetch('https://api.texttorrent.com/api/v1/contact/blocked-list/delete', {
11 method: 'DELETE',
12 headers: {
13 'X-API-SID': 'SID....................................',
14 'X-API-PUBLIC-KEY': 'PK.....................................',
15 'Content-Type': 'application/json',
16 'Accept': 'application/json'
17 },
18 body: JSON.stringify({
19 contact_ids: contactIds
20 })
21 });
22
23 const data = await response.json();
24
25 if (data.success) {
26 console.log('Blocked contacts deleted permanently');
27 alert('"$"{contactIds.length} blocked contact(s) deleted');
28 // Refresh blocked list
29 await getBlockedList();
30 } else {
31 console.error('Error:', data.message);
32 alert('Failed to delete contacts');
33 }
34}
35
36// Usage
37deleteBlockedContacts([1234, 1235]);Export blocked contacts to an Excel (.xlsx) file. You can export specific contacts or all blocked contacts. The export includes contact information, phone numbers, and blocking details.
POST: /api/v1/contact/blocked-list/export1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
contact_ids | array | Yes | Array of contact IDs to export (must exist in contacts table) |
1curl -X POST "https://api.texttorrent.com/api/v1/contact/blocked-list/export"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "contact_ids": [1234, 1235, 1236]
8 }'1{
2 "code": 200,
3 "success": true,
4 "message": "File generated successfully",
5 "data": {
6 "url": "https://your-storage.digitaloceanspaces.com/blocked-list/blocked_list_export_1729180845.xlsx"
7 },
8 "errors": null
9}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "contact_ids": [
8 "The contact ids field is required."
9 ],
10 "contact_ids.0": [
11 "The selected contact ids.0 is invalid."
12 ]
13 }
14}The exported Excel file typically includes:
1async function exportBlockedList(contactIds) {
2 const response = await fetch('https://api.texttorrent.com/api/v1/contact/blocked-list/export', {
3 method: 'POST',
4 headers: {
5 'X-API-SID': 'SID....................................',
6 'X-API-PUBLIC-KEY': 'PK.....................................',
7 'Content-Type': 'application/json',
8 'Accept': 'application/json'
9 },
10 body: JSON.stringify({
11 contact_ids: contactIds
12 })
13 });
14
15 const data = await response.json();
16
17 if (data.success) {
18 console.log('Export successful, downloading...');
19 // Trigger download
20 window.open(data.data.url, '_blank');
21 return data.data.url;
22 } else {
23 console.error('Export failed:', data.message);
24 throw new Error(data.message);
25 }
26}
27
28// Usage - Export specific contacts
29exportBlockedList([1234, 1235, 1236]);
30
31// Usage - Export all blocked contacts (get IDs from list first)
32async function exportAllBlocked() {
33 const blockedData = await getBlockedList({ limit: 1000 });
34 const allIds = blockedData.blocked_list.data.map(contact => contact.id);
35 await exportBlockedList(allIds);
36}blocked_list_export_ timestamp here.xlsxadded_by filter to distinguish manual vs auto-blocked contacts1// Complete blocked list management workflow
2class BlockedListManager {
3 constructor(apiSid, apiKey) {
4 this.apiSid = apiSid;
5 this.apiKey = apiKey;
6 this.baseUrl = 'https://api.texttorrent.com/api/v1';
7 }
8
9 async getList(options = {}) {
10 const params = new URLSearchParams({
11 page: options.page || 1,
12 limit: options.limit || 10
13 });
14
15 if (options.search) params.append('search', options.search);
16 if (options.addedBy) params.append('added_by', options.addedBy);
17
18 return await this.apiCall('GET', '/contact/blocked-list/?"$"{params}');
19 }
20
21 async blockNumbers(phoneNumbers) {
22 return await this.apiCall('POST', '/contact/blocked-list/', {
23 numbers: phoneNumbers
24 });
25 }
26
27 async unblock(contactIds) {
28 return await this.apiCall('POST', '/contact/blocked-list/remove', {
29 contact_ids: contactIds
30 });
31 }
32
33 async delete(contactIds) {
34 return await this.apiCall('DELETE', '/contact/blocked-list/delete', {
35 contact_ids: contactIds
36 });
37 }
38
39 async export(contactIds) {
40 const result = await this.apiCall('POST', '/contact/blocked-list/export', {
41 contact_ids: contactIds
42 });
43
44 if (result.success) {
45 window.open(result.data.url, '_blank');
46 }
47
48 return result;
49 }
50
51 async auditBlockedList() {
52 // Get all blocked contacts
53 const data = await this.getList({ limit: 1000 });
54 const blocked = data.data.blocked_list.data;
55
56 console.log('Total blocked contacts: "$"{data.data.total}');
57 console.log('Opt-out words configured: "$"{data.data.total_otp_out}');
58
59 // Categorize
60 const manual = blocked.filter(c => c.blacklisted_by !== null);
61 const auto = blocked.filter(c => c.blacklisted_by === null);
62
63 console.log('Manually blocked: "$"{manual.length}');
64 console.log('Auto-blocked: "$"{auto.length}');
65
66 // Export for records
67 const allIds = blocked.map(c => c.id);
68 await this.export(allIds);
69
70 return { total: data.data.total, manual: manual.length, auto: auto.length };
71 }
72
73 async apiCall(method, endpoint, data = null) {
74 const headers = {
75 'X-API-SID': this.apiSid,
76 'X-API-PUBLIC-KEY': this.apiKey,
77 'Accept': 'application/json'
78 };
79
80 if (data && method !== 'GET') {
81 headers['Content-Type'] = 'application/json';
82 }
83
84 const options = {
85 method,
86 headers,
87 body: data && method !== 'GET' ? JSON.stringify(data) : null
88 };
89
90 const response = await fetch(this.baseUrl + endpoint, options);
91 return await response.json();
92 }
93}
94
95// Usage
96const blockedMgr = new BlockedListManager('SID....................................', 'PK.....................................');
97
98// Block numbers
99await blockedMgr.blockNumbers(['2025551234', '2025555678']);
100
101// Get blocked list
102await blockedMgr.getList({ addedBy: 'manual' });
103
104// Unblock contacts
105await blockedMgr.unblock([1234, 1235]);
106
107// Delete contacts
108await blockedMgr.delete([1236]);
109
110// Export contacts
111await blockedMgr.export([1234, 1235, 1236]);
112
113// Run audit
114await blockedMgr.auditBlockedList();The Opt-Out Words Management API allows you to configure keywords that automatically block contacts when they reply with specific words or phrases. When a contact sends a message containing any of your opt-out words (like "STOP", "UNSUBSCRIBE", "CANCEL"), they are automatically added to the blocked list.
Key Features:
blacklisted status to 1blacklisted_by=null (auto-blocked)Opt-out words follow account hierarchy rules:
Retrieve a paginated list of all opt-out words configured for your account. Results can be searched by keyword and paginated for easy management.
GET: /api/v1/contact/opt-out-word1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
limit | integer | No | Number of results per page (default: 10) |
page | integer | No | Page number for pagination (default: 1) |
search | string | No | Search term to filter opt-out words (partial match) |
1curl -X GET "https://api.texttorrent.com/api/v1/contact/opt-out-word"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1curl -X GET "https://api.texttorrent.com/api/v1/contact/opt-out-word?search=STOP&limit=20&page=1"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Opt-out words fetched successfully",
5 "data": {
6 "current_page": 1,
7 "data": [
8 {
9 "id": 1,
10 "user_id": 1,
11 "word": "STOP",
12 "created_at": "2025-01-15T10:30:00.000000Z",
13 "updated_at": "2025-01-15T10:30:00.000000Z"
14 },
15 {
16 "id": 2,
17 "user_id": 1,
18 "word": "UNSUBSCRIBE",
19 "created_at": "2025-01-15T10:31:00.000000Z",
20 "updated_at": "2025-01-15T10:31:00.000000Z"
21 },
22 {
23 "id": 3,
24 "user_id": 1,
25 "word": "CANCEL",
26 "created_at": "2025-01-15T10:32:00.000000Z",
27 "updated_at": "2025-01-15T10:32:00.000000Z"
28 },
29 {
30 "id": 4,
31 "user_id": 1,
32 "word": "END",
33 "created_at": "2025-01-15T10:33:00.000000Z",
34 "updated_at": "2025-01-15T10:33:00.000000Z"
35 },
36 {
37 "id": 5,
38 "user_id": 1,
39 "word": "QUIT",
40 "created_at": "2025-01-15T10:34:00.000000Z",
41 "updated_at": "2025-01-15T10:34:00.000000Z"
42 },
43 {
44 "id": 6,
45 "user_id": 1,
46 "word": "OPT OUT",
47 "created_at": "2025-01-15T10:35:00.000000Z",
48 "updated_at": "2025-01-15T10:35:00.000000Z"
49 }
50 ],
51 "first_page_url": "https://api.texttorrent.com/api/v1/contact/opt-out-word?page=1",
52 "from": 1,
53 "last_page": 1,
54 "last_page_url": "https://api.texttorrent.com/api/v1/contact/opt-out-word?page=1",
55 "next_page_url": null,
56 "path": "https://api.texttorrent.com/api/v1/contact/opt-out-word",
57 "per_page": 10,
58 "prev_page_url": null,
59 "to": 6,
60 "total": 6
61 },
62 "errors": null
63}| Field | Type | Description |
|---|---|---|
id | integer | Unique identifier for the opt-out word |
user_id | integer | ID of the user who created this opt-out word |
word | string | The opt-out keyword or phrase (case-insensitive) |
created_at | string | Timestamp when opt-out word was created (ISO 8601 format) |
updated_at | string | Timestamp when opt-out word was last updated (ISO 8601 format) |
1async function getOptOutWords(options = {}) {
2 const params = new URLSearchParams({
3 page: options.page || 1,
4 limit: options.limit || 10
5 });
6
7 if (options.search) {
8 params.append('search', options.search);
9 }
10
11 const response = await fetch(
12 'https://api.texttorrent.com/api/v1/contact/opt-out-word?"$"{params}',
13 {
14 method: 'GET',
15 headers: {
16 'X-API-SID': 'SID....................................',
17 'X-API-PUBLIC-KEY': 'PK.....................................',
18 'Accept': 'application/json'
19 }
20 }
21 );
22
23 const data = await response.json();
24
25 if (data.success) {
26 console.log('Opt-out words:', data.data.data);
27 console.log('Total opt-out words: "$"{data.data.total}');
28 return data.data;
29 } else {
30 console.error('Error:', data.message);
31 throw new Error(data.message);
32 }
33}
34
35// Usage - Get all opt-out words
36getOptOutWords();
37
38// Usage - Search for specific word
39getOptOutWords({ search: 'STOP' });
40
41// Usage - Get with custom limit
42getOptOutWords({ limit: 50 });created_at in descending order (newest first)Add a new opt-out word or phrase to your account. When contacts reply with this word, they will be automatically blocked. The system prevents duplicate words across account hierarchies.
POST: /api/v1/contact/opt-out-word1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
word | string | Yes | Opt-out keyword or phrase (max 255 characters) |
1curl -X POST "https://api.texttorrent.com/api/v1/contact/opt-out-word"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "word": "STOP"
8 }'1curl -X POST "https://api.texttorrent.com/api/v1/contact/opt-out-word"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "word": "DO NOT CONTACT ME"
8 }'1{
2 "code": 200,
3 "success": true,
4 "message": "Opt-out word created successfully",
5 "data": {
6 "id": 7,
7 "user_id": 1,
8 "word": "STOP",
9 "created_at": "2025-10-17T14:30:00.000000Z",
10 "updated_at": "2025-10-17T14:30:00.000000Z"
11 },
12 "errors": null
13}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "word": [
8 "The word field is required.",
9 "The word must be a string.",
10 "The word may not be greater than 255 characters."
11 ]
12 }
13}1{
2 "code": 422,
3 "success": false,
4 "message": "This word already exists under your account.",
5 "data": null,
6 "errors": null
7}1{
2 "code": 422,
3 "success": false,
4 "message": "This word already exists under your parent or master account.",
5 "data": null,
6 "errors": null
7}1async function createOptOutWord(word) {
2 // Validate word
3 if (!word || word.trim().length === 0) {
4 throw new Error('Opt-out word cannot be empty');
5 }
6
7 if (word.length > 255) {
8 throw new Error('Opt-out word is too long (max 255 characters)');
9 }
10
11 const response = await fetch('https://api.texttorrent.com/api/v1/contact/opt-out-word', {
12 method: 'POST',
13 headers: {
14 'X-API-SID': 'SID....................................',
15 'X-API-PUBLIC-KEY': 'PK.....................................',
16 'Content-Type': 'application/json',
17 'Accept': 'application/json'
18 },
19 body: JSON.stringify({
20 word: word.trim().toUpperCase() // Standardize to uppercase
21 })
22 });
23
24 const data = await response.json();
25
26 if (data.success) {
27 console.log('Opt-out word "$"{word}" created successfully');
28 alert('Opt-out word added: "$"{word}');
29 return data.data;
30 } else {
31 console.error('Error:', data.message);
32 alert('Failed to add opt-out word: "$"{data.message}');
33 throw new Error(data.message);
34 }
35}
36
37// Usage - Create single opt-out word
38createOptOutWord('STOP');
39
40// Usage - Create common opt-out words
41const commonOptOutWords = ['STOP', 'UNSUBSCRIBE', 'CANCEL', 'END', 'QUIT', 'OPT OUT'];
42for (const word of commonOptOutWords) {
43 try {
44 await createOptOutWord(word);
45 } catch (error) {
46 console.log('Skipping "$"{word}": "$"{error.message}');
47 }
48}1function createOptOutWord($word) {
2 $ch = curl_init('https://api.texttorrent.com/api/v1/contact/opt-out-word');
3
4 curl_setopt($ch, CURLOPT_POST, true);
5 curl_setopt($ch, CURLOPT_HTTPHEADER, [
6 'X-API-SID: SID....................................',
7 'X-API-PUBLIC-KEY: PK.....................................',
8 'Content-Type: application/json',
9 'Accept: application/json'
10 ]);
11 curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
12 'word' => strtoupper(trim($word))
13 ]));
14 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
15
16 $response = curl_exec($ch);
17 curl_close($ch);
18
19 $data = json_decode($response, true);
20
21 if ($data['success']) {
22 echo "Opt-out word created: " . $word;
23 return $data['data'];
24 } else {
25 echo 'Error: ' . $data['message'];
26 return false;
27 }
28}
29
30// Usage
31createOptOutWord('STOP');For compliance and best practices, configure these standard opt-out words:
Update an existing opt-out word. This allows you to correct typos or change the wording of an opt-out keyword. The system validates uniqueness within the account hierarchy.
PUT: /api/v1/contact/opt-out-word/{id}1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | ID of the opt-out word to update |
| Parameter | Type | Required | Description |
|---|---|---|---|
word | string | Yes | New opt-out keyword or phrase (max 255 characters, must be unique) |
1curl -X PUT "https://api.texttorrent.com/api/v1/contact/opt-out-word/7"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "word": "STOP ALL"
8 }'1{
2 "code": 200,
3 "success": true,
4 "message": "Opt-out word updated successfully",
5 "data": {
6 "id": 7,
7 "user_id": 1,
8 "word": "STOP ALL",
9 "created_at": "2025-10-17T14:30:00.000000Z",
10 "updated_at": "2025-10-17T15:45:00.000000Z"
11 },
12 "errors": null
13}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "word": [
8 "The word field is required.",
9 "The word has already been taken."
10 ]
11 }
12}1{
2 "code": 404,
3 "success": false,
4 "message": "Opt-out word not found",
5 "data": null,
6 "errors": null
7}1async function updateOptOutWord(id, newWord) {
2 const response = await fetch('https://api.texttorrent.com/api/v1/contact/opt-out-word/"$"{id}', {
3 method: 'PUT',
4 headers: {
5 'X-API-SID': 'SID....................................',
6 'X-API-PUBLIC-KEY': 'PK.....................................',
7 'Content-Type': 'application/json',
8 'Accept': 'application/json'
9 },
10 body: JSON.stringify({
11 word: newWord.trim().toUpperCase()
12 })
13 });
14
15 const data = await response.json();
16
17 if (data.success) {
18 console.log('Opt-out word updated successfully');
19 return data.data;
20 } else {
21 console.error('Error:', data.message);
22 throw new Error(data.message);
23 }
24}
25
26// Usage
27updateOptOutWord(7, 'STOP ALL');Permanently delete one or more opt-out words from your account. Once deleted, incoming messages with these words will no longer trigger automatic blocking.
POST: /api/v1/contact/opt-out-word/delete1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
ids | array | Yes | Array of opt-out word IDs to delete (must exist in opt_out_words table) |
1curl -X POST "https://api.texttorrent.com/api/v1/contact/opt-out-word/delete"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "ids": [7]
8 }'1curl -X POST "https://api.texttorrent.com/api/v1/contact/opt-out-word/delete"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "ids": [7, 8, 9]
8 }'1{
2 "code": 200,
3 "success": true,
4 "message": "Opt-out word(s) deleted successfully",
5 "data": null,
6 "errors": null
7}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "ids": [
8 "The ids field is required.",
9 "The ids must be an array."
10 ],
11 "ids.0": [
12 "The selected ids.0 is invalid."
13 ]
14 }
15}1async function deleteOptOutWords(ids) {
2 // Confirm deletion
3 if (!confirm('Delete "$"{ids.length} opt-out word(s)? This cannot be undone.')) {
4 return;
5 }
6
7 const response = await fetch('https://api.texttorrent.com/api/v1/contact/opt-out-word/delete', {
8 method: 'POST',
9 headers: {
10 'X-API-SID': 'SID....................................',
11 'X-API-PUBLIC-KEY': 'PK.....................................',
12 'Content-Type': 'application/json',
13 'Accept': 'application/json'
14 },
15 body: JSON.stringify({
16 ids: ids
17 })
18 });
19
20 const data = await response.json();
21
22 if (data.success) {
23 console.log('Opt-out words deleted successfully');
24 alert('"$"{ids.length} opt-out word(s) deleted"');
25 // Refresh list
26 await getOptOutWords();
27 } else {
28 console.error('Error:', data.message);
29 alert('Failed to delete opt-out words');
30 }
31}
32
33// Usage - Delete single opt-out word
34deleteOptOutWords([7]);
35
36// Usage - Delete multiple opt-out words
37deleteOptOutWords([7, 8, 9]);Export opt-out words to a CSV file for backup, audit, or reporting purposes. You can export specific words or all words configured for your account.
POST: /api/v1/contact/opt-out-word/export1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Content-Type: application/json
4Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
ids | array | No | Array of opt-out word IDs to export (if omitted, exports all words) |
1curl -X POST "https://api.texttorrent.com/api/v1/contact/opt-out-word/export"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{
7 "ids": [1, 2, 3]
8 }'1curl -X POST "https://api.texttorrent.com/api/v1/contact/opt-out-word/export"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Content-Type: application/json"
5 -H "Accept: application/json"
6 -d '{}'1{
2 "code": 200,
3 "success": true,
4 "message": "Opt-out words exported successfully",
5 "data": {
6 "message": "Opt-out words exported successfully",
7 "file": "https://your-storage.digitaloceanspaces.com/opt-out-words/opt-out-words-2025-10-17_14-30-45.csv"
8 },
9 "errors": null
10}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "ids": [
8 "The ids must be an array."
9 ],
10 "ids.0": [
11 "The ids.0 must be an integer.",
12 "The selected ids.0 is invalid."
13 ]
14 }
15}The exported CSV file typically includes:
1async function exportOptOutWords(ids = null) {
2 const body = ids ? { ids } : {};
3
4 const response = await fetch('https://api.texttorrent.com/api/v1/contact/opt-out-word/export', {
5 method: 'POST',
6 headers: {
7 'X-API-SID': 'SID....................................',
8 'X-API-PUBLIC-KEY': 'PK.....................................',
9 'Content-Type': 'application/json',
10 'Accept': 'application/json'
11 },
12 body: JSON.stringify(body)
13 });
14
15 const data = await response.json();
16
17 if (data.success) {
18 console.log('Export successful, downloading...');
19 // Trigger download
20 window.open(data.data.file, '_blank');
21 return data.data.file;
22 } else {
23 console.error('Export failed:', data.message);
24 throw new Error(data.message);
25 }
26}
27
28// Usage - Export specific opt-out words
29exportOptOutWords([1, 2, 3]);
30
31// Usage - Export all opt-out words
32exportOptOutWords();
33
34// Usage - Export all with fetched IDs
35async function exportAllOptOutWords() {
36 const words = await getOptOutWords({ limit: 1000 });
37 const allIds = words.data.map(word => word.id);
38 await exportOptOutWords(allIds);
39}opt-out-words-YYYY-MM-DD_HH-MM-SS.csvids is omitted or empty, all words are exported| Word/Phrase | Priority | Reason |
|---|---|---|
STOP | Critical | Legally required by TCPA, most common opt-out word |
STOPALL | Critical | Used by carriers to stop all messages from account |
UNSUBSCRIBE | Critical | Email convention, widely recognized |
CANCEL | High | Common alternative to STOP |
END | High | Simple and clear opt-out word |
QUIT | High | Common opt-out request |
OPT OUT | Medium | Explicit two-word phrase |
REMOVE | Medium | Common list removal request |
1// Complete opt-out words management system
2class OptOutWordsManager {
3 constructor(apiSid, apiKey) {
4 this.apiSid = apiSid;
5 this.apiKey = apiKey;
6 this.baseUrl = 'https://api.texttorrent.com/api/v1';
7 }
8
9 async getAll(options = {}) {
10 const params = new URLSearchParams({
11 page: options.page || 1,
12 limit: options.limit || 50
13 });
14
15 if (options.search) params.append('search', options.search);
16
17 return await this.apiCall('GET', '/contact/opt-out-word?"$"{params}');
18 }
19
20 async create(word) {
21 return await this.apiCall('POST', '/contact/opt-out-word', { word });
22 }
23
24 async update(id, word) {
25 return await this.apiCall('PUT', '/contact/opt-out-word"$"{id}', { word });
26 }
27
28 async delete(ids) {
29 return await this.apiCall('POST', '/contact/opt-out-worddelete', { ids });
30 }
31
32 async export(ids = null) {
33 const result = await this.apiCall('POST', '/contact/opt-out-wordexport',
34 ids ? { ids } : {}
35 );
36
37 if (result.success) {
38 window.open(result.data.file, '_blank');
39 }
40
41 return result;
42 }
43
44 async setupCompliance() {
45 // Configure all essential opt-out words for compliance
46 const essentialWords = [
47 'STOP',
48 'STOPALL',
49 'UNSUBSCRIBE',
50 'CANCEL',
51 'END',
52 'QUIT',
53 'OPT OUT',
54 'REMOVE'
55 ];
56
57 console.log('Setting up compliance opt-out words...');
58
59 for (const word of essentialWords) {
60 try {
61 await this.create(word);
62 console.log('✓ Added: "$"{word}');
63 } catch (error) {
64 console.log('Skipped "$"{word}: "$"{error.message}');
65 }
66 }
67
68 console.log('Compliance setup complete!');
69
70 // Verify all words are configured
71 const current = await this.getAll({ limit: 100 });
72 console.log('Total opt-out words configured: "$"{current.data.total}');
73
74 return current;
75 }
76
77 async apiCall(method, endpoint, data = null) {
78 const headers = {
79 'X-API-SID': this.apiSid,
80 'X-API-PUBLIC-KEY': this.apiKey,
81 'Accept': 'application/json'
82 };
83
84 if (data && method !== 'GET') {
85 headers['Content-Type'] = 'application/json';
86 }
87
88 const options = {
89 method,
90 headers,
91 body: data && method !== 'GET' ? JSON.stringify(data) : null
92 };
93
94 const response = await fetch(this.baseUrl + endpoint, options);
95 const result = await response.json();
96
97 if (!result.success) {
98 throw new Error(result.message);
99 }
100
101 return result;
102 }
103}
104
105// Usage
106const optOutMgr = new OptOutWordsManager('SID....................................', 'PK.....................................');
107
108// Setup compliance words
109await optOutMgr.setupCompliance();
110
111// Get all opt-out words
112await optOutMgr.getAll();
113
114// Add custom word
115await optOutMgr.create('DO NOT CONTACT');
116
117// Update word
118await optOutMgr.update(7, 'STOP MESSAGING');
119
120// Delete words
121await optOutMgr.delete([7, 8]);
122
123// Export all words for backup
124await optOutMgr.export();blacklisted_by=nullThe Inbox & Messaging API provides a complete solution for managing SMS conversations with your contacts. View conversation threads, send and receive messages, manage templates, and track message status in real-time.
Key Features:
Retrieve a paginated list of all conversation threads in your inbox. Each conversation shows the contact details, last message, unread count, and timestamp. Results can be searched, filtered by folder, time range, and unread status.
GET: /api/v1/inbox1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
limit | integer | No | Number of results per page (default: 10) |
page | integer | No | Page number for pagination (default: 1) |
search | string | No | Search by contact name, number, email, company, or message content |
folder | integer | No | Filter by folder ID |
time | string | No | Filter by time: today, last_week, last_month,last_year |
unread | boolean | No | Show only conversations with unread messages (default: false) |
email | string | No | Base64 encoded email address of a sub-user. Main accounts can use this to retrieve inbox messages belonging to their sub-users. |
email parameter. If the provided email does not belong to a sub-user associated with your main account, a 403 Forbidden response will be returned.1curl -X GET "https://api.texttorrent.com/api/v1/inbox"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1# First, encode the sub-user's email in Base64
2# Example: subuser@example.com → c3VidXNlckBleGFtcGxlLmNvbQ==
3
4curl -X GET "https://api.texttorrent.com/api/v1/inbox?email=c3VidXNlckBleGFtcGxlLmNvbQ=="
5 -H "X-API-SID: SID...................................."
6 -H "X-API-PUBLIC-KEY: PK....................................."
7 -H "Accept: application/json"1curl -X GET "https://api.texttorrent.com/api/v1/inbox?search=john&folder=5&time=last_week&unread=true&limit=20"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Chats retrieved successfully",
5 "data": {
6 "current_page": 1,
7 "data": [
8 {
9 "chat_id": 1234,
10 "contact_id": 567,
11 "first_name": "John",
12 "last_name": "Doe",
13 "number": "+12025551234",
14 "email": "john.doe@example.com",
15 "company": "Acme Corp",
16 "folder_id": 5,
17 "last_message": "Thanks for the update!",
18 "last_chat_time": "2025-10-17T14:30:00.000000Z",
19 "unread_count": 3,
20 "send_by": "contact",
21 "avatar_ltr": "JD"
22 },
23 {
24 "chat_id": 1235,
25 "contact_id": 568,
26 "first_name": "Jane",
27 "last_name": "Smith",
28 "number": "+12025555678",
29 "email": "jane.smith@example.com",
30 "company": null,
31 "folder_id": null,
32 "last_message": "Got it, will check tomorrow",
33 "last_chat_time": "2025-10-17T13:15:00.000000Z",
34 "unread_count": 0,
35 "send_by": "me",
36 "avatar_ltr": "JS"
37 }
38 ],
39 "first_page_url": "https://api.texttorrent.com/api/v1/inbox?page=1",
40 "from": 1,
41 "last_page": 5,
42 "last_page_url": "https://api.texttorrent.com/api/v1/inbox?page=5",
43 "next_page_url": "https://api.texttorrent.com/api/v1/inbox?page=2",
44 "path": "https://api.texttorrent.com/api/v1/inbox",
45 "per_page": 10,
46 "prev_page_url": null,
47 "to": 10,
48 "total": 48
49 },
50 "errors": null
51}1{
2 "code": 403,
3 "success": false,
4 "message": "Unauthorized access",
5 "data": null,
6 "errors": null
7}| Field | Type | Description |
|---|---|---|
chat_id | integer | Unique identifier for the conversation thread |
contact_id | integer | ID of the contact in this conversation |
last_message | string | Most recent message content in the conversation |
last_chat_time | string | Timestamp of the last message (ISO 8601 format) |
unread_count | integer | Number of unread messages in this conversation |
send_by | string | Who sent the last message: me or contact |
avatar_ltr | string | Contact initials for avatar display (e.g., "JD") |
1async function getInboxMessages(options = {}) {
2 const params = new URLSearchParams({
3 page: options.page || 1,
4 limit: options.limit || 10
5 });
6
7 if (options.search) params.append('search', options.search);
8 if (options.folder) params.append('folder', options.folder);
9 if (options.time) params.append('time', options.time);
10 if (options.unread) params.append('unread', 'true');
11
12 const response = await fetch(
13 'https://api.texttorrent.com/api/v1/inbox?"$"{params}',
14 {
15 method: 'GET',
16 headers: {
17 'X-API-SID': 'SID....................................',
18 'X-API-PUBLIC-KEY': 'PK.....................................',
19 'Accept': 'application/json'
20 }
21 }
22 );
23
24 const data = await response.json();
25
26 if (data.success) {
27 console.log('Conversations:', data.data.data);
28 console.log('Total conversations: "$"{data.data.total}');
29
30 // Count unread messages
31 const totalUnread = data.data.data.reduce((sum, chat) => sum + chat.unread_count, 0);
32 console.log('Total unread messages: "$"{totalUnread}');
33
34 return data.data;
35 } else {
36 console.error('Error:', data.message);
37 throw new Error(data.message);
38 }
39}
40
41// Usage - Get all conversations
42getInboxMessages();
43
44// Usage - Get unread conversations only
45getInboxMessages({ unread: true });
46
47// Usage - Search conversations
48getInboxMessages({ search: 'john' });
49
50// Usage - Filter by folder and time
51getInboxMessages({ folder: 5, time: 'last_week' });last_chat_time in descending order (most recent first)send_by indicates who sent the most recent messageemail parameter is providedemail parameterecho -n "subuser@example.com" | base64 (Linux/Mac) or use an online Base64 encoderRetrieve the most recently active conversation. This is useful for quickly resuming the last conversation you were engaged in, or for showing a "Continue last conversation" feature in your UI.
GET: /api/v1/inbox/last-chat1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json1curl -X GET "https://api.texttorrent.com/api/v1/inbox/last-chat"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Last chat retrieved successfully",
5 "data": {
6 "chat_id": 1234,
7 "contact_id": 567,
8 "contact_name": "John Doe",
9 "contact_phone_number": "+12025551234",
10 "last_message": "Thanks for the update!",
11 "last_chat_time": "2025-10-17T14:30:00.000000Z"
12 },
13 "errors": null
14}1{
2 "code": 200,
3 "success": true,
4 "message": "Last chat retrieved successfully",
5 "data": null,
6 "errors": null
7}1async function getLastChat() {
2 const response = await fetch('https://api.texttorrent.com/api/v1/inbox/last-chat', {
3 method: 'GET',
4 headers: {
5 'X-API-SID': 'SID....................................',
6 'X-API-PUBLIC-KEY': 'PK.....................................',
7 'Accept': 'application/json'
8 }
9 });
10
11 const data = await response.json();
12
13 if (data.success) {
14 if (data.data) {
15 console.log('Last active chat:', data.data);
16 console.log('Continue conversation with "$"{data.data.contact_name}');
17 return data.data;
18 } else {
19 console.log('No previous conversations found');
20 return null;
21 }
22 } else {
23 console.error('Error:', data.message);
24 throw new Error(data.message);
25 }
26}
27
28// Usage
29const lastChat = await getLastChat();
30if (lastChat) {
31 // Navigate to the conversation
32 window.location.href = '/inbox/"$"{lastChat.chat_id}';
33}updated_at timestampnull if no conversations existRetrieve all message templates configured for your account. Templates allow you to save frequently used messages for quick replies. Search templates by name to find specific templates quickly.
GET: /api/v1/inbox/templates1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
search | string | No | Search templates by name (partial match) |
email | string | No | Base64 encoded email address of a sub-user. Main accounts can use this to retrieve message templates belonging to their sub-users. |
email parameter. If the provided email does not belong to a sub-user associated with your main account, a 403 Forbidden response will be returned.1curl -X GET "https://api.texttorrent.com/api/v1/inbox/templates"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1# First, encode the sub-user's email in Base64
2# Example: subuser@example.com → c3VidXNlckBleGFtcGxlLmNvbQ==
3
4curl -X GET "https://api.texttorrent.com/api/v1/inbox/templates?email=c3VidXNlckBleGFtcGxlLmNvbQ=="
5 -H "X-API-SID: SID...................................."
6 -H "X-API-PUBLIC-KEY: PK....................................."
7 -H "Accept: application/json"1curl -X GET "https://api.texttorrent.com/api/v1/inbox/templates?search=welcome"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Templates retrieved successfully",
5 "data": [
6 {
7 "id": 1,
8 "user_id": 1,
9 "template_name": "Welcome Message",
10 "status": 1,
11 "preview_message": "Welcome to our service! We're excited to have you on board.",
12 "created_at": "2025-10-01T10:30:00.000000Z"
13 },
14 {
15 "id": 2,
16 "user_id": 1,
17 "template_name": "Follow Up",
18 "status": 1,
19 "preview_message": "Just following up on our previous conversation. Let me know if you have any questions!",
20 "created_at": "2025-10-05T14:20:00.000000Z"
21 },
22 {
23 "id": 3,
24 "user_id": 1,
25 "template_name": "Thank You",
26 "status": 1,
27 "preview_message": "Thank you for your business! We appreciate your support.",
28 "created_at": "2025-10-10T09:15:00.000000Z"
29 }
30 ],
31 "errors": null
32}1{
2 "code": 403,
3 "success": false,
4 "message": "Unauthorized access",
5 "data": null,
6 "errors": null
7}| Field | Type | Description |
|---|---|---|
id | integer | Unique identifier for the template |
template_name | string | Name/title of the template |
status | integer | Template status: 1 = active, 0 = inactive |
preview_message | string | The template message content |
created_at | string | Timestamp when template was created (ISO 8601 format) |
1async function getMessageTemplates(searchTerm = null) {
2 const params = new URLSearchParams();
3 if (searchTerm) params.append('search', searchTerm);
4
5 const url = searchTerm
6 ? 'https://api.texttorrent.com/api/v1/inbox/templates?"$"{params}'
7 : 'https://api.texttorrent.com/api/v1/inbox/templates';
8
9 const response = await fetch(url, {
10 method: 'GET',
11 headers: {
12 'X-API-SID': 'SID....................................',
13 'X-API-PUBLIC-KEY': 'PK.....................................',
14 'Accept': 'application/json'
15 }
16 });
17
18 const data = await response.json();
19
20 if (data.success) {
21 console.log('Message templates:', data.data);
22
23 // Display templates in a dropdown
24 const activeTemplates = data.data.filter(t => t.status === 1);
25 console.log('"$"{activeTemplates.length} active templates available');
26
27 return data.data;
28 } else {
29 console.error('Error:', data.message);
30 throw new Error(data.message);
31 }
32}
33
34// Usage - Get all templates
35const templates = await getMessageTemplates();
36
37// Usage - Search templates
38const welcomeTemplates = await getMessageTemplates('welcome');
39
40// Usage - Create template selector UI
41function createTemplateSelector(templates) {
42 const select = document.createElement('select');
43 select.innerHTML = 'Select a template...';
44
45 templates.forEach(template => {
46 if (template.status === 1) {
47 const option = document.createElement('option');
48 option.value = template.preview_message;
49 option.textContent = template.template_name;
50 select.appendChild(option);
51 }
52 });
53
54 select.addEventListener('change', (e) => {
55 if (e.target.value) {
56 document.getElementById('message-input').value = e.target.value;
57 }
58 });
59
60 return select;
61}status=1, inactive have status=0email parameter is providedemail parameterecho -n "subuser@example.com" | base64 (Linux/Mac) or use an online Base64 encoderRetrieve all active phone numbers in your account that can be used to send messages. Each number includes details about who purchased it, when it was purchased, and its capabilities.
GET: /api/v1/inbox/numbers/active1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
limit | integer | No | Number of results per page (default: 10) |
page | integer | No | Page number for pagination (default: 1) |
1curl -X GET "https://api.texttorrent.com/api/v1/inbox/numbers/active?limit=20"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Active numbers retrieved successfully",
5 "data": {
6 "current_page": 1,
7 "data": [
8 {
9 "id": 1,
10 "user_id": 1,
11 "purchased_by_user": "John Admin",
12 "purchased_by": 1,
13 "status": 1,
14 "created_at": "2025-09-15T10:30:00.000000Z",
15 "number": "+12025551234",
16 "friendly_name": "Sales Line",
17 "region": "DC",
18 "country": "US",
19 "latitude": "38.895",
20 "longitude": "-77.036",
21 "postal_code": "20001",
22 "capabilities": {
23 "voice": true,
24 "sms": true,
25 "mms": true
26 },
27 "purchased_at": "2025-09-15T10:30:00.000000Z",
28 "twilio_number_sid": "PNxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
29 "twilio_service_sid": "MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
30 "type": "local"
31 },
32 {
33 "id": 2,
34 "user_id": 1,
35 "purchased_by_user": "Jane Manager",
36 "purchased_by": 5,
37 "status": 1,
38 "created_at": "2025-09-20T14:20:00.000000Z",
39 "number": "+12025555678",
40 "friendly_name": "Support Line",
41 "region": "DC",
42 "country": "US",
43 "latitude": "38.895",
44 "longitude": "-77.036",
45 "postal_code": "20001",
46 "capabilities": {
47 "voice": true,
48 "sms": true,
49 "mms": true
50 },
51 "purchased_at": "2025-09-20T14:20:00.000000Z",
52 "twilio_number_sid": "PNyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
53 "twilio_service_sid": "MGyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
54 "type": "local"
55 }
56 ],
57 "first_page_url": "https://api.texttorrent.com/api/v1/inbox/numbers/active?page=1",
58 "from": 1,
59 "last_page": 2,
60 "last_page_url": "https://api.texttorrent.com/api/v1/inbox/numbers/active?page=2",
61 "next_page_url": "https://api.texttorrent.com/api/v1/inbox/numbers/active?page=2",
62 "path": "https://api.texttorrent.com/api/v1/inbox/numbers/active",
63 "per_page": 10,
64 "prev_page_url": null,
65 "to": 10,
66 "total": 15
67 },
68 "errors": null
69}| Field | Type | Description |
|---|---|---|
number | string | Phone number in E.164 format (e.g., +12025551234) |
friendly_name | string | Custom name for the number (e.g., "Sales Line") |
status | integer | Number status: 1 = active, 0 = inactive |
purchased_by_user | string | Full name of user who purchased the number |
capabilities | object | Number capabilities: voice, sms, mms |
type | string | Number type: local, toll-free, mobile |
twilio_number_sid | string | Twilio number SID |
1async function getActiveNumbers(options = {}) {
2 const params = new URLSearchParams({
3 page: options.page || 1,
4 limit: options.limit || 10
5 });
6
7 const response = await fetch(
8 'https://api.texttorrent.com/api/v1/inbox/numbers/active?"$"{params}',
9 {
10 method: 'GET',
11 headers: {
12 'X-API-SID': 'SID....................................',
13 'X-API-PUBLIC-KEY': 'PK.....................................',
14 'Accept': 'application/json'
15 }
16 }
17 );
18
19 const data = await response.json();
20
21 if (data.success) {
22 console.log('Active numbers:', data.data.data);
23 console.log('Total active numbers: "$"{data.data.total}');
24
25 // Create sender number dropdown
26 const smsCapableNumbers = data.data.data.filter(n => n.capabilities.sms);
27 console.log('"$"{smsCapableNumbers.length} numbers can send SMS');
28
29 return data.data;
30 } else {
31 console.error('Error:', data.message);
32 throw new Error(data.message);
33 }
34}
35
36// Usage
37const numbers = await getActiveNumbers();
38
39// Usage - Create from-number selector
40function createFromNumberSelector(numbers) {
41 const select = document.createElement('select');
42 select.innerHTML = 'Select sender number...';
43
44 numbers.data.forEach(number => {
45 if (number.status === 1 && number.capabilities.sms) {
46 const option = document.createElement('option');
47 option.value = number.number;
48 option.textContent = '"$"{number.friendly_name} ("$"{number.number})
49 ';
50 select.appendChild(option);
51 }
52 });
53
54 return select;
55}status=1 (active)user_id matches)capabilities.sms to verify SMS capabilitynumber field as the "from" number when sending messagesRetrieve contacts who don't have an existing conversation thread yet. This is useful for starting new conversations - it shows contacts you can message but haven't messaged yet.
GET: /api/v1/inbox/numbers/receiver1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json1curl -X GET "https://api.texttorrent.com/api/v1/inbox/numbers/receiver"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Receiver numbers retrieved successfully",
5 "data": [
6 {
7 "id": 789,
8 "user_id": 1,
9 "first_name": "Alice",
10 "last_name": "Johnson",
11 "number": "+12025559876",
12 "email": "alice.johnson@example.com",
13 "company": "Tech Solutions",
14 "list_id": 10,
15 "folder_id": null,
16 "blacklisted": 0,
17 "created_at": "2025-10-15T10:30:00.000000Z",
18 "updated_at": "2025-10-15T10:30:00.000000Z"
19 },
20 {
21 "id": 790,
22 "user_id": 1,
23 "first_name": "Bob",
24 "last_name": "Williams",
25 "number": "+12025559877",
26 "email": "bob.williams@example.com",
27 "company": null,
28 "list_id": 10,
29 "folder_id": 3,
30 "blacklisted": 0,
31 "created_at": "2025-10-16T14:20:00.000000Z",
32 "updated_at": "2025-10-16T14:20:00.000000Z"
33 }
34 ],
35 "errors": null
36}1async function getReceiverNumbers() {
2 const response = await fetch('https://api.texttorrent.com/api/v1/inbox/numbers/receiver', {
3 method: 'GET',
4 headers: {
5 'X-API-SID': 'SID....................................',
6 'X-API-PUBLIC-KEY': 'PK.....................................',
7 'Accept': 'application/json'
8 }
9 });
10
11 const data = await response.json();
12
13 if (data.success) {
14 console.log('Available receivers:', data.data);
15 console.log('"$"{data.data.length} contacts available for new conversations');
16 return data.data;
17 } else {
18 console.error('Error:', data.message);
19 throw new Error(data.message);
20 }
21}
22
23// Usage - Get contacts for starting new conversations
24const availableContacts = await getReceiverNumbers();
25
26// Usage - Create "Start New Chat" contact selector
27function createNewChatSelector(contacts) {
28 const select = document.createElement('select');
29 select.innerHTML = 'Select contact to message...';
30
31 contacts.forEach(contact => {
32 const option = document.createElement('option');
33 option.value = contact.id;
34 option.textContent = '"$"{contact.first_name} "$"{contact.last_name} ("$"{contact.number})';
35 select.appendChild(option);
36 });
37
38 select.addEventListener('change', async (e) => {
39 if (e.target.value) {
40 // Start new conversation with selected contact
41 await startNewChat(e.target.value);
42 }
43 });
44
45 return select;
46}Retrieve full details of a specific conversation including contact information, notes, and paginated message history. Messages are automatically marked as read when you view the conversation.
GET: /api/v1/inbox/{id}1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | Chat ID (conversation ID) |
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | integer | No | Number of messages per page (default: 10) |
page | integer | No | Page number for message pagination (default: 1) |
1curl -X GET "https://api.texttorrent.com/api/v1/inbox/1234?limit=20"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Chat retrieved successfully",
5 "data": {
6 "chat": {
7 "id": 567,
8 "user_id": 1,
9 "first_name": "John",
10 "last_name": "Doe",
11 "number": "+12025551234",
12 "email": "john.doe@example.com",
13 "company": "Acme Corp",
14 "list_id": 10,
15 "folder_id": 5,
16 "blacklisted": 0,
17 "contact_id": 567,
18 "from_number": "+12025559999",
19 "chat_id": 1234,
20 "avatar_ltr": "JD",
21 "created_at": "2025-09-15T10:30:00.000000Z",
22 "updated_at": "2025-10-17T14:30:00.000000Z",
23 "notes": [
24 {
25 "id": 1,
26 "note": "VIP customer - priority support"
27 },
28 {
29 "id": 2,
30 "note": "Interested in premium features"
31 }
32 ]
33 },
34 "messages": {
35 "current_page": 1,
36 "data": [
37 {
38 "id": 5001,
39 "chat_id": 1234,
40 "message": "Thanks for the update!",
41 "direction": "inbound",
42 "status": 1,
43 "from_number": "+12025551234",
44 "to_number": "+12025559999",
45 "media_url": null,
46 "created_at": "2025-10-17T14:30:00.000000Z",
47 "updated_at": "2025-10-17T14:30:05.000000Z"
48 },
49 {
50 "id": 5000,
51 "chat_id": 1234,
52 "message": "Your order has been shipped!",
53 "direction": "outbound",
54 "status": 1,
55 "from_number": "+12025559999",
56 "to_number": "+12025551234",
57 "media_url": null,
58 "created_at": "2025-10-17T14:25:00.000000Z",
59 "updated_at": "2025-10-17T14:25:05.000000Z"
60 }
61 ],
62 "first_page_url": "https://api.texttorrent.com/api/v1/inbox/1234?page=1",
63 "from": 1,
64 "last_page": 3,
65 "last_page_url": "https://api.texttorrent.com/api/v1/inbox/1234?page=3",
66 "next_page_url": "https://api.texttorrent.com/api/v1/inbox/1234?page=2",
67 "path": "https://api.texttorrent.com/api/v1/inbox/1234",
68 "per_page": 10,
69 "prev_page_url": null,
70 "to": 10,
71 "total": 28
72 }
73 },
74 "errors": null
75}1{
2 "code": 200,
3 "success": true,
4 "message": "Chat not found",
5 "data": null,
6 "errors": null
7}| Field | Type | Description |
|---|---|---|
direction | string | inbound (received) or outbound (sent) |
status | integer | 0 = unread, 1 = read |
from_number | string | Sender's phone number |
to_number | string | Recipient's phone number |
media_url | string|null | URL to media attachment (for MMS) |
1async function getChatDetails(chatId, options = {}) {
2 const params = new URLSearchParams({
3 page: options.page || 1,
4 limit: options.limit || 10
5 });
6
7 const response = await fetch(
8 'https://api.texttorrent.com/api/v1/inbox/"$"{chatId}?"$"{params}',
9 {
10 method: 'GET',
11 headers: {
12 'X-API-SID': 'SID....................................',
13 'X-API-PUBLIC-KEY': 'PK.....................................',
14 'Accept': 'application/json'
15 }
16 }
17 );
18
19 const data = await response.json();
20
21 if (data.success && data.data) {
22 const { chat, messages } = data.data;
23
24 console.log('Contact:', '"$"{chat.first_name} "$"{chat.last_name}');
25 console.log('Notes:', chat.notes);
26 console.log('Messages:', messages.data);
27 console.log('Total messages: "$"{messages.total}');
28
29 return data.data;
30 } else {
31 console.log('Chat not found');
32 return null;
33 }
34}
35
36// Usage
37const chatDetails = await getChatDetails(1234);
38
39// Usage - Load more messages (pagination)
40const moreMessages = await getChatDetails(1234, { page: 2, limit: 20 });
41
42// Usage - Display conversation UI
43function displayConversation(chatData) {
44 if (!chatData) return;
45
46 const { chat, messages } = chatData;
47
48 // Display contact info
49 document.getElementById('contact-name').textContent =
50 '"$"{chat.first_name} "$"{chat.last_name}';
51 document.getElementById('contact-number').textContent = chat.number;
52
53 // Display notes
54 const notesContainer = document.getElementById('notes');
55 notesContainer.innerHTML = chat.notes
56 .map(note => '"$"{note.note}')
57 .join('');
58
59 // Display messages
60 const messagesContainer = document.getElementById('messages');
61 messagesContainer.innerHTML = messages.data
62 .reverse() // Show oldest first
63 .map(msg => '
64 "$"{msg.message}
65 "$"{new Date(msg.created_at).toLocaleString()}
66 "$"{msg.media_url ? ' : ''}
67
68 ')
69 .join('');
70}created_at DESC (newest first in API response)null if chat doesn't exist or doesn't belong to youavatar_ltr provides initials for avatar displayPermanently delete a conversation thread and all associated messages. This removes the chat from your inbox but does not delete the contact record.
DELETE: /api/v1/inbox/{id}1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | Chat ID (conversation ID) to delete |
1curl -X DELETE "https://api.texttorrent.com/api/v1/inbox/1234"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Chat deleted successfully",
5 "data": null,
6 "errors": null
7}1async function deleteChat(chatId) {
2 // Confirm deletion
3 if (!confirm('Delete this conversation? All messages will be permanently removed.')) {
4 return;
5 }
6
7 const response = await fetch('https://api.texttorrent.com/api/v1/inbox/"$"{chatId}', {
8 method: 'DELETE',
9 headers: {
10 'X-API-SID': 'SID....................................',
11 'X-API-PUBLIC-KEY': 'PK.....................................',
12 'Accept': 'application/json'
13 }
14 });
15
16 const data = await response.json();
17
18 if (data.success) {
19 console.log('Chat deleted successfully');
20 alert('Conversation deleted');
21 // Redirect to inbox
22 window.location.href = '/inbox';
23 } else {
24 console.error('Error:', data.message);
25 alert('Failed to delete conversation');
26 }
27}
28
29// Usage
30deleteChat(1234);unread=true filter to track pending messagesFor real-time message updates, implement polling or webhooks:
1// Polling example - check for new messages every 10 seconds
2let lastCheckTime = new Date();
3
4async function pollNewMessages() {
5 const chats = await getInboxMessages({
6 time: 'today',
7 limit: 50
8 });
9
10 // Find conversations updated since last check
11 const newMessages = chats.data.filter(chat =>
12 new Date(chat.last_chat_time) > lastCheckTime
13 );
14
15 if (newMessages.length > 0) {
16 console.log('"$"{newMessages.length} new message(s)');
17 // Update UI
18 refreshInbox();
19 // Play notification sound
20 playNotificationSound();
21 }
22
23 lastCheckTime = new Date();
24}
25
26// Poll every 10 seconds
27setInterval(pollNewMessages, 10000);1class InboxManager {
2 constructor(apiSid, apiKey) {
3 this.apiSid = apiSid;
4 this.apiKey = apiKey;
5 this.baseUrl = 'https://api.texttorrent.com/api/v1';
6 }
7
8 async getConversations(filters = {}) {
9 const params = new URLSearchParams(filters);
10 return await this.apiCall('GET', '/inbox?"$"{params}');
11 }
12
13 async getLastChat() {
14 return await this.apiCall('GET', '/inbox/last-chat');
15 }
16
17 async getTemplates(search = null) {
18 const params = search ? '?search="$"{search}' : '';
19 return await this.apiCall('GET', '/inbox/templates"$"{params}');
20 }
21
22 async getActiveNumbers() {
23 return await this.apiCall('GET', '/inbox/numbers/active');
24 }
25
26 async getReceiverNumbers() {
27 return await this.apiCall('GET', '/inbox/numbers/receiver');
28 }
29
30 async getChatDetails(chatId, page = 1, limit = 10) {
31 return await this.apiCall('GET', '/inbox/"$"{chatId}?page="$"{page}&limit="$"{limit}');
32 }
33
34 async deleteChat(chatId) {
35 return await this.apiCall('DELETE', '/inbox/"$"{chatId}');
36 }
37
38 async apiCall(method, endpoint, data = null) {
39 const headers = {
40 'X-API-SID': this.apiSid,
41 'X-API-PUBLIC-KEY': this.apiKey,
42 'Accept': 'application/json'
43 };
44
45 if (data && method !== 'GET') {
46 headers['Content-Type'] = 'application/json';
47 }
48
49 const options = {
50 method,
51 headers,
52 body: data && method !== 'GET' ? JSON.stringify(data) : null
53 };
54
55 const response = await fetch(this.baseUrl + endpoint, options);
56 const result = await response.json();
57
58 if (!result.success) {
59 throw new Error(result.message);
60 }
61
62 return result;
63 }
64}
65
66// Usage
67const inbox = new InboxManager('SID....................................', 'PK.....................................');
68
69// Get unread conversations
70const unreadChats = await inbox.getConversations({ unread: true });
71
72// Resume last conversation
73const lastChat = await inbox.getLastChat();
74if (lastChat.data) {
75 const chatDetails = await inbox.getChatDetails(lastChat.data.chat_id);
76}
77
78// Get templates for quick replies
79const templates = await inbox.getTemplates();
80
81// Delete old conversations
82await inbox.deleteChat(1234);Send an SMS or MMS message to a contact in an existing conversation. Supports both text messages and media attachments (MMS). Messages are automatically cleaned using AI to fix encoding issues, and credits are deducted based on message type and gateway.
POST: /api/v1/inbox/chat1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json
4Content-Type: multipart/form-data| Parameter | Type | Required | Description |
|---|---|---|---|
message | string | Yes | Message content (max 5000 characters) |
chat_id | integer | Yes | ID of the conversation thread |
from_number | string | Yes | Sender phone number (must exist in your active numbers) |
to_number | string | Yes | Recipient phone number |
chatFile | file | No | Media file attachment for MMS (image, video, audio) |
1curl -X POST "https://api.texttorrent.com/api/v1/inbox/chat"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"
5 -F "message=Hello! How can I help you today?"
6 -F "chat_id=1234"
7 -F "from_number=+12025559999"
8 -F "to_number=+12025551234"1curl -X POST "https://api.texttorrent.com/api/v1/inbox/chat"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"
5 -F "message=Check out this image!"
6 -F "chat_id=1234"
7 -F "from_number=+12025559999"
8 -F "to_number=+12025551234"
9 -F "chatFile=@/path/to/image.jpg"1{
2 "code": 201,
3 "success": true,
4 "message": "Message send successfully",
5 "data": {
6 "id": 5002,
7 "chat_id": 1234,
8 "direction": "outbound",
9 "message": "Hello! How can I help you today?",
10 "msg_type": "sms",
11 "file": null,
12 "api_send_status": "sent",
13 "msg_sid": "SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
14 "created_at": "2025-10-17T15:30:00.000000Z",
15 "updated_at": "2025-10-17T15:30:05.000000Z"
16 },
17 "errors": null
18}1{
2 "code": 400,
3 "success": false,
4 "message": "Invalid sender number",
5 "data": null,
6 "errors": null
7}1{
2 "code": 422,
3 "success": false,
4 "message": "You do not have enough credits to perform this action.",
5 "data": null,
6 "errors": null
7}1{
2 "code": 403,
3 "success": false,
4 "message": "You have reached your daily limit for sending messages.",
5 "data": null,
6 "errors": null
7}1{
2 "code": 422,
3 "success": false,
4 "message": "Unable to send message. Sender number is not active!",
5 "data": null,
6 "errors": null
7}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "message": ["The message field is required."],
8 "from_number": ["The selected from number is invalid."],
9 "chat_id": ["The chat id field is required."],
10 "to_number": ["The to number field is required."]
11 }
12}1async function sendMessage(chatId, message, fromNumber, toNumber, mediaFile = null) {
2 const formData = new FormData();
3 formData.append('chat_id', chatId);
4 formData.append('message', message);
5 formData.append('from_number', fromNumber);
6 formData.append('to_number', toNumber);
7
8 if (mediaFile) {
9 formData.append('chatFile', mediaFile);
10 }
11
12 const response = await fetch('https://api.texttorrent.com/api/v1/inbox/chat', {
13 method: 'POST',
14 headers: {
15 'X-API-SID': 'SID....................................',
16 'X-API-PUBLIC-KEY': 'PK.....................................',
17 'Accept': 'application/json'
18 },
19 body: formData
20 });
21
22 const data = await response.json();
23
24 if (data.success) {
25 console.log('Message sent:', data.data);
26 return data.data;
27 } else {
28 console.error('Error:', data.message);
29 throw new Error(data.message);
30 }
31}
32
33// Usage - Send SMS
34await sendMessage(1234, 'Hello!', '+12025559999', '+12025551234');
35
36// Usage - Send MMS with image
37const fileInput = document.getElementById('file-input');
38const file = fileInput.files[0];
39await sendMessage(1234, 'Check this out!', '+12025559999', '+12025551234', file);| Gateway Type | SMS (per segment) | MMS |
|---|---|---|
| Own Gateway | 1 credit | 3 credits + SMS credits for text |
| Text Torrent Gateway | 3 credits | 7 credits + SMS credits for text |
Create a new conversation thread with a contact. If the contact doesn't exist in your contact list, they will be automatically created. This is useful for initiating conversations with new or existing contacts.
POST: /api/v1/inbox/chat/create1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json
4Content-Type: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
receiver_number | string | Yes | Receiver's phone number (10 digits, without +1) |
sender_id | string | Yes | Sender phone number (your active number) |
1curl -X POST "https://api.texttorrent.com/api/v1/inbox/chat/create"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"
5 -H "Content-Type: application/json"
6 -d '{
7 "receiver_number": "2025551234",
8 "sender_id": "+12025559999"
9 }'1{
2 "code": 201,
3 "success": true,
4 "message": "Chat started successfully.",
5 "data": {
6 "id": 1235,
7 "user_id": 1,
8 "contact_id": 568,
9 "from_number": "+12025559999",
10 "last_message": null,
11 "created_at": "2025-10-17T15:45:00.000000Z",
12 "updated_at": "2025-10-17T15:45:00.000000Z"
13 },
14 "errors": null
15}1{
2 "code": 404,
3 "success": false,
4 "message": "This contact is blacklisted.",
5 "data": null,
6 "errors": null
7}1{
2 "code": 404,
3 "success": false,
4 "message": "You have already started a chat with this contact.",
5 "data": null,
6 "errors": null
7}1async function startNewChat(receiverNumber, senderNumber) {
2 const response = await fetch('https://api.texttorrent.com/api/v1/inbox/chat/create', {
3 method: 'POST',
4 headers: {
5 'X-API-SID': 'SID....................................',
6 'X-API-PUBLIC-KEY': 'PK.....................................',
7 'Accept': 'application/json',
8 'Content-Type': 'application/json'
9 },
10 body: JSON.stringify({
11 receiver_number: receiverNumber,
12 sender_id: senderNumber
13 })
14 });
15
16 const data = await response.json();
17
18 if (data.success) {
19 console.log('Chat created:', data.data);
20 // Navigate to the new chat
21 window.location.href = '/inbox/"$"{data.data.id}';
22 return data.data;
23 } else {
24 console.error('Error:', data.message);
25 alert(data.message);
26 }
27}
28
29// Usage - Start chat with new number
30await startNewChat('2025551234', '+12025559999');Generate 5-7 AI-powered reply suggestions based on the conversation history. The AI analyzes the tone, context, and recent messages to provide natural, contextually appropriate responses. Useful for quick replies and maintaining conversation flow.
POST: /api/v1/inbox/generate/ai/response1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json
4Content-Type: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
chat_id | integer | Yes | ID of the conversation (must exist in chats table) |
direction | string | Yes | Message direction: inbound or outbound |
message | string | Yes | The last message content for context |
1curl -X POST "https://api.texttorrent.com/api/v1/inbox/generate/ai/response"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"
5 -H "Content-Type: application/json"
6 -d '{
7 "chat_id": 1234,
8 "direction": "inbound",
9 "message": "When will my order arrive?"
10 }'1{
2 "code": 200,
3 "success": true,
4 "message": "AI replies generated successfully.",
5 "data": [
6 "Your order should arrive by Friday. I'll send you tracking info shortly!",
7 "Great question! Let me check the tracking for you right now.",
8 "It's on the way! Expected delivery is this Friday.",
9 "I'll look up your order status and get back to you in a moment.",
10 "Your package is in transit and should arrive within 2-3 business days.",
11 "Let me pull up your order details to give you an accurate ETA.",
12 "I see it's scheduled for Friday delivery. Would you like the tracking number?"
13 ],
14 "errors": null
15}1{
2 "code": 404,
3 "success": false,
4 "message": "No chat history found for this chat.",
5 "data": null,
6 "errors": null
7}1{
2 "code": 422,
3 "success": false,
4 "message": "No suitable replies generated. The last message may be too short or passive",
5 "data": null,
6 "errors": null
7}1{
2 "code": 404,
3 "success": false,
4 "message": "You do not have enough credits to perform this action.",
5 "data": null,
6 "errors": null
7}1async function generateAiReplies(chatId, direction, message) {
2 const response = await fetch('https://api.texttorrent.com/api/v1/inbox/generate/ai/response', {
3 method: 'POST',
4 headers: {
5 'X-API-SID': 'SID....................................',
6 'X-API-PUBLIC-KEY': 'PK.....................................',
7 'Accept': 'application/json',
8 'Content-Type': 'application/json'
9 },
10 body: JSON.stringify({
11 chat_id: chatId,
12 direction: direction,
13 message: message
14 })
15 });
16
17 const data = await response.json();
18
19 if (data.success) {
20 console.log('AI suggestions:', data.data);
21 return data.data;
22 } else {
23 console.error('Error:', data.message);
24 throw new Error(data.message);
25 }
26}
27
28// Usage - Get AI reply suggestions
29const suggestions = await generateAiReplies(1234, 'inbound', 'When will my order arrive?');
30
31// Display suggestions in UI
32function displaySuggestions(suggestions) {
33 const container = document.getElementById('suggestions');
34 container.innerHTML = '';
35
36 suggestions.forEach((suggestion, index) => {
37 const button = document.createElement('button');
38 button.className = 'suggestion-btn';
39 button.textContent = suggestion;
40 button.onclick = () => {
41 document.getElementById('message-input').value = suggestion;
42 };
43 container.appendChild(button);
44 });
45}
46
47displaySuggestions(suggestions);| Gateway Type | Cost per Request |
|---|---|
| Own Gateway | 3 credits |
| Text Torrent Gateway | 6 credits |
Add a contact to your blocked list. Blocked contacts cannot send you messages, and their conversations are hidden from your inbox. Useful for preventing unwanted communications.
POST: /api/v1/inbox/blacklist/{contactId}1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
contactId | integer | Yes | ID of the contact to block |
1curl -X POST "https://api.texttorrent.com/api/v1/inbox/blacklist/567"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Contact blacklisted successfully",
5 "data": null,
6 "errors": null
7}1async function blockContact(contactId) {
2 if (!confirm('Block this contact? They will not be able to message you.')) {
3 return;
4 }
5
6 const response = await fetch('https://api.texttorrent.com/api/v1/inbox/blacklist/"$"{contactId}', {
7 method: 'POST',
8 headers: {
9 'X-API-SID': 'SID....................................',
10 'X-API-PUBLIC-KEY': 'PK.....................................',
11 'Accept': 'application/json'
12 }
13 });
14
15 const data = await response.json();
16
17 if (data.success) {
18 console.log('Contact blocked successfully');
19 alert('Contact has been blocked');
20 // Refresh inbox list
21 window.location.reload();
22 } else {
23 console.error('Error:', data.message);
24 alert('Failed to block contact');
25 }
26}
27
28// Usage
29blockContact(567);Remove one or more contacts from your blocked list. Unblocked contacts can send you messages again, and their conversations will reappear in your inbox. Supports bulk unblocking.
POST: /api/v1/inbox/unblock1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json
4Content-Type: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
contact_ids | array | Yes | Array of contact IDs to unblock (minimum 1) |
1curl -X POST "https://api.texttorrent.com/api/v1/inbox/unblock"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"
5 -H "Content-Type: application/json"
6 -d '{
7 "contact_ids": [567]
8 }'1curl -X POST "https://api.texttorrent.com/api/v1/inbox/unblock"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"
5 -H "Content-Type: application/json"
6 -d '{
7 "contact_ids": [567, 568, 569]
8 }'1{
2 "code": 200,
3 "success": true,
4 "message": "Contact unblocked successfully",
5 "data": null,
6 "errors": null
7}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "contact_ids": ["The contact ids field is required."],
8 "contact_ids.0": ["The selected contact ids.0 is invalid."]
9 }
10}1async function unblockContacts(contactIds) {
2 const response = await fetch('https://api.texttorrent.com/api/v1/inbox/unblock', {
3 method: 'POST',
4 headers: {
5 'X-API-SID': 'SID....................................',
6 'X-API-PUBLIC-KEY': 'PK.....................................',
7 'Accept': 'application/json',
8 'Content-Type': 'application/json'
9 },
10 body: JSON.stringify({
11 contact_ids: contactIds
12 })
13 });
14
15 const data = await response.json();
16
17 if (data.success) {
18 console.log('Contacts unblocked successfully');
19 alert('"$"{contactIds.length} contact(s) have been unblocked');
20 return true;
21 } else {
22 console.error('Error:', data.message);
23 alert('Failed to unblock contacts');
24 return false;
25 }
26}
27
28// Usage - Unblock single contact
29await unblockContacts([567]);
30
31// Usage - Unblock multiple contacts
32await unblockContacts([567, 568, 569]);
33
34// Usage - Bulk unblock from checkbox selection
35function bulkUnblock() {
36 const checkboxes = document.querySelectorAll('.contact-checkbox:checked');
37 const contactIds = Array.from(checkboxes).map(cb => parseInt(cb.value));
38
39 if (contactIds.length === 0) {
40 alert('Please select contacts to unblock');
41 return;
42 }
43
44 if (confirm('Unblock "$"{contactIds.length} contact(s)?')) {
45 unblockContacts(contactIds);
46 }
47}Create a scheduled event or reminder related to a conversation. Events can trigger alerts at a specified time before the event, helping you stay on top of follow-ups, appointments, and important dates.
POST: /api/v1/inbox/event/add1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json
4Content-Type: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Event name/title |
subject | string | Yes | Event subject/description |
date | string | Yes | Event date (YYYY-MM-DD format) |
time | string | Yes | Event time (HH:MM format, 24-hour) |
sender_number | string | Yes | Phone number to send reminder from |
alert_before | integer | Yes | Minutes before event to send alert |
participant_number | string | No | Participant phone number |
participant_email | string | No | Participant email address (must be valid email format) |
1curl -X POST "https://api.texttorrent.com/api/v1/inbox/event/add"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"
5 -H "Content-Type: application/json"
6 -d '{
7 "name": "Follow-up Call",
8 "subject": "Discuss product demo with client",
9 "date": "2025-10-20",
10 "time": "14:00",
11 "sender_number": "+12025559999",
12 "alert_before": 30,
13 "participant_number": "+12025551234",
14 "participant_email": "client@example.com"
15 }'1{
2 "code": 201,
3 "success": true,
4 "message": "Event created successfully",
5 "data": {
6 "id": 123,
7 "name": "Follow-up Call",
8 "subject": "Discuss product demo with client",
9 "date": "2025-10-20",
10 "time": "14:00",
11 "sender_number": "+12025559999",
12 "alert_before": 30,
13 "alert_at": "2025-10-20 13:30:00",
14 "receiver_number": "+12025551111",
15 "participant_number": "+12025551234",
16 "participant_email": "client@example.com",
17 "user_id": 1,
18 "created_at": "2025-10-17T15:30:00.000000Z",
19 "updated_at": "2025-10-17T15:30:00.000000Z"
20 },
21 "errors": null
22}1async function addEvent(eventData) {
2 const response = await fetch('https://api.texttorrent.com/api/v1/inbox/event/add', {
3 method: 'POST',
4 headers: {
5 'X-API-SID': 'SID....................................',
6 'X-API-PUBLIC-KEY': 'PK.....................................',
7 'Accept': 'application/json',
8 'Content-Type': 'application/json'
9 },
10 body: JSON.stringify(eventData)
11 });
12
13 const data = await response.json();
14
15 if (data.success) {
16 console.log('Event created:', data.data);
17 alert('Event scheduled successfully');
18 return data.data;
19 } else {
20 console.error('Error:', data.message);
21 throw new Error(data.message);
22 }
23}
24
25// Usage - Create follow-up event
26await addEvent({
27 name: 'Follow-up Call',
28 subject: 'Discuss product demo with client',
29 date: '2025-10-20',
30 time: '14:00',
31 sender_number: '+12025559999',
32 alert_before: 30,
33 participant_number: '+12025551234',
34 participant_email: 'client@example.com'
35});
36
37// Usage - Create event from form
38function scheduleEventFromForm() {
39 const form = document.getElementById('event-form');
40 const formData = new FormData(form);
41
42 const eventData = {
43 name: formData.get('name'),
44 subject: formData.get('subject'),
45 date: formData.get('date'),
46 time: formData.get('time'),
47 sender_number: formData.get('sender_number'),
48 alert_before: parseInt(formData.get('alert_before')),
49 participant_number: formData.get('participant_number'),
50 participant_email: formData.get('participant_email')
51 };
52
53 addEvent(eventData);
54}event_time - alert_beforeExport all your inbox conversations to a CSV file. The export includes all contacts you've had conversations with (excluding blacklisted contacts). Useful for backups, data analysis, or importing into other systems.
POST: /api/v1/inbox/export1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json1curl -X POST "https://api.texttorrent.com/api/v1/inbox/export"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Exported successfully",
5 "data": {
6 "path": "inbox/chats_2025_10_17_15_30_45.csv",
7 "url": "https://your-cdn.com/inbox/chats_2025_10_17_15_30_45.csv"
8 },
9 "errors": null
10}1async function exportInbox() {
2 const response = await fetch('https://api.texttorrent.com/api/v1/inbox/export', {
3 method: 'POST',
4 headers: {
5 'X-API-SID': 'SID....................................',
6 'X-API-PUBLIC-KEY': 'PK.....................................',
7 'Accept': 'application/json'
8 }
9 });
10
11 const data = await response.json();
12
13 if (data.success) {
14 console.log('Export successful:', data.data);
15
16 // Download the file
17 const link = document.createElement('a');
18 link.href = data.data.url;
19 link.download = 'inbox_export.csv';
20 document.body.appendChild(link);
21 link.click();
22 document.body.removeChild(link);
23
24 alert('Inbox exported successfully');
25 return data.data;
26 } else {
27 console.error('Error:', data.message);
28 throw new Error(data.message);
29 }
30}
31
32// UsageOrganize your inbox by moving a contact to a specific folder. This helps categorize conversations for better organization and filtering.
POST: /api/v1/inbox/to-folder/create1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json
4Content-Type: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
contact_id | integer | Yes | ID of the contact to move (must exist in contacts table) |
folder_id | integer | Yes | ID of the destination folder |
1curl -X POST "https://api.texttorrent.com/api/v1/inbox/to-folder/create"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"
5 -H "Content-Type: application/json"
6 -d '{
7 "contact_id": 567,
8 "folder_id": 5
9 }'1{
2 "code": 200,
3 "success": true,
4 "message": "Contact moved to folder successfully.",
5 "data": {
6 "id": 567,
7 "user_id": 1,
8 "first_name": "John",
9 "last_name": "Doe",
10 "number": "+12025551234",
11 "email": "john.doe@example.com",
12 "company": "Acme Corp",
13 "folder_id": 5,
14 "created_at": "2025-09-15T10:30:00.000000Z",
15 "updated_at": "2025-10-17T15:45:00.000000Z"
16 },
17 "errors": null
18}1async function moveContactToFolder(contactId, folderId) {
2 const response = await fetch('https://api.texttorrent.com/api/v1/inbox/to-folder/create', {
3 method: 'POST',
4 headers: {
5 'X-API-SID': 'SID....................................',
6 'X-API-PUBLIC-KEY': 'PK.....................................',
7 'Accept': 'application/json',
8 'Content-Type': 'application/json'
9 },
10 body: JSON.stringify({
11 contact_id: contactId,
12 folder_id: folderId
13 })
14 });
15
16 const data = await response.json();
17
18 if (data.success) {
19 console.log('Contact moved:', data.data);
20 alert('Contact moved to folder successfully');
21 return data.data;
22 } else {
23 console.error('Error:', data.message);
24 throw new Error(data.message);
25 }
26}
27
28// Usage
29await moveContactToFolder(567, 5);
30
31// Usage - Create folder selector dropdown
32function createFolderMoveAction(folders, contactId) {
33 const select = document.createElement('select');
34 select.innerHTML = 'Move to folder...';
35
36 folders.forEach(folder => {
37 const option = document.createElement('option');
38 option.value = folder.id;
39 option.textContent = folder.name;
40 select.appendChild(option);
41 });
42
43 select.addEventListener('change', async (e) => {
44 if (e.target.value) {
45 await moveContactToFolder(contactId, parseInt(e.target.value));
46 }
47 });
48
49 return select;
50}folder_id fieldAdd a note to a contact from the inbox. Notes help you track important information, context, or reminders about specific contacts. Same functionality as Contact Notes but accessible from the inbox interface.
POST: /api/v1/inbox/note/add1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json
4Content-Type: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
contact_id | integer | Yes | ID of the contact to add note to |
note | string | Yes | Note content |
1curl -X POST "https://api.texttorrent.com/api/v1/inbox/note/add"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"
5 -H "Content-Type: application/json"
6 -d '{
7 "contact_id": 567,
8 "note": "VIP customer - priority support. Interested in enterprise plan."
9 }'1{
2 "code": 201,
3 "success": true,
4 "message": "Note added successfully.",
5 "data": {
6 "id": 123,
7 "contact_id": 567,
8 "note": "VIP customer - priority support. Interested in enterprise plan.",
9 "created_at": "2025-10-17T15:50:00.000000Z",
10 "updated_at": "2025-10-17T15:50:00.000000Z"
11 },
12 "errors": null
13}1async function addInboxNote(contactId, noteText) {
2 const response = await fetch('https://api.texttorrent.com/api/v1/inbox/note/add', {
3 method: 'POST',
4 headers: {
5 'X-API-SID': 'SID....................................',
6 'X-API-PUBLIC-KEY': 'PK.....................................',
7 'Accept': 'application/json',
8 'Content-Type': 'application/json'
9 },
10 body: JSON.stringify({
11 contact_id: contactId,
12 note: noteText
13 })
14 });
15
16 const data = await response.json();
17
18 if (data.success) {
19 console.log('Note added:', data.data);
20 return data.data;
21 } else {
22 console.error('Error:', data.message);
23 throw new Error(data.message);
24 }
25}
26
27// Usage
28await addInboxNote(567, 'VIP customer - priority support');Remove a note from a contact. This permanently deletes the note from the contact record.
DELETE: /api/v1/inbox/note/delete/{id}1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | ID of the note to delete |
1curl -X DELETE "https://api.texttorrent.com/api/v1/inbox/note/delete/123"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Note deleted successfully.",
5 "data": {
6 "id": 123,
7 "contact_id": 567,
8 "note": "VIP customer - priority support. Interested in enterprise plan.",
9 "created_at": "2025-10-17T15:50:00.000000Z",
10 "updated_at": "2025-10-17T15:50:00.000000Z"
11 },
12 "errors": null
13}1async function deleteInboxNote(noteId) {
2 if (!confirm('Delete this note?')) {
3 return;
4 }
5
6 const response = await fetch('https://api.texttorrent.com/api/v1/inbox/note/delete/"$"{noteId}', {
7 method: 'DELETE',
8 headers: {
9 'X-API-SID': 'SID....................................',
10 'X-API-PUBLIC-KEY': 'PK.....................................',
11 'Accept': 'application/json'
12 }
13 });
14
15 const data = await response.json();
16
17 if (data.success) {
18 console.log('Note deleted successfully');
19 return true;
20 } else {
21 console.error('Error:', data.message);
22 throw new Error(data.message);
23 }
24}
25
26// Usage
27await deleteInboxNote(123);The Sub-Account Management API allows you to create and manage sub-users under your main account. Sub-accounts can have limited permissions, daily SMS limits, and role-based access control. This is perfect for teams, agencies, or organizations that need multiple users with controlled access.
Key Features:
Retrieve a paginated list of all sub-accounts under your main account. Results can be searched by name and sorted by various fields. Each sub-account includes their role information.
GET: /api/v1/user/sub-account/list
1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
limit | integer | No | Number of results per page (default: 10) |
page | integer | No | Page number for pagination (default: 1) |
search | string | No | Search by first name or last name |
sortBy | string | No | Field to sort by (default: id) |
sortType | string | No | Sort direction: asc or desc (default: desc) |
1curl -X GET "https://api.texttorrent.com/api/v1/user/sub-account/list"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1curl -X GET "https://api.texttorrent.com/api/v1/user/sub-account/list?search=john&sortBy=first_name&sortType=asc&limit=20"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Retrieved successfully",
5 "data": {
6 "current_page": 1,
7 "data": [
8 {
9 "id": 101,
10 "first_name": "John",
11 "last_name": "Smith",
12 "username": "johnsmith",
13 "email": "john.smith@example.com",
14 "type": "sub",
15 "parent_id": 1,
16 "status": 1,
17 "role": 2,
18 "permissions": ["view_contacts", "send_messages"],
19 "daily_sms_limit": 500,
20 "admin_approval": 1,
21 "created_at": "2025-09-15T10:30:00.000000Z",
22 "updated_at": "2025-10-17T14:20:00.000000Z",
23 "role": {
24 "id": 2,
25 "name": "Sales Agent"
26 }
27 },
28 {
29 "id": 102,
30 "first_name": "Sarah",
31 "last_name": "Johnson",
32 "username": "sarahjohnson",
33 "email": "sarah.johnson@example.com",
34 "type": "sub",
35 "parent_id": 1,
36 "status": 1,
37 "role": 3,
38 "permissions": ["view_contacts", "send_messages", "view_analytics"],
39 "daily_sms_limit": 1000,
40 "admin_approval": 1,
41 "created_at": "2025-09-20T11:15:00.000000Z",
42 "updated_at": "2025-10-16T09:30:00.000000Z",
43 "role": {
44 "id": 3,
45 "name": "Marketing Manager"
46 }
47 }
48 ],
49 "first_page_url": "https://api.texttorrent.com/api/v1/user/sub-account/list?page=1",
50 "from": 1,
51 "last_page": 3,
52 "last_page_url": "https://api.texttorrent.com/api/v1/user/sub-account/list?page=3",
53 "next_page_url": "https://api.texttorrent.com/api/v1/user/sub-account/list?page=2",
54 "path": "https://api.texttorrent.com/api/v1/user/sub-account/list",
55 "per_page": 10,
56 "prev_page_url": null,
57 "to": 10,
58 "total": 25
59 },
60 "errors": null
61}| Field | Type | Description |
|---|---|---|
type | string | Always "sub" for sub-accounts |
parent_id | integer | ID of the parent account (your account) |
status | integer | Account status: 1 = active, 0 = inactive |
permissions | array | List of permission strings assigned to this sub-account |
daily_sms_limit | integer | Maximum SMS messages this sub-account can send per day |
admin_approval | integer | Admin approval status: 1 = approved, 0 = pending |
1async function getSubAccounts(options = {}) {
2 const params = new URLSearchParams({
3 page: options.page || 1,
4 limit: options.limit || 10
5 });
6
7 if (options.search) params.append('search', options.search);
8 if (options.sortBy) params.append('sortBy', options.sortBy);
9 if (options.sortType) params.append('sortType', options.sortType);
10
11 const response = await fetch(
12 'https://api.texttorrent.com/api/v1/user/sub-account/list?"$"{params}',
13 {
14 method: 'GET',
15 headers: {
16 'X-API-SID': 'SID....................................',
17 'X-API-PUBLIC-KEY': 'PK.....................................',
18 'Accept': 'application/json'
19 }
20 }
21 );
22
23 const data = await response.json();
24
25 if (data.success) {
26 console.log('Sub-accounts:', data.data.data);
27 console.log('Total sub-accounts: "$"{data.data.total}');
28 return data.data;
29 } else {
30 console.error('Error:', data.message);
31 throw new Error(data.message);
32 }
33}
34
35// Usage - Get all sub-accounts
36const subAccounts = await getSubAccounts();
37
38// Usage - Search sub-accounts
39const searchResults = await getSubAccounts({ search: 'john' });
40
41// Usage - Sort by name ascending
42const sorted = await getSubAccounts({ sortBy: 'first_name', sortType: 'asc' });parent_id matches your user IDCreate a new sub-account under your main account. The sub-account will receive a welcome email with their login credentials. You can set permissions, role, and daily SMS limits during creation.
POST: /api/v1/user/sub-account/store1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json
4Content-Type: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
first_name | string | Yes | First name (max 20 characters) |
last_name | string | Yes | Last name (max 20 characters) |
username | string | Yes | Unique username for login |
email | string | Yes | Unique email address |
password | string | Yes | Password (minimum 6 characters) |
confirm_password | string | Yes | Must match password |
role | integer | Yes | Role ID (must exist in user_roles table) |
permissions | array | No | Array of permission strings |
sms_limit | integer | No | Daily SMS limit (default: 0 = unlimited) |
1curl -X POST "https://api.texttorrent.com/api/v1/user/sub-account/store"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"
5 -H "Content-Type: application/json"
6 -d '{
7 "first_name": "John",
8 "last_name": "Smith",
9 "username": "johnsmith",
10 "email": "john.smith@example.com",
11 "password": "SecurePass123",
12 "confirm_password": "SecurePass123",
13 "role": 2,
14 "permissions": ["view_contacts", "send_messages", "view_inbox"],
15 "sms_limit": 500
16 }'1{
2 "code": 201,
3 "success": true,
4 "message": "Sub Account Created Successfully",
5 "data": {
6 "id": 103,
7 "first_name": "John",
8 "last_name": "Smith",
9 "username": "johnsmith",
10 "email": "john.smith@example.com",
11 "type": "sub",
12 "parent_id": 1,
13 "status": 1,
14 "role": 2,
15 "permissions": ["view_contacts", "send_messages", "view_inbox"],
16 "daily_sms_limit": 500,
17 "admin_approval": 1,
18 "created_at": "2025-10-17T16:00:00.000000Z",
19 "updated_at": "2025-10-17T16:00:00.000000Z"
20 },
21 "errors": null
22}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "username": ["The username has already been taken."],
8 "email": ["The email has already been taken."],
9 "password": ["The password must be at least 6 characters."],
10 "confirm_password": ["The confirm password and password must match."],
11 "role": ["The selected role is invalid."]
12 }
13}1async function createSubAccount(accountData) {
2 const response = await fetch('https://api.texttorrent.com/api/v1/user/sub-account/store', {
3 method: 'POST',
4 headers: {
5 'X-API-SID': 'SID....................................',
6 'X-API-PUBLIC-KEY': 'PK.....................................',
7 'Accept': 'application/json',
8 'Content-Type': 'application/json'
9 },
10 body: JSON.stringify(accountData)
11 });
12
13 const data = await response.json();
14
15 if (data.success) {
16 console.log('Sub-account created:', data.data);
17 alert('Sub-account created successfully! Welcome email sent.');
18 return data.data;
19 } else {
20 console.error('Error:', data.message, data.errors);
21 throw new Error(data.message);
22 }
23}
24
25// Usage
26await createSubAccount({
27 first_name: 'John',
28 last_name: 'Smith',
29 username: 'johnsmith',
30 email: 'john.smith@example.com',
31 password: 'SecurePass123',
32 confirm_password: 'SecurePass123',
33 role: 2,
34 permissions: ['view_contacts', 'send_messages', 'view_inbox'],
35 sms_limit: 500
36});admin_approval=1)status=1)Retrieve detailed information about a specific sub-account. Useful for viewing full sub-account profile before editing or for displaying sub-account information in your UI.
GET: /api/v1/user/sub-account/show/{id}1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | Sub-account user ID |
1curl -X GET "https://api.texttorrent.com/api/v1/user/sub-account/show/101"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Sub Account Retrieved Successfully",
5 "data": {
6 "id": 101,
7 "first_name": "John",
8 "last_name": "Smith",
9 "username": "johnsmith",
10 "email": "john.smith@example.com",
11 "type": "sub",
12 "parent_id": 1,
13 "status": 1,
14 "role": 2,
15 "permissions": ["view_contacts", "send_messages", "view_inbox"],
16 "daily_sms_limit": 500,
17 "admin_approval": 1,
18 "created_at": "2025-09-15T10:30:00.000000Z",
19 "updated_at": "2025-10-17T14:20:00.000000Z"
20 },
21 "errors": null
22}1{
2 "code": 404,
3 "success": false,
4 "message": "Sub Account Not Found",
5 "data": [],
6 "errors": null
7}1async function getSubAccountDetails(subAccountId) {
2 const response = await fetch(
3 'https://api.texttorrent.com/api/v1/user/sub-account/show/"$"{subAccountId}',
4 {
5 method: 'GET',
6 headers: {
7 'X-API-SID': 'SID....................................',
8 'X-API-PUBLIC-KEY': 'PK.....................................',
9 'Accept': 'application/json'
10 }
11 }
12 );
13
14 const data = await response.json();
15
16 if (data.success) {
17 console.log('Sub-account details:', data.data);
18 return data.data;
19 } else {
20 console.error('Error:', data.message);
21 throw new Error(data.message);
22 }
23}
24
25// Usage
26const subAccount = await getSubAccountDetails(101);parent_id check)Update an existing sub-account's information including name, email, username, password, permissions, role, and daily SMS limit. Password update is optional.
PUT: /api/v1/user/sub-account/update/{id}1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json
4Content-Type: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | Sub-account user ID to update |
| Parameter | Type | Required | Description |
|---|---|---|---|
first_name | string | Yes | First name (max 20 characters) |
last_name | string | Yes | Last name (max 20 characters) |
username | string | Yes | Username (unique, except current user) |
email | string | Yes | Email address (unique, except current user) |
password | string | No | New password (minimum 6 characters) |
confirm_password | string | No* | Required if password is provided, must match password |
role | integer | Yes | Role ID (must exist in user_roles table) |
permissions | array | No | Array of permission strings |
sms_limit | integer | No | Daily SMS limit (default: 0 = unlimited) |
1curl -X PUT "https://api.texttorrent.com/api/v1/user/sub-account/update/101"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"
5 -H "Content-Type: application/json"
6 -d '{
7 "first_name": "John",
8 "last_name": "Smith",
9 "username": "johnsmith",
10 "email": "john.smith@example.com",
11 "role": 2,
12 "permissions": ["view_contacts", "send_messages", "view_inbox", "view_analytics"],
13 "sms_limit": 1000
14 }'1curl -X PUT "https://api.texttorrent.com/api/v1/user/sub-account/update/101"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"
5 -H "Content-Type: application/json"
6 -d '{
7 "first_name": "John",
8 "last_name": "Smith",
9 "username": "johnsmith",
10 "email": "john.smith@example.com",
11 "password": "NewSecurePass456",
12 "confirm_password": "NewSecurePass456",
13 "role": 2,
14 "permissions": ["view_contacts", "send_messages", "view_inbox"],
15 "sms_limit": 1000
16 }'1{
2 "code": 200,
3 "success": true,
4 "message": "Sub Account Updated Successfully",
5 "data": {
6 "id": 101,
7 "first_name": "John",
8 "last_name": "Smith",
9 "username": "johnsmith",
10 "email": "john.smith@example.com",
11 "type": "sub",
12 "parent_id": 1,
13 "status": 1,
14 "role": 2,
15 "permissions": ["view_contacts", "send_messages", "view_inbox", "view_analytics"],
16 "daily_sms_limit": 1000,
17 "admin_approval": 1,
18 "created_at": "2025-09-15T10:30:00.000000Z",
19 "updated_at": "2025-10-17T16:15:00.000000Z"
20 },
21 "errors": null
22}1{
2 "code": 404,
3 "success": false,
4 "message": "Sub Account Not Found",
5 "data": [],
6 "errors": null
7}1async function updateSubAccount(subAccountId, accountData) {
2 const response = await fetch(
3 'https://api.texttorrent.com/api/v1/user/sub-account/update/"$"{subAccountId}',
4 {
5 method: 'PUT',
6 headers: {
7 'X-API-SID': 'SID....................................',
8 'X-API-PUBLIC-KEY': 'PK.....................................',
9 'Accept': 'application/json',
10 'Content-Type': 'application/json'
11 },
12 body: JSON.stringify(accountData)
13 }
14 );
15
16 const data = await response.json();
17
18 if (data.success) {
19 console.log('Sub-account updated:', data.data);
20 alert('Sub-account updated successfully!');
21 return data.data;
22 } else {
23 console.error('Error:', data.message, data.errors);
24 throw new Error(data.message);
25 }
26}
27
28// Usage - Update without password
29await updateSubAccount(101, {
30 first_name: 'John',
31 last_name: 'Smith',
32 username: 'johnsmith',
33 email: 'john.smith@example.com',
34 role: 2,
35 permissions: ['view_contacts', 'send_messages', 'view_inbox', 'view_analytics'],
36 sms_limit: 1000
37});
38
39// Usage - Update with password
40await updateSubAccount(101, {
41 first_name: 'John',
42 last_name: 'Smith',
43 username: 'johnsmith',
44 email: 'john.smith@example.com',
45 password: 'NewSecurePass456',
46 confirm_password: 'NewSecurePass456',
47 role: 2,
48 permissions: ['view_contacts', 'send_messages'],
49 sms_limit: 1000
50});Permanently delete a sub-account from your account. This action cannot be undone. All data associated with the sub-account will be removed.
DELETE: /api/v1/user/sub-account/delete/{id}1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | Sub-account user ID to delete |
1curl -X DELETE "https://api.texttorrent.com/api/v1/user/sub-account/delete/101"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Sub Account Deleted Successfully",
5 "data": [],
6 "errors": null
7}1{
2 "code": 404,
3 "success": false,
4 "message": "Sub Account Not Found",
5 "data": [],
6 "errors": null
7}1async function deleteSubAccount(subAccountId) {
2 if (!confirm('Delete this sub-account? This action cannot be undone.')) {
3 return;
4 }
5
6 const response = await fetch(
7 'https://api.texttorrent.com/api/v1/user/sub-account/delete/"$"{subAccountId}',
8 {
9 method: 'DELETE',
10 headers: {
11 'X-API-SID': 'SID....................................',
12 'X-API-PUBLIC-KEY': 'PK.....................................',
13 'Accept': 'application/json'
14 }
15 }
16 );
17
18 const data = await response.json();
19
20 if (data.success) {
21 console.log('Sub-account deleted successfully');
22 alert('Sub-account has been deleted');
23 return true;
24 } else {
25 console.error('Error:', data.message);
26 alert('Failed to delete sub-account');
27 return false;
28 }
29}
30
31// Usage
32await deleteSubAccount(101);Enable or disable a sub-account. Disabled sub-accounts cannot log in or access the system. This is useful for temporarily suspending access without deleting the account.
PATCH: /api/v1/user/sub-account/status/{id}1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json
4Content-Type: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | Sub-account user ID |
| Parameter | Type | Required | Description |
|---|---|---|---|
status | integer | Yes | New status: 1 = active, 0 = inactive |
1curl -X PATCH "https://api.texttorrent.com/api/v1/user/sub-account/status/101"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"
5 -H "Content-Type: application/json"
6 -d '{
7 "status": 0
8 }'1curl -X PATCH "https://api.texttorrent.com/api/v1/user/sub-account/status/101"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"
5 -H "Content-Type: application/json"
6 -d '{
7 "status": 1
8 }'1{
2 "code": 200,
3 "success": true,
4 "message": "Sub Account Status Updated Successfully",
5 "data": {
6 "id": 101,
7 "first_name": "John",
8 "last_name": "Smith",
9 "username": "johnsmith",
10 "email": "john.smith@example.com",
11 "type": "sub",
12 "parent_id": 1,
13 "status": 0,
14 "role": 2,
15 "permissions": ["view_contacts", "send_messages"],
16 "daily_sms_limit": 500,
17 "admin_approval": 1,
18 "created_at": "2025-09-15T10:30:00.000000Z",
19 "updated_at": "2025-10-17T16:30:00.000000Z"
20 },
21 "errors": null
22}1{
2 "code": 404,
3 "success": false,
4 "message": "Sub Account Not Found",
5 "data": [],
6 "errors": null
7}1async function changeSubAccountStatus(subAccountId, status) {
2 const statusText = status === 1 ? 'enable' : 'disable';
3
4 if (!confirm('Are you sure you want to "$"{statusText} this sub-account?')) {
5 return;
6 }
7
8 const response = await fetch(
9 'https://api.texttorrent.com/api/v1/user/sub-account/status/"$"{subAccountId}',
10 {
11 method: 'PATCH',
12 headers: {
13 'X-API-SID': 'SID....................................',
14 'X-API-PUBLIC-KEY': 'PK.....................................',
15 'Accept': 'application/json',
16 'Content-Type': 'application/json'
17 },
18 body: JSON.stringify({ status })
19 }
20 );
21
22 const data = await response.json();
23
24 if (data.success) {
25 console.log('Status updated:', data.data);
26 alert('Sub-account "$"{statusText}d successfully');
27 return data.data;
28 } else {
29 console.error('Error:', data.message);
30 throw new Error(data.message);
31 }
32}
33
34// Usage - Disable sub-account
35await changeSubAccountStatus(101, 0);
36
37// Usage - Enable sub-account
38await changeSubAccountStatus(101, 1);
39
40// Usage - Toggle status
41async function toggleSubAccountStatus(subAccount) {
42 const newStatus = subAccount.status === 1 ? 0 : 1;
43 return await changeSubAccountStatus(subAccount.id, newStatus);
44}Generate an authentication token to log in as a sub-user. This is useful for support purposes, allowing you to view the system from the sub-account's perspective without knowing their password.
POST: /api/v1/user/sub-account/login-as-sub-user/{id}1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | Sub-account user ID to log in as |
1curl -X POST "https://api.texttorrent.com/api/v1/user/sub-account/login-as-sub-user/101"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Logged in as Sub Account Successfully",
5 "data": {
6 "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9..."
7 },
8 "errors": null
9}1{
2 "code": 404,
3 "success": false,
4 "message": "Sub Account Not Found",
5 "data": [],
6 "errors": null
7}1async function loginAsSubUser(subAccountId) {
2 const response = await fetch(
3 'https://api.texttorrent.com/api/v1/user/sub-account/login-as-sub-user/"$"{subAccountId}',
4 {
5 method: 'POST',
6 headers: {
7 'X-API-SID': 'SID....................................',
8 'X-API-PUBLIC-KEY': 'PK.....................................',
9 'Accept': 'application/json'
10 }
11 }
12 );
13
14 const data = await response.json();
15
16 if (data.success) {
17 console.log('Sub-user token generated:', data.data.token);
18
19 // Store the token
20 localStorage.setItem('sub_user_token', data.data.token);
21
22 // Redirect to dashboard as sub-user
23 window.location.href = '/dashboard?as_sub_user=true';
24
25 return data.data.token;
26 } else {
27 console.error('Error:', data.message);
28 throw new Error(data.message);
29 }
30}
31
32// Usage
33await loginAsSubUser(101);
34
35// Usage - With confirmation
36async function impersonateSubUser(subAccountId, subAccountName) {
37 if (confirm('Log in as "$"{subAccountName}? You'll be viewing the system from their perspective.')) {
38 const token = await loginAsSubUser(subAccountId);
39 console.log('Now viewing as sub-user');
40 }
41}1<?php
2$subAccountId = 101;
3
4$ch = curl_init();
5curl_setopt($ch, CURLOPT_URL, "https://api.texttorrent.com/api/v1/user/sub-account/login-as-sub-user/{$subAccountId}");
6curl_setopt($ch, CURLOPT_POST, true);
7curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
8curl_setopt($ch, CURLOPT_HTTPHEADER, [
9 'X-API-SID: SID....................................',
10 'X-API-PUBLIC-KEY: PK.....................................',
11 'Accept: application/json'
12]);
13
14$response = curl_exec($ch);
15curl_close($ch);
16
17$data = json_decode($response, true);
18
19if ($data['success']) {
20 $token = $data['data']['token'];
21
22 // Store token in session for sub-user access
23 $_SESSION['sub_user_token'] = $token;
24 $_SESSION['is_impersonating'] = true;
25
26 // Redirect to dashboard
27 header('Location: /dashboard');
28 exit;
29} else {
30 echo "Error: " . $data['message'];
31}
32?>1class SubAccountManager {
2 constructor(apiSid, apiKey) {
3 this.apiSid = apiSid;
4 this.apiKey = apiKey;
5 this.baseUrl = 'https://api.texttorrent.com/api/v1/user/sub-account';
6 }
7
8 async list(options = {}) {
9 const params = new URLSearchParams(options);
10 return await this.apiCall('GET', '/list?"$"{params}
11 ');
12 }
13
14 async create(accountData) {
15 return await this.apiCall('POST', '/store', accountData);
16 }
17
18 async get(id) {
19 return await this.apiCall('GET', '/show/"$"{id}');
20 }
21
22 async update(id, accountData) {
23 return await this.apiCall('PUT', '/update/"$"{id}', accountData);
24 }
25
26 async delete(id) {
27 return await this.apiCall('DELETE', '/delete/"$"{id}');
28 }
29
30 async changeStatus(id, status) {
31 return await this.apiCall('PATCH', '/status/"$"{id}', { status });
32 }
33
34 async loginAs(id) {
35 return await this.apiCall('POST', '/login-as-sub-user/"$"{id}');
36 }
37
38 async apiCall(method, endpoint, data = null) {
39 const headers = {
40 'X-API-SID': this.apiSid,
41 'X-API-PUBLIC-KEY': this.apiKey,
42 'Accept': 'application/json'
43 };
44
45 if (data && method !== 'GET') {
46 headers['Content-Type'] = 'application/json';
47 }
48
49 const options = {
50 method,
51 headers,
52 body: data && method !== 'GET' ? JSON.stringify(data) : null
53 };
54
55 const response = await fetch(this.baseUrl + endpoint, options);
56 const result = await response.json();
57
58 if (!result.success) {
59 throw new Error(result.message);
60 }
61
62 return result;
63 }
64}
65
66// Usage
67const subAccountMgr = new SubAccountManager('SID....................................', 'PK.....................................');
68
69// List sub-accounts
70const accounts = await subAccountMgr.list({ search: 'john' });
71
72// Create new sub-account
73const newAccount = await subAccountMgr.create({
74 first_name: 'Jane',
75 last_name: 'Doe',
76 username: 'janedoe',
77 email: 'jane.doe@example.com',
78 password: 'SecurePass123',
79 confirm_password: 'SecurePass123',
80 role: 2,
81 permissions: ['view_contacts', 'send_messages'],
82 sms_limit: 500
83});
84
85// Update sub-account
86await subAccountMgr.update(101, {
87 first_name: 'John',
88 last_name: 'Smith',
89 username: 'johnsmith',
90 email: 'john.smith@example.com',
91 role: 2,
92 permissions: ['view_contacts', 'send_messages', 'view_analytics'],
93 sms_limit: 1000
94});
95
96// Disable sub-account
97await subAccountMgr.changeStatus(101, 0);
98
99// Login as sub-user
100const token = await subAccountMgr.loginAs(101);| Role | Permissions | Daily Limit |
|---|---|---|
| Sales Agent | view_contacts, send_messages, view_inbox | 500 |
| Marketing Manager | view_contacts, send_messages, view_inbox, view_analytics, create_campaigns | 1000 |
| Support Rep | view_contacts, send_messages, view_inbox, manage_notes | 300 |
| Admin | All permissions | 0 (unlimited) |
The Bulk Messaging API allows you to create and manage SMS/MMS campaigns to send messages to multiple contacts at once. You can schedule campaigns, use message templates, implement batch processing, and track campaign performance. Perfect for marketing campaigns, announcements, and mass notifications.
Key Features:
Retrieve all necessary data for creating a bulk messaging campaign. This endpoint provides contact lists, active phone numbers, and sub-user information. Use this endpoint to populate your campaign creation form.
GET: /api/v1/campaigning/bulk1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json| Parameter | Type | Required | Description |
|---|---|---|---|
search_number | string | No | Search/filter contact lists by name |
search_contact | string | No | Search/filter active numbers |
1curl -X GET "https://api.texttorrent.com/api/v1/campaigning/bulk"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1curl -X GET "https://api.texttorrent.com/api/v1/campaigning/bulk?search_number=customers&search_contact=+1"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"1{
2 "code": 200,
3 "success": true,
4 "message": "Data fetched successfully",
5 "data": {
6 "contactLists": [
7 {
8 "id": 45,
9 "name": "VIP Customers",
10 "bookmarked": 1,
11 "total_contacts": 1250
12 },
13 {
14 "id": 42,
15 "name": "Newsletter Subscribers",
16 "bookmarked": 0,
17 "total_contacts": 3420
18 }
19 ],
20 "activeNumbers": [
21 {
22 "id": 12,
23 "number": "+15551234567",
24 "friendly_name": "Main Business Line",
25 "capabilities": {"sms": true, "mms": true, "voice": true},
26 "status": 1
27 },
28 {
29 "id": 15,
30 "number": "+15559876543",
31 "friendly_name": "Marketing Line",
32 "capabilities": {"sms": true, "mms": true, "voice": false},
33 "status": 1
34 }
35 ],
36 "subUsers": [
37 {
38 "id": 1,
39 "name": "John Smith (2)",
40 "numbers": ["+15551234567", "+15559876543"]
41 },
42 {
43 "id": 101,
44 "name": "Sarah Johnson (1)",
45 "numbers": ["+15556543210"]
46 }
47 ]
48 },
49 "errors": null
50}| Field | Type | Description |
|---|---|---|
contactLists | array | Available contact lists with total contact counts |
activeNumbers | array | Active phone numbers available for sending (status=1) |
subUsers | array | Sub-accounts and their associated active phone numbers |
total_contacts | integer | Number of contacts in each list |
capabilities | object | Number capabilities (sms, mms, voice) |
1async function getCampaignData(searchOptions = {}) {
2 const params = new URLSearchParams();
3
4 if (searchOptions.contactList) {
5 params.append('search_number', searchOptions.contactList);
6 }
7 if (searchOptions.phoneNumber) {
8 params.append('search_contact', searchOptions.phoneNumber);
9 }
10
11 const queryString = params.toString() ? '?"$"{params} : '';
12
13 const response = await fetch(
14 'https://api.texttorrent.com/api/v1/campaigning/bulk"$"{queryString}',
15 {
16 method: 'GET',
17 headers: {
18 'X-API-SID': 'SID....................................',
19 'X-API-PUBLIC-KEY': 'PK.....................................',
20 'Accept': 'application/json'
21 }
22 }
23 );
24
25 const data = await response.json();
26
27 if (data.success) {
28 console.log('Contact Lists:', data.data.contactLists);
29 console.log('Active Numbers:', data.data.activeNumbers);
30 console.log('Sub Users:', data.data.subUsers);
31 return data.data;
32 } else {
33 console.error('Error:', data.message);
34 throw new Error(data.message);
35 }
36}
37
38// Usage - Get all data
39const campaignData = await getCampaignData();
40
41// Usage - Search contact lists
42const filtered = await getCampaignData({
43 contactList: 'customers',
44 phoneNumber: '+1'
45});user_id matches your accountstatus=1) are includedCreate a new bulk messaging campaign to send SMS or MMS messages to a contact list. You can schedule the campaign for future delivery, enable batch processing, use number pools, and track credit consumption. The campaign will be processed in the background via job queue.
POST: /api/v1/campaigning/bulk1X-API-SID: SID....................................
2X-API-PUBLIC-KEY: PK.....................................
3Accept: application/json
4Content-Type: multipart/form-data| Parameter | Type | Required | Description |
|---|---|---|---|
campaign_name | string | Yes | Campaign name (max 255 characters) |
contact_list_id | integer | Yes | ID of contact list to send to (must exist) |
template_id | integer | Yes | ID of message template (must exist) |
sms_type | string | Yes | Message type: sms or mms |
sms_body | string | Yes | Message content (max 1600 characters) |
numbers | array | Yes | Array of phone numbers to send from (max 15 chars each) |
file | file | No* | Media file for MMS (required if sms_type=mms) |
appended_message | string | No | Additional text appended to message (max 1600 chars) |
number_pool | boolean | No | Enable number pool distribution |
batch_process | boolean | No | Enable batch processing |
opt_out_link | boolean | No | Include opt-out link in message |
round_robin_campaign | boolean | No | Enable round-robin number rotation |
batch_size | integer | No | Number of messages per batch (if batch_process enabled) |
batch_frequency | integer | No | Minutes between batches (if batch_process enabled) |
selected_date | date | No | Schedule date (YYYY-MM-DD format) |
selected_time | string | No | Schedule time (HH:MM format) |
phone_numbers | string | No | Comma-separated phone numbers |
selected_users | array | No | Array of sub-user IDs to use their numbers |
1curl -X POST "https://api.texttorrent.com/api/v1/campaigning/bulk"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"
5 -F "campaign_name=Spring Sale Announcement"
6 -F "contact_list_id=45"
7 -F "template_id=12"
8 -F "sms_type=sms"
9 -F "sms_body=Hi {first_name}, don't miss our Spring Sale! Get 20% off all items. Shop now!"
10 -F "numbers[]=+15551234567"
11 -F "numbers[]=+15559876543"
12 -F "opt_out_link=true"1curl -X POST "https://api.texttorrent.com/api/v1/campaigning/bulk"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"
5 -F "campaign_name=Product Launch"
6 -F "contact_list_id=45"
7 -F "template_id=12"
8 -F "sms_type=mms"
9 -F "sms_body=Check out our new product!"
10 -F "file=@/path/to/product-image.jpg"
11 -F "numbers[]=+15551234567"
12 -F "opt_out_link=true"1curl -X POST "https://api.texttorrent.com/api/v1/campaigning/bulk"
2 -H "X-API-SID: SID...................................."
3 -H "X-API-PUBLIC-KEY: PK....................................."
4 -H "Accept: application/json"
5 -F "campaign_name=Holiday Greetings"
6 -F "contact_list_id=45"
7 -F "template_id=12"
8 -F "sms_type=sms"
9 -F "sms_body=Happy Holidays from our team!"
10 -F "numbers[]=+15551234567"
11 -F "batch_process=true"
12 -F "batch_size=100"
13 -F "batch_frequency=30"
14 -F "selected_date=2025-12-25"
15 -F "selected_time=09:00"1{
2 "code": 200,
3 "success": true,
4 "message": "Campaign created successfully",
5 "data": {
6 "campaign_id": 567,
7 "total_credit": 1875,
8 "total_contacts": 1250,
9 "total_segments": 1
10 },
11 "errors": null
12}1{
2 "code": 422,
3 "success": false,
4 "message": "You do not have enough credits to perform this action.",
5 "data": null,
6 "errors": null
7}1{
2 "code": 422,
3 "success": false,
4 "message": "You cannot use this feature during the trial period.",
5 "data": null,
6 "errors": null
7}1{
2 "code": 422,
3 "success": false,
4 "message": "Validation Error",
5 "data": null,
6 "errors": {
7 "campaign_name": ["The campaign name field is required."],
8 "contact_list_id": ["The selected contact list id is invalid."],
9 "template_id": ["The selected template id is invalid."],
10 "sms_type": ["The sms type must be either sms or mms."],
11 "file": ["The file field is required when sms type is mms."],
12 "numbers": ["The numbers field is required."]
13 }
14}1async function createCampaign(campaignData, mediaFile = null) {
2 const formData = new FormData();
3
4 // Required fields
5 formData.append('campaign_name', campaignData.campaign_name);
6 formData.append('contact_list_id', campaignData.contact_list_id);
7 formData.append('template_id', campaignData.template_id);
8 formData.append('sms_type', campaignData.sms_type);
9 formData.append('sms_body', campaignData.sms_body);
10
11 // Numbers array
12 campaignData.numbers.forEach(number => {
13 formData.append('numbers[]', number);
14 });
15
16 // Optional fields
17 if (campaignData.appended_message) {
18 formData.append('appended_message', campaignData.appended_message);
19 }
20 if (campaignData.number_pool) {
21 formData.append('number_pool', campaignData.number_pool);
22 }
23 if (campaignData.batch_process) {
24 formData.append('batch_process', campaignData.batch_process);
25 formData.append('batch_size', campaignData.batch_size);
26 formData.append('batch_frequency', campaignData.batch_frequency);
27 }
28 if (campaignData.opt_out_link) {
29 formData.append('opt_out_link', campaignData.opt_out_link);
30 }
31 if (campaignData.round_robin_campaign) {
32 formData.append('round_robin_campaign', campaignData.round_robin_campaign);
33 }
34 if (campaignData.selected_date) {
35 formData.append('selected_date', campaignData.selected_date);
36 formData.append('selected_time', campaignData.selected_time);
37 }
38
39 // MMS file
40 if (mediaFile && campaignData.sms_type === 'mms') {
41 formData.append('file', mediaFile);
42 }
43
44 const response = await fetch('https://api.texttorrent.com/api/v1/campaigning/bulk', {
45 method: 'POST',
46 headers: {
47 'X-API-SID': 'SID....................................',
48 'X-API-PUBLIC-KEY': 'PK.....................................',
49 'Accept': 'application/json'
50 },
51 body: formData
52 });
53
54 const data = await response.json();
55
56 if (data.success) {
57 console.log('Campaign created:', data.data);
58 alert('Campaign created! ID: "$"{data.data.campaign_id}, Credits: "$"{data.data.total_credit}');
59 return data.data;
60 } else {
61 console.error('Error:', data.message, data.errors);
62 throw new Error(data.message);
63 }
64}
65
66// Usage - Simple SMS campaign
67await createCampaign({
68 campaign_name: 'Spring Sale Announcement',
69 contact_list_id: 45,
70 template_id: 12,
71 sms_type: 'sms',
72 sms_body: 'Hi {first_name}, don't miss our Spring Sale! Get 20% off all items.',
73 numbers: ['+15551234567', '+15559876543'],
74 opt_out_link: true
75});
76
77// Usage - Scheduled campaign
78await createCampaign({
79 campaign_name: 'Holiday Greetings',
80 contact_list_id: 45,
81 template_id: 12,
82 sms_type: 'sms',
83 sms_body: 'Happy Holidays from our team!',
84 numbers: ['+15551234567'],
85 batch_process: true,
86 batch_size: 100,
87 batch_frequency: 30,
88 selected_date: '2025-12-25',
89 selected_time: '09:00'
90});
91
92// Usage - MMS campaign with file
93const fileInput = document.getElementById('mms-file');
94await createCampaign({
95 campaign_name: 'Product Launch',
96 contact_list_id: 45,
97 template_id: 12,
98 sms_type: 'mms',
99 sms_body: 'Check out our new product!',
100 numbers: ['+15551234567']
101}, fileInput.files[0]);1<?php
2$ch = curl_init();
3
4$campaignData = [
5 'campaign_name' => 'Spring Sale Announcement',
6 'contact_list_id' => 45,
7 'template_id' => 12,
8 'sms_type' => 'sms',
9 'sms_body' => 'Hi {first_name}, don't miss our Spring Sale! Get 20% off all items.',
10 'numbers' => ['+15551234567', '+15559876543'],
11 'opt_out_link' => true
12];
13
14// Convert numbers array to proper format
15$postFields = $campaignData;
16$postFields['numbers'] = $campaignData['numbers'];
17
18curl_setopt($ch, CURLOPT_URL, 'https://api.texttorrent.com/api/v1/campaigning/bulk');
19curl_setopt($ch, CURLOPT_POST, true);
20curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postFields));
21curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
22curl_setopt($ch, CURLOPT_HTTPHEADER, [
23 'X-API-SID: SID....................................',
24 'X-API-PUBLIC-KEY: PK.....................................',
25 'Accept: application/json'
26]);
27
28$response = curl_exec($ch);
29curl_close($ch);
30
31$data = json_decode($response, true);
32
33if ($data['success']) {
34 echo "Campaign created! ID: {$data['data']['campaign_id']}
35";
36 echo "Credits consumed: {$data['data']['total_credit']}
37";
38 echo "Total contacts: {$data['data']['total_contacts']}
39";
40} else {
41 echo "Error: {$data['message']}
42";
43}
44?>| Encoding | Single Segment | Multi-Segment | Credit per Segment |
|---|---|---|---|
| GSM-7 (Standard) | Up to 160 characters | 153 characters each | 1 credit |
| UCS-2 (Unicode) | Up to 70 characters | 67 characters each | 1 credit |
| MMS | N/A | N/A | Fixed MMS rate |
1class CampaignManager {
2 constructor(apiSid, apiKey) {
3 this.apiSid = apiSid;
4 this.apiKey = apiKey;
5 this.baseUrl = 'https://api.texttorrent.com/api/v1/campaigning';
6 }
7
8 async getCampaignData(search = {}) {
9 const params = new URLSearchParams(search);
10 const response = await fetch(
11 '"$"{this.baseUrl}/bulk?"$"{params}"',
12 {
13 method: 'GET',
14 headers: this.getHeaders()
15 }
16 );
17 return await this.handleResponse(response);
18 }
19
20 async createCampaign(campaignData, file = null) {
21 const formData = new FormData();
22
23 // Add all campaign data
24 Object.keys(campaignData).forEach(key => {
25 if (Array.isArray(campaignData[key])) {
26 campaignData[key].forEach(value => {
27 formData.append('"$"{key}[]', value);
28 });
29 } else if (campaignData[key] !== null && campaignData[key] !== undefined) {
30 formData.append(key, campaignData[key]);
31 }
32 });
33
34 // Add file for MMS
35 if (file) {
36 formData.append('file', file);
37 }
38
39 const response = await fetch('"$"{this.baseUrl}/bulk', {
40 method: 'POST',
41 headers: {
42 'X-API-SID': this.apiSid,
43 'X-API-PUBLIC-KEY': this.apiKey,
44 'Accept': 'application/json'
45 },
46 body: formData
47 });
48
49 return await this.handleResponse(response);
50 }
51
52 getHeaders() {
53 return {
54 'X-API-SID': this.apiSid,
55 'X-API-PUBLIC-KEY': this.apiKey,
56 'Accept': 'application/json'
57 };
58 }
59
60 async handleResponse(response) {
61 const data = await response.json();
62 if (!data.success) {
63 throw new Error(data.message);
64 }
65 return data.data;
66 }
67}
68
69// Usage
70const manager = new CampaignManager('SID....................................', 'PK.....................................');
71
72// Get campaign data
73const data = await manager.getCampaignData({ search_number: 'customers' });
74console.log('Available contact lists:', data.contactLists);
75
76// Create immediate SMS campaign
77const campaign = await manager.createCampaign({
78 campaign_name: 'Flash Sale Alert',
79 contact_list_id: 45,
80 template_id: 12,
81 sms_type: 'sms',
82 sms_body: 'Hi {first_name}! Flash sale ends in 2 hours. Shop now!',
83 numbers: ['+15551234567', '+15559876543'],
84 opt_out_link: true,
85 round_robin_campaign: true
86});
87
88console.log('Campaign "$"{campaign.campaign_id} created!');
89console.log('Credits consumed: "$"{campaign.total_credit}');
90console.log('Messages queued: "$"{campaign.total_contacts}');
91
92// Create scheduled campaign with batches
93const scheduled = await manager.createCampaign({
94 campaign_name: 'Weekly Newsletter',
95 contact_list_id: 45,
96 template_id: 12,
97 sms_type: 'sms',
98 sms_body: 'This week's top stories: {spin_text}',
99 numbers: ['+15551234567'],
100 opt_out_link: true,
101 batch_process: true,
102 batch_size: 100,
103 batch_frequency: 30,
104 selected_date: '2025-10-20',
105 selected_time: '09:00'
106});
107
108console.log('Scheduled campaign created:', scheduled);1function estimateCampaignCredits(messageBody, totalContacts, smsType = 'sms') {
2 if (smsType === 'mms') {
3 // MMS has fixed cost + SMS cost if body included
4 const mmsCredit = 3; // Example MMS cost
5 const smsSegments = messageBody ? calculateSegments(messageBody) : 0;
6 return (mmsCredit + smsSegments) * totalContacts;
7 }
8
9 const segments = calculateSegments(messageBody);
10 return segments * totalContacts;
11}
12
13function calculateSegments(message) {
14 const isGSM7 = /^[