One base URL, one authentication header, 119 documented operations. Everything your account can do in Send It By Text, your code can do here: send and receive texts, manage clients and users, book appointments, collect eSignatures, and more.
application/x-www-form-urlencoded (multipart form data for file operations)
Auth
apikey header on every request
Operation
The request parameter in the body selects the operation, e.g. request=sendclienttext
Responses
JSON
Three operations use their own URLs: sendclientmms posts to /api/mms_v2.ashx, signrequest posts to /api/signrequest_v2.ashx, and sendgroupmsg posts to /api/sendgroupmsg.ashx. Every other call goes to the base URL above.
Authentication
Every request must include your API key in the apikey request header. Authorized administrators can find the key in Send It By Text under Admin Menu → Company Settings. Keys are scoped to your company.
Header
apikey: YOUR_API_KEY
Keep your key on the server. The API key carries admin-level access to your company's data. Put it in the header, never in a query string. Never embed it in a mobile app, browser JavaScript, logs, support tickets, or a public repository. Make API calls from your backend only, and rotate the key from Company Settings if you believe it has been exposed. All examples in these docs use the placeholder YOUR_API_KEY.
Making requests
Send parameters as a form encoded POST body. The request parameter names the operation, and the remaining parameters are listed per operation below. Dates use mm/dd/yyyy and, where a time is expected, mm/dd/yyyy hh:mm:ss.
Phone number rule. Normalize every phone number to 10 digits with no country code. Strip +1, spaces, parentheses, hyphens, and dots before sending. The exception is getsigninglink, which expects the country code included.
File operations (MMS, document upload, signature requests, group messages with attachments) accept a file POSTed as multipart form data, or Base64 encoded content in a rawdata parameter with a filename. When you use browser FormData, do not set the Content-Type header yourself; the browser adds the multipart boundary for you.
Which ID do I use?
One rule covers almost every call.searchclient lets you search by name, phone, email, visible Contact ID, or your own custom ClientID. But most follow-up calls require the internal ID from that search result, passed as cid.
search by anything → take ID from the response → pass it as cid
Send It By Text has six identifiers that look interchangeable and are not. Mixing them up is the most common integration bug we see, and the failure mode is nasty: a display ID that happens to also be a valid internal ID will not error, it will quietly act on the wrong contact. Read this once before writing any code.
Name
What it is
When to use it
ID
The internal client record ID, returned by searchclient
This is the value nearly every endpoint wants as cid.
cid
The parameter name for that internal client record ID
Your own custom / business / display client ID, e.g. ABC-10001
Searchable and useful for reference. Never pass it as cid
Two more IDs appear throughout but cause less trouble: userid is the user performing the action (message sender, note author, task assigner), and groupid is the numeric group ID, always the number and never the group name.
What searchclient gives you back
Every row contains all three client identifiers. The one you almost always want is ID.
Do not pass ClientID directly as cid. First call searchclient using that ClientID. Then take the response row's ID and pass that value as cid in the next API call.
Search by ClientID, then act on the internal ID
# 1. Search by custom ClientID
curl -X POST "https://useclientconnect.com/api/clientconnect_v2.aspx" \
-H "apikey: YOUR_API_KEY" \
--data-urlencode "request=searchclient" \
--data-urlencode "search=ABC-10001"
# Response includes:
# ID: 1234567
# UniqueClientID: 9876543
# ClientID: ABC-10001
# 2. Use response.ID as cid in the next call
curl -X POST "https://useclientconnect.com/api/clientconnect_v2.aspx" \
-H "apikey: YOUR_API_KEY" \
--data-urlencode "request=sendclienttext" \
--data-urlencode "cid=1234567" \
--data-urlencode "userid=101" \
--data-urlencode "from=8135551234" \
--data-urlencode "to=8135551212" \
--data-urlencode "text=Example message"
The one exception: contactid
Endpoints that explicitly ask for contactid generally want UniqueClientID, the visible Contact ID, notcid. The two you are most likely to hit are getcustomfieldsdata and getclientcreditors. Every parameter table in this reference states which one it expects, so check the table rather than assuming.
If you remember nothing else: search and display IDs help you find a contact; the internal ID (passed as cid) is what action endpoints need.
Responses & errors
All operations return JSON. Create operations such as addclient and adduser return a message string plus a UniqueId you should store and use in later calls.
Do not trust HTTP 200. Some failures return HTTP 200 with an error message in the body. Always parse the response and inspect message, success, and Error fields before treating a call as successful.
Turn off automatic redirect following. Some error paths answer with an HTTP 302 pointing at an error page whose body is { "message": "Error: Invalid request." }. Most HTTP clients follow redirects by default, so your code reads that error page as if it were the API response, with a 200 status, and the failure arrives looking like a malformed success. Configure your client not to follow redirects on API calls, and treat any 3xx from an API endpoint as an error. This is the same class of problem as the 200-with-error above, and it is harder to spot because the status code actively lies to you.
Both of these matter more than they look, because of what happens next: a client that cannot tell failure from success tends to retry. Retrying into the rate limits is what escalates a bad call into a blocked key. Failures should stop your traffic, not trigger a retry loop.
Error responses you will see in practice:
Common error shapes
{ "message": "No client found according to given criteria." }
{ "message": "Null request." }
{ "success": false, "Error": "No Record Found." }
{ "message": "Please provide client groups." }
{ "message": "IP rate limit exceeded." }
A defensive check that catches the common failure strings:
JavaScript
function assertSendItByTextSuccess(result) {
const text = JSON.stringify(result);
if (/rate limit exceeded|invalid request|null request|error occurred|no record found/i.test(text)) {
throw new Error(text);
}
return result;
}
eSignature calls wrap their result differently. Observed responses from the signing endpoints nest the outcome under a Response object rather than returning message at the top level, so a check written for the shapes above will miss them:
eSignature failure shape (observed)
{
"Response": {
"error": true,
"message": "The remote server returned an error: (402) Payment Required."
}
}
Parse Response.error and Response.message for signing calls, do not rely on the HTTP status, and log the raw body so support can help you quickly.
We are expanding these docs with full response schemas per operation. If you need a response shape that is not documented yet, make the call against test data and inspect the JSON, or ask us.
Rate limits
The API enforces the following limits. When you hit one, the response body contains a throttle message such as { "message": "API key rate limit exceeded." }.
Limit
Threshold
Behavior
Per IP
500 requests per 60 seconds
Throttle message, cooldown
Per API key
300 requests per 60 seconds
Throttle message, cooldown
Burst detection
200 requests per 10 seconds per key
Treated as a throttle
Cooldown
5 minutes
Back off fully before retrying
Repeated violations
3 within 1 hour
May lead to a key block
Practical guidance: pace batch jobs, avoid uncontrolled parallel fanout, and use exponential backoff with jitter. When you see a throttle message, stop immediately and wait out the cooldown; retry storms are what get keys blocked. Log the request type, HTTP status, and response body so support can help you quickly.
Quick start: find a contact
Grab your API key from Company Settings, replace the placeholder, and run:
That's the whole pattern. Every operation works the same way: POST to the base URL, pass your key in the header, pick the operation with request, and read the JSON that comes back.
Before your second call, read Which ID do I use? The response above contains three different client identifiers. Take ID and pass it as cid to follow-up calls like sendclienttext. Passing ClientID or UniqueClientID instead is the most common mistake developers make against this API.
Messaging (SMS & MMS)
Send and retrieve text messages, MMS, scheduled texts, and group messages.
Get recent SMS list
POSTrequest=getrecentsmslist
Returns the list of new (recent) SMS messages for a user.
Parameters
request
String
required
Set to getrecentsmslist
userid
Integer
required
ID of the staff user performing this action, never a client ID. Get user IDs from getcompanyusers or searchuser; they are stable per user.
showmytext
Integer
required
0 = all conversations, 1 = my conversations
Returns: JSON. A detailed response schema for this call is being added; see Responses & errors for the general pattern.
Heads up:Use the internal ID, not a display ID. Do not pass ClientID or UniqueClientID here. Pass the internal ID returned by searchclient. Passing a display ID either fails or, worse, silently targets the wrong contact.
Parameters
request
String
required
Set to sendclienttext
cid
Integer
required
Client the text is sent to. Internal client record ID, returned as ID by searchclient. Not ClientID and not UniqueClientID.
from
Integer
required
From mobile number (without country code)
to
Integer
required
To mobile number (without country code)
text
String
required
Message text to send
userid
Integer
required
ID of the staff user performing this action, never a client ID. Get user IDs from getcompanyusers or searchuser; they are stable per user.
Returns: JSON. A detailed response schema for this call is being added; see Responses & errors for the general pattern.
Example request
curl -X POST \
"https://useclientconnect.com/api/clientconnect_v2.aspx" \
-H "apikey: YOUR_API_KEY" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "request=sendclienttext" \
--data-urlencode "cid=123456" \
--data-urlencode "from=8135551234" \
--data-urlencode "to=8135555678" \
--data-urlencode "text=Hello from the Send It By Text API" \
--data-urlencode "userid=101"
Send client MMS
POSTrequest=sendclientmms
Sends an MMS (picture or file message) to a client. Note the different endpoint URL below; send as multipart form data with the file attached, or Base64 encoded via filename and body content.
Different endpoint URL. POST this request to https://useclientconnect.com/api/mms_v2.ashx instead of the standard base URL.
Heads up:Use the internal ID, not a display ID. Do not pass ClientID or UniqueClientID here. Pass the internal ID returned by searchclient. Passing a display ID either fails or, worse, silently targets the wrong contact.
Parameters
request
String
required
Set to sendclientmms
cid
Integer
required
Client the MMS is sent to. Internal client record ID, returned as ID by searchclient. Not ClientID and not UniqueClientID.
from
Integer
required
From mobile number (without country code)
to
Integer
required
To mobile number (without country code)
text
String
required
Message text to send
userid
Integer
required
ID of the staff user performing this action, never a client ID. Get user IDs from getcompanyusers or searchuser; they are stable per user.
filename
String
optional
File name including extension. Required only when sending the file Base64 encoded in the request body
File
File
conditional
The file to send. POST the file with the request, or supply it Base64 encoded via the body with filename
Returns: JSON. A detailed response schema for this call is being added; see Responses & errors for the general pattern.
Example request
curl -X POST \
"https://useclientconnect.com/api/mms_v2.ashx" \
-H "apikey: YOUR_API_KEY" \
-F "request=sendclientmms" \
-F "cid=123456" \
-F "from=8135551234" \
-F "to=8135555678" \
-F "text=Hello from the Send It By Text API" \
-F "userid=101"
Send scheduled text
POSTrequest=sendclientscheduledtext
Schedules a text message for future delivery, with optional repeat rules.
Parameters
request
String
required
Set to sendclientscheduledtext
from
Integer
required
From mobile number (without country code)
to
Integer
required
To mobile number (without country code)
text
String
required
Message text to send
month
Integer
required
Month the text should be sent
day
Integer
required
Day of the month the text should be sent
year
Integer
required
Year the text should be sent
hour
Integer
required
Hour the text should be sent
minute
Integer
required
Minute the text should be sent
ampm
String
required
am or pm
repeat
Integer
required
1 = no repeat, 2 = daily, 3 = weekly, 4 = monthly, 5 = every 3 months, 6 = every 6 months, 7 = yearly, 8 = weekdays only, 9 = weekends only
Returns: JSON. A detailed response schema for this call is being added; see Responses & errors for the general pattern.
Example request
curl -X POST \
"https://useclientconnect.com/api/clientconnect_v2.aspx" \
-H "apikey: YOUR_API_KEY" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "request=sendclientscheduledtext" \
--data-urlencode "from=8135551234" \
--data-urlencode "to=8135555678" \
--data-urlencode "text=Hello from the Send It By Text API" \
--data-urlencode "month=8" \
--data-urlencode "day=15" \
--data-urlencode "year=2026" \
--data-urlencode "hour=4" \
--data-urlencode "minute=30" \
--data-urlencode "ampm=pm" \
--data-urlencode "repeat=1"
Send precomposed message
POSTrequest=sendprecomposedmessage
Sends a saved (precomposed) message template to a client. Get template IDs from getcannedresponse.
Heads up:Use the internal ID, not a display ID. Do not pass ClientID or UniqueClientID here. Pass the internal ID returned by searchclient. Passing a display ID either fails or, worse, silently targets the wrong contact.
Parameters
request
String
required
Set to sendprecomposedmessage
cid
Integer
required
Client the message is sent to. Internal client record ID, returned as ID by searchclient. Not ClientID and not UniqueClientID.
from
Integer
required
From mobile number (without country code)
caid
Integer
required
ID of the precomposed response
userid
Integer
required
ID of the staff user performing this action, never a client ID. Get user IDs from getcompanyusers or searchuser; they are stable per user.
Returns: JSON. A detailed response schema for this call is being added; see Responses & errors for the general pattern.
Sends an SMS to a phone number that may not belong to an existing client. If the destination number is unknown, createclient controls whether a new client record is created.
Parameters
request
String
required
Set to sendclientsms
from
Integer
required
From mobile number (without country code)
to
Integer
required
To mobile number (without country code)
text
String
required
Message text to send
userid
Integer
required
ID of the staff user performing this action, never a client ID. Get user IDs from getcompanyusers or searchuser; they are stable per user.
createclient
Integer
required
1 = create a new client if the number is unknown, 0 = send without creating a client
firstname
String
optional
First name (used when creating a client)
lastname
String
optional
Last name (used when creating a client)
Returns: JSON. A detailed response schema for this call is being added; see Responses & errors for the general pattern.
Example request
curl -X POST \
"https://useclientconnect.com/api/clientconnect_v2.aspx" \
-H "apikey: YOUR_API_KEY" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "request=sendclientsms" \
--data-urlencode "from=8135551234" \
--data-urlencode "to=8135555678" \
--data-urlencode "text=Hello from the Send It By Text API" \
--data-urlencode "userid=101" \
--data-urlencode "createclient=1"
Get client text history by date
POSTrequest=getclienttexthistorybydate
Returns a client's text history within a date range.
Parameters
request
String
required
Set to getclienttexthistorybydate
cid
Integer
required
Client whose text history is required. Internal client record ID, returned as ID by searchclient. Not ClientID and not UniqueClientID.
startdate
Date (String)
required
Start date
enddate
Date (String)
required
End date
Returns: JSON. A detailed response schema for this call is being added; see Responses & errors for the general pattern.
Sends a message to every contact in a group, via SMS, email, or both. Note the dedicated endpoint URL below; send this call as multipart form data, not as a URL encoded body.
Different endpoint URL. POST this request to https://useclientconnect.com/api/sendgroupmsg.ashx instead of the standard base URL.
Heads up: Do not POST sendgroupmsg to the standard base URL. It has its own endpoint at /api/sendgroupmsg.ashx and expects form data fields. Use the numeric group ID from getcontactgroups, not the group name.
Parameters
request
String
required
Set to sendgroupmsg
userid
Integer
required
ID of the staff user performing this action, never a client ID. Get user IDs from getcompanyusers or searchuser; they are stable per user.
sendvia
Integer
required
1 = SMS, 2 = email, 3 = both
groupid
Integer
required
Group ID
textnote
String
conditional
Message body. Required when sendvia is 1 or 3
textnoteemail
String
conditional
Email body. Required when sendvia is 2 or 3
usecompanyemail
Integer
required
0 = false, 1 = true
emailsendinguserid
Integer
conditional
Required when sendvia is 2 or 3 and usecompanyemail is 0
useVpnNumberForText
Integer
required
0 = false, 1 = true
emailsubject
String
optional
Email subject
nosendtoreplied
Integer
optional
1 = skip contacts who have already replied, 0 = send to everyone
timezone
String
optional
eastern, central, mountain, or pacific
File
File
optional
Attachment (sends as MMS)
Returns: JSON. A detailed response schema for this call is being added; see Responses & errors for the general pattern.
Create, update, search, and list client (contact) records.
Add client
POSTrequest=addclient
Creates a client record and returns the new client's unique ID (CID) for use in later calls.
Heads up: Adding a contact does not reliably assign groups. Create the contact first, capture the returned ID, then call addcontactgroups to assign groups.
Parameters
request
String
required
Set to addclient
fname
String
required
First name
lname
String
required
Last name
userid
Integer
required
ID of the staff user performing this action, never a client ID. Get user IDs from getcompanyusers or searchuser; they are stable per user.
email
String
optional
Email address
mobile
Integer
optional
Mobile number (without country code)
otherphone
Integer
optional
Other phone number (without country code)
phonetype
String
optional
home, work, mobile 2, fax, or other
address
String
optional
Street address
city
String
optional
City
state
String
optional
State
zip
String
optional
ZIP code
company
String
optional
Company name
status
Integer
optional
0 = inactive, 1 = active
stopfurtheremails
Integer
optional
1 = stop all emails to this contact
stopfurthertexts
Integer
optional
1 = stop all text messages to this contact
clientid
String
optional
Your own custom / business client ID, stored for display and search. This is never the value to pass as cid.
scratchpad
String
optional
Description / scratchpad text
customnotification
Integer
optional
0 = disabled, 1 = enabled
customnotificationtext
String
optional
Custom notification text
ContactLocation
String
optional
Location of contact
JobTitle
String
optional
Job title
localnumber
Integer
optional
Local number (without country code)
customtext1-10
String
optional
Custom text fields 1 through 10
customdate1-5
Date
optional
Custom date fields 1 through 5
customlong1-5
Numeric
optional
Custom numeric fields 1 through 5
Returns: Returns message (success or error text) and UniqueId, which is the new client's internal client record ID (its CID). Despite the name, this is not a UniqueClientID; store it and pass it as cid on follow-up calls. See Which ID do I use?
Updates an existing client record. Send only the fields you want to change, plus the required id and userid. Changes are attributed to the user in userid, and a change to contactstatus is written to the contact's audit trail.
Heads up:id and cid are the same thing here: both mean the internal contact ID, and this endpoint accepts either. These examples use id. userid is required. It was missing from earlier versions of these docs, so an integration built against the old reference may be omitting it. Add it. Use the internal ID, not a display ID. Do not pass ClientID or UniqueClientID here. Pass the internal ID returned by searchclient. Passing a display ID either fails or, worse, silently targets the wrong contact. Separately: updating a contact does not reliably assign groups. Use addcontactgroups and deletecontactgroups for group membership.
Parameters
request
String
required
Set to updateclient
id
Integer
required
Client to update. Internal client record ID, returned as ID by searchclient. Not ClientID and not UniqueClientID.
email
String
optional
Email address
fname
String
optional
First name
lname
String
optional
Last name
mobile
Integer
optional
Mobile number (without country code)
otherphone
Integer
optional
Other phone number (without country code)
phonetype
String
optional
home, work, mobile 2, fax, or other
address
String
optional
Street address
city
String
optional
City
state
String
optional
State
zip
String
optional
ZIP code
company
String
optional
Company name
status
Integer
optional
0 = inactive, 1 = active
stopfurtheremails
Integer
optional
1 = stop all emails to this contact
stopfurthertexts
Integer
optional
1 = stop all text messages to this contact
clientid
String
optional
Your own custom / business client ID, stored for display and search. This is never the value to pass as cid.
scratchpad
String
optional
Description / scratchpad text
customnotification
Integer
optional
0 = disabled, 1 = enabled
customnotificationtext
String
optional
Custom notification text
ContactLocation
String
optional
Location of contact
JobTitle
String
optional
Job title
contactcomapny
String
optional
Contact company
contactstatus
Integer
optional
Contact status ID. Get valid IDs from getcontactstatuses. Changing this writes an entry to the contact's audit trail, attributed to userid. The audit trail is viewable in the app; it is not returned by getallnotesbyclientid.
contactstatusname
String
optional
Contact status name
localnumber
Integer
optional
Local number (without country code)
userid
Integer
required
ID of the user making the change (a staff user, not a client). Now actively used: the update is attributed to this user, and contact status changes are recorded in the audit trail.
customtext1-10
String
optional
Custom text fields 1 through 10
customdate1-5
Date
optional
Custom date fields 1 through 5
customlong1-5
Numeric
optional
Custom numeric fields 1 through 5
Returns: JSON. A detailed response schema for this call is being added; see Responses & errors for the general pattern.
Heads up:Use the internal ID, not a display ID. Do not pass ClientID or UniqueClientID here. Pass the internal ID returned by searchclient. Passing a display ID either fails or, worse, silently targets the wrong contact.
Parameters
request
String
required
Set to getclientdetails
cid
String
required
Client whose details are required. Internal client record ID, returned as ID by searchclient. Not ClientID and not UniqueClientID.
Returns: JSON. A detailed response schema for this call is being added; see Responses & errors for the general pattern.
Searches clients by name, mobile number, email, contact ID, or custom client ID. This is usually the first call in any workflow: search, then use the returned ID as cid in later calls.
Heads up:The ID field in the response is the one you want. It is the internal client record ID, and it is what nearly every other endpoint expects as cid. The same row also carries UniqueClientID (the visible Contact ID) and ClientID (your custom ID). Those are for display and searching, not for passing as cid. Full breakdown: Which ID do I use?
Parameters
request
String
required
Set to searchclient
search
String
required
Search term: first name, last name, mobile, email, contact ID, or custom client ID
Heads up:Use the internal ID, not a display ID. Do not pass ClientID, UniqueClientID or a visible Contact ID here. Pass the internal ID returned by searchclient.
Parameters
request
String
required
Set to getclientaffiliate
cid
Integer
required
Internal client record ID, returned as ID by searchclient. Not ClientID and not UniqueClientID.
Returns: The affiliate record. When the contact has no affiliate, the response is the literal string No record found. Treat that as an empty result, not an error.
Create and manage appointments, check availability, and pull time slots.
Create appointment
POSTrequest=createappointment
Creates an appointment for a client.
Heads up:Use the internal ID, not a display ID. Do not pass ClientID or UniqueClientID here. Pass the internal ID returned by searchclient. Passing a display ID either fails or, worse, silently targets the wrong contact.
Parameters
request
String
required
Set to createappointment
assignedto
Integer
required
User ID the appointment is assigned to
cid
Integer
required
Internal client record ID, returned as ID by searchclient. Not ClientID and not UniqueClientID.
ID of the staff user performing this action, never a client ID. Get user IDs from getcompanyusers or searchuser; they are stable per user.
clientid
Integer
required
Note the parameter is named clientid here, but it still expects the internal record ID. Internal client record ID, returned as ID by searchclient. Not ClientID and not UniqueClientID.
Heads up:Use the internal ID, not a display ID. Do not pass ClientID or UniqueClientID here. Pass the internal ID returned by searchclient. Passing a display ID either fails or, worse, silently targets the wrong contact.
Parameters
request
String
required
Set to addtask
cid
Integer
required
Client the task is for. Internal client record ID, returned as ID by searchclient. Not ClientID and not UniqueClientID.
assignedto
Integer
required
User the task is assigned to
priority
Integer
required
Priority ID
preselectedtask
Integer
required
Preselected task ID
followupdate
Date (String)
required
Follow up date
tasknotes
String
required
Task notes
userid
Integer
required
ID of the staff user performing this action, never a client ID. Get user IDs from getcompanyusers or searchuser; they are stable per user.
Returns: JSON. A detailed response schema for this call is being added; see Responses & errors for the general pattern.
Client notes, audit trail, and document upload and retrieval.
Add note
POSTrequest=addnote
Adds a note to a client.
Heads up:Use the internal ID, not a display ID. Do not pass ClientID or UniqueClientID here. Pass the internal ID returned by searchclient. Passing a display ID either fails or, worse, silently targets the wrong contact.
Parameters
request
String
required
Set to addnote
cid
Integer
required
Client the note is added to. Internal client record ID, returned as ID by searchclient. Not ClientID and not UniqueClientID.
note
String
required
Note text
userid
Integer
required
ID of the staff user performing this action, never a client ID. Get user IDs from getcompanyusers or searchuser; they are stable per user.
Returns: JSON. A detailed response schema for this call is being added; see Responses & errors for the general pattern.
Returns the user-created notes from the contact's Notes tab. This does not include audit trail entries, such as the status changes written by updateclient. The audit trail is viewable in the app only.
Heads up:Use the internal ID, not a display ID. Do not pass ClientID or UniqueClientID here. Pass the internal ID returned by searchclient. Passing a display ID either fails or, worse, silently targets the wrong contact.
Parameters
request
String
required
Set to getallnotesbyclientid
cid
Integer
required
Client whose notes are to be listed. Internal client record ID, returned as ID by searchclient. Not ClientID and not UniqueClientID.
Returns: JSON. A detailed response schema for this call is being added; see Responses & errors for the general pattern.
Returns the Documents tab rows (including eSignature records) for a contact by CID.
Heads up:Use the internal ID, not a display ID. Do not pass ClientID or UniqueClientID here. Pass the internal ID returned by searchclient. Passing a display ID either fails or, worse, silently targets the wrong contact. Separately: document URLs can contain spaces. URL encode them before downloading.
Parameters
request
String
required
Set to getdocsdetailsbycid
cid
Integer
required
Internal client record ID, returned as ID by searchclient. Not ClientID and not UniqueClientID.
Uploads a file to a contact's document section. Send as multipart form data with the file attached.
Heads up:Use the internal ID, not a display ID. Do not pass ClientID or UniqueClientID here. Pass the internal ID returned by searchclient. Passing a display ID either fails or, worse, silently targets the wrong contact. Separately: when using browser FormData, do not set the Content-Type header manually. The browser sets the multipart boundary for you; overriding it breaks the upload.
Parameters
request
String
required
Set to uploaddocument
userid
Integer
required
ID of the staff user performing this action, never a client ID. Get user IDs from getcompanyusers or searchuser; they are stable per user.
cid
Integer
required
Internal client record ID, returned as ID by searchclient. Not ClientID and not UniqueClientID.
File
File
required
File to upload, POSTed with the request
Returns: JSON. A detailed response schema for this call is being added; see Responses & errors for the general pattern.
Returns case document data by CID and document ID.
Heads up:Use the internal ID, not a display ID. Do not pass ClientID or UniqueClientID here. Pass the internal ID returned by searchclient. Passing a display ID either fails or, worse, silently targets the wrong contact.
Parameters
request
String
required
Set to getcasedocumentdata
CID
Integer
required
Internal client record ID, returned as ID by searchclient. Not ClientID and not UniqueClientID.
DocumentID
Integer
required
Document ID
Returns: JSON. A detailed response schema for this call is being added; see Responses & errors for the general pattern.
Sends a Document Data Portal (DDP) link to a client by email, text, or both. Include the [DDPLink] tag in the email body and/or SMS body; it is replaced with the actual link.
Heads up:Use the internal ID, not a display ID. Do not pass ClientID or UniqueClientID here. Pass the internal ID returned by searchclient. Passing a display ID either fails or, worse, silently targets the wrong contact.
Parameters
request
String
required
Set to SendDDPlink
CID
Integer
required
Internal client record ID, returned as ID by searchclient. Not ClientID and not UniqueClientID.
DocumentID
String
required
Document ID
SendVia
String
required
email, text, or both
emailsubject
String
conditional
Required when SendVia is email or both
emailbody
String
conditional
Required when SendVia is email or both. Must include the [DDPLink] tag
smsbody
String
conditional
Required when SendVia is text or both. Must include the [DDPLink] tag
Returns: JSON. A detailed response schema for this call is being added; see Responses & errors for the general pattern.
Send documents for signature, track status, and manage templates. Up to four signers; the third and fourth require an organization-level feature.
Yes/no convention. Several eSignature parameters accept yes or no. Anything other than yes is treated as no, and omitting the parameter entirely is the same as sending no. Enum values are shown lowercase; send them lowercase.
Which endpoint do I use?
There are two ways to send a document for signature, and they serve different workflows. Neither is deprecated. Capability statements in this section are endpoint-scoped: something that is true of one endpoint is not automatically true of the other.
signerpreference controls notification order, not access. It decides who is asked to sign first, and the other signer is not notified until the first has finished. It does not stop the other signer from signing early. Both signing links are created with the document and both come back in the create response, so a signer who has not been notified yet can still sign at any time if they have their link.
If your workflow depends on one party signing before the other, enforce that in your own application. The API does not enforce it, and a document can come back with the second signature applied first.
Delivering the signing link yourself. Both endpoints accept donotsend=1, which suppresses the email and SMS notifications while still creating the request and returning the signing link in the response. Use it when you want the link to reach the signer through your own product: in your app, in your own email, or in an SMS you send. The response field differs by endpoint (SignLink on signrequest, SignUrl on sendpretaggeddocumentforsignature), so read the one that matches the call you made.
White tag reference
To place signing fields, put these tags in the document body as white colored text where each field should appear. The signer never sees the tag itself.
Purpose
Signer 1
Signer 2
Signers 3–4
Constraints
Signature
^S1
^S2
^S3 / ^S4
Initial
^I1
^I2
^I3 / ^I4
Required when present. The signing UI will not let the signer finish while an initial field is empty.
Date (auto-filled)
^D1
^D2
^D3 / ^D4
Never required, because the system fills it. Auto-fills the current date from the server, in US Eastern time (daylight-saving aware; not fixed EST). A signer in Pacific time signing after 9:00pm local gets the following day’s date on the document.
Mandatory text
^M1
^M2
^M3 / ^M4
Maximum 500 characters. Over the cap, the request returns an error and nothing is saved for that field. Not truncated.
Optional text
^T1
^T2
^T3 / ^T4
Same 500-character cap and overflow behavior.
Radio button
^R1_G1
^R2_G1
n/a
Required when present._G[number] picks the group: up to 9 groups per signer (_G1–_G9), any number of options per group. A group is the combination of signer number and _G value.
There is no checkbox tag. For a checkbox-like choice, use a radio group.
Filled field values are returned on the PDF only. The text a signer types into ^M and ^T fields, and the option they pick in a ^R group, are placed onto the document. No API response carries them as data. Not getsignstatus, not the signing webhook, not the send response. If your workflow needs those values as structured data, you have to extract them from the signed PDF yourself. Plan for that before you design a form-style document.
Types are not consistent across surfaces
The same concept is serialized differently depending on which endpoint or channel you read it from. None of this is a bug you can work around by changing your request, so normalize on the way in.
The rule: treat RecordID as a string everywhere in your own code. It identifies the same record on every surface, but three of the four places you can read it disagree about whether it is quoted. Coercing to string on the way in costs nothing, survives whatever any single endpoint does, and avoids the comparison bugs you get when an integer 1234567 from one call fails to match the string "1234567" from another. This is engineering’s own recommendation, not just ours.
The string "false" is the one that bites. In JavaScript, PHP, Python and most loosely typed languages a non-empty string is truthy, so if (response.error) flags every successful template send as a failure. Compare the value explicitly rather than testing it for truthiness.
One Click Sign, and why the signing page sometimes changes
One Click Sign is a company-level feature. When it is enabled and a document’s tags are limited to signatures (^S), initials (^I) and dates (^D), the signer gets a streamlined experience: the document scrolls on their device with an execute button at the top and bottom, and one tap executes every signature, initial and date at once.
The fallback is automatic, and it is tag-driven. If the document contains any fillable text field (^M or ^T) or any radio group (^R), the system presents the traditional field-by-field signing page instead. Those fields need signer input, so one-tap execution is not possible.
No request parameter forces either mode. If your signing experience changed and you did not change your code, check whether the document’s tag set changed. Adding a single optional text field to an otherwise signature-only template switches every signer on that document to the long form.
The California cancellation window
California law gives debt settlement consumers a cooling-off period after they sign. Send It By Text implements it as a hold on the completed document, controlled by bypass3dayswait. This is an organization-level feature; contact support to have it enabled for your account.
The hold happens after signing, not before sending. This is the part integrations get wrong. The signer is notified immediately and signs immediately, exactly as they would on any other document. What is held is the executed document.
What happens, in order:
Step
What happens
1. Send
Normal. No delay. The signer is notified on whichever channel sendvia specifies.
2. Signer completes
Normal. They sign and are finished from their point of view.
3. Cancel link goes out
Immediately on completion, on the same channel the signing request used: text, email, or both, following sendvia. The link is good for 3 days.
4a. Signer cancels
Clicking the cancel link voids the document.
4b. Signer does nothing
After 3 days the document releases as signed, automatically.
Three calendar days, not business days.
The cancel link is not a notification, and donotsend does not suppress it. It is tied to a compliance event rather than to messaging, so it is sent even when you have suppressed the signing notification to deliver the link through your own channel. You cannot accidentally switch off the consumer's cancellation right by controlling your own delivery.
Omitting bypass3dayswait leaves the window in place. On an organization with the feature enabled, you have to opt out deliberately by sending yes. You cannot forget your way out of it.
Single signer only. The window does not apply to two-signer documents and there is no two-signer equivalent. Sending bypass3dayswait on a two-signer request has no effect.
Not documented yet: what getsignstatus reports while a document is held, whether the signing webhook fires at signature or at release, and what a cancellation looks like through the API. CA3DayWait on the getsignstatus response is related to this feature. We have asked engineering and will publish the answers here. Ask us before building a workflow that depends on the held state.
Third and fourth signers are an organization-level feature. The ^S3/^S4 tag series and the thirdsigner/fourthsigner parameters only work when the multi-signer feature is enabled for your organization. If you need three or four signers and get single- or two-signer behavior, contact support to have it enabled.
Send document for signature
POSTrequest=signrequest
Sends a PDF or Word document you have tagged yourself (see the white tag reference) to a contact for signature. Upload and send are one call: the tagged file and the delivery options travel in the same request, with no separate store step. POST the file with the request, or send it Base64 encoded in rawdata with a filename. Note the different endpoint URL below.
Different endpoint URL. POST this request to https://useclientconnect.com/api/signrequest_v2.ashx instead of the standard base URL.
Heads up:Use the internal ID, not a display ID. Do not pass ClientID or UniqueClientID here. Pass the internal ID returned by searchclient. Passing a display ID either fails or, worse, silently targets the wrong contact. Separately: secondsigner overrides the tags. A document tagged for two signers, sent with secondsigner=no or omitted, is processed as single-signer, silently.
Parameters
request
String
required
Set to signrequest
userid
String
required
ID of the staff user performing this action, never a client ID. Get user IDs from getcompanyusers or searchuser; they are stable per user.
cid
String
required
Internal client record ID, returned as ID by searchclient. Not ClientID and not UniqueClientID.
sendvia
String
required
Delivery channel for the first signer: email, text, or both. Not case sensitive. Each signer's channel is set independently; see secondsignersendvia.
ishandsign
String
optional
yes = the signer draws the signature by hand instead of a typed style. Yes/no convention above applies.
secondsigner
String
optional
The literal string yes enables the second signer. Anything else is treated as no, including a numeric 1, so sending this as an integer silently disables the second signer. It also takes precedence over the tags: a document containing ^S2-series tags sent with anything other than yes is processed as a single-signer document, the request succeeds with no error, and the second signer’s fields go unfilled. Check this parameter first when a second signer never receives the document.
secondsignerfirstname
String
optional
Second signer first name
secondsignerlastname
String
optional
Second signer last name
secondsigneremail
String
optional
Second signer email address
secondsignermobile
String
optional
Second signer mobile number
secondsignersendvia
String
optional
Second signer's delivery channel: email, text, or both, chosen independently of the first signer. Signer 1 can receive by text while signer 2 receives by email.
signerpreference
Integer
optional
1 notifies the first signer first, 2 notifies the second signer first. The other signer is not notified until the first has finished. This controls notification order only, not access. Both signing links are generated when the document is created and both are returned in the response, so a signer who has not yet been notified can still sign if they have their link. If your workflow requires one party to sign before the other, enforce it in your own application. Set this explicitly: the behavior when the parameter is omitted is not confirmed.
donotsend
Integer
optional
1 = suppress the email and SMS notifications, 0 or omitted = send them normally. Suppression is all it does: the request is still created and SignLink still comes back in the response, so you can deliver the link through your own channel (in-app, your own email or SMS). Behaves the same as donotsend on sendpretaggeddocumentforsignature. Added to this endpoint on 2026-08-31; older integrations built before that date will not have had it available.
bypass3dayswait
String
optional
yes skips the California cancellation window. Anything else, including omitting the parameter, leaves the window in place. The window is applied after signing, not before delivery: the signer receives and signs the document normally, and the completed document is then held for 3 calendar days before it releases. See the California cancellation window for what the signer experiences and what your integration sees. Single signer only, and only on organizations where the feature is enabled.
pdftoimage
Integer
optional
0 or omitted uses the standard PDF-to-HTML process. 1 converts the PDF to images instead. Leave this off unless you need it. PDF-to-Image only works when the dynamically sized PDF pages feature is enabled for the company, and it is backed by a paid subscription service, so pdftoimage=1 returns HTTP 402 with (402) Payment Required in the message when that subscription is not in place. That is a configuration failure, not a malformed request. Use it only when the company has the dynamic-size feature enabled and you are specifically working around a PDF-to-HTML rendering problem.
filename
String
conditional
File name with extension (.pdf or .docx). Required only when sending the file Base64 encoded in the body
rawdata
String (Base64)
conditional
File contents, Base64 encoded. Not required if the file is POSTed with the request
File
File
conditional
File POSTed with the request. Not required if rawdata is used
Returns: A Response object. On success, Response.error is false and the body carries RecordID (store this: it is the durable key for getsignstatus and for correlating signing webhooks), Filename, and the signing links. SignLink is returned at creation time, so you do not need a second call to get the first signer’s link. SecSignLink is populated at creation when secondsigner=yes, and is an empty string otherwise. Field names differ from sendpretaggeddocumentforsignature, which returns SignUrl/SecSignUrl for the same concepts. This endpoint returns RecordID as a quoted string (a number inside quotation marks), while getsignstatus and the template endpoint return it unquoted. See types across surfaces for the full picture and the rule to follow. Field names and value types here are confirmed by engineering. The complete field list is drawn from production responses rather than a published contract, so treat an unfamiliar extra field as possible rather than impossible.
Example request: file POSTed as multipart form data
{
"Response": {
"error": false,
"message": "A document for the signature has been sent to a client",
"RecordID": "1234567",
"Filename": "Example Agreement_000000000000000000.pdf",
"SignLink": "https://useclientconnect.com/_examplelink",
"SecSignLink": ""
}
}
Send pre-tagged document for signature
POSTrequest=sendpretaggeddocumentforsignature
Sends a stored signature template (or several) to a contact for signature. This is the template path: the document exists beforehand, and this call sends it. Template IDs come from getsignaturetemplatesandgrouplist. Up to four signers; the third and fourth require the multi-signer org feature.
Heads up:error comes back as the string"false", not the boolean false. In JavaScript, PHP and most loosely typed languages a non-empty string is truthy, so if (response.error) treats every success as a failure. Compare explicitly: String(response.error) === "true" means it failed. Separately: use the internal ID, not a display ID. Do not pass ClientID or UniqueClientID here. Pass the internal ID returned by searchclient. Passing a display ID either fails or, worse, silently targets the wrong contact.
Parameters
request
String
required
Set to sendpretaggeddocumentforsignature
userid
String
required
ID of the staff user performing this action, never a client ID. Get user IDs from getcompanyusers or searchuser; they are stable per user.
cid
String
required
Internal client record ID, returned as ID by searchclient. Not ClientID and not UniqueClientID.
templateid
String
required
Template record ID. Single entry or comma separated multiple entries
sendvia
String
required
email, text, or both. Values are not case sensitive.both was added to this endpoint in August 2026; if you are reading an older integration that avoids it here, that constraint no longer applies.
donotsend
Integer
optional
1 = suppress the email and SMS notifications, 0 or omitted = send them normally. Suppression is all it does: the request is still created and SignUrl still comes back in the response, so you can deliver the link through your own channel (in-app, your own email or SMS). The same parameter works on signrequest. If you lose the link, getsigninglink returns the most recent unsigned request for the contact's mobile number.
bypass3dayswait
String
optional
yes skips the California cancellation window. Anything else, including omitting the parameter, leaves the window in place. The window is applied after signing, not before delivery: the signer receives and signs the document normally, and the completed document is then held for 3 calendar days before it releases. See the California cancellation window for what the signer experiences and what your integration sees. Single signer only, and only on organizations where the feature is enabled.
1 notifies the first signer first, 2 notifies the second signer first. The other signer is not notified until the first has finished. This controls notification order only, not access. Both signing links are generated when the document is created and both are returned in the response, so a signer who has not yet been notified can still sign if they have their link. If your workflow requires one party to sign before the other, enforce it in your own application. Set this explicitly: the behavior when the parameter is omitted is not confirmed.
secondsigner
String
optional
The literal string yes enables the second signer. Anything else is treated as no, including a numeric 1, so sending this as an integer silently disables the second signer. It also takes precedence over the tags: a document containing ^S2-series tags sent with anything other than yes is processed as a single-signer document, the request succeeds with no error, and the second signer’s fields go unfilled. Check this parameter first when a second signer never receives the document.
secondsignerfirstname
String
optional
Second signer first name
secondsignerlastname
String
optional
Second signer last name
secondsigneremail
String
optional
Second signer email address
secondsignermobile
String
optional
Second signer mobile number
secondsignersendvia
String
optional
email or text
thirdsigner
String
optional
yes or no. Requires the multi-signer org feature; without it, third-signer parameters and ^S3-series tags do not work.
Returns:RecordID (integer) is the durable key for getsignstatus and webhook correlation. PackageId identifies the package. SignUrl is the first signer’s link, returned at creation time; SecSignUrl carries the second signer’s link where applicable and is otherwise blank. Note the field names differ from signrequest, which returns SignLink/SecSignLink for the same concepts.
{
"error": "false",
"message": "A document for the signature has been sent to a client",
"RecordID": 1234567,
"PackageId": 12345678901234,
"Filename": "Example_Agreement_000000000000.pdf",
"SignUrl": "https://useclientconnect.com/_examplelink",
"SecSignUrl": ""
}
Get signature status
POSTrequest=getsignstatus
Returns the status of a document sent for signature.
Heads up:The full set of Status values is not published.Unsigned, Signed and Unsigned By Both Signers have all been seen in production, so the values are descriptive phrases rather than a short enum. Do not write an exhaustive switch on Status: branch on the paired date fields, treat unrecognized Status values as their own case, and tell us what you see. Separately: poll this endpoint to recover missed webhooks. Signing callbacks are sent once with no retry (see the signing webhook), so this call is the only way to learn about an event your receiver did not capture.
Parameters
request
String
required
Set to getsignstatus
recordid
Integer
required
The RecordID returned as Response.RecordID when the document was sent with signrequest. This is the correlation key, not PackageID.
Returns:Per-signer state comes back through paired fields. The unpaired field is the first signer, the Second prefixed field is the second signer, and CombinedSignedFileUrl is the merged document: ViewedDate / SecondViewedDate, SignedDate / SecondSignedDate, SignedFileUrl / SecondSignedFileUrl. An empty string means it has not happened yet, not null. The second-signer fields appear on two-signer documents; a single-signer document returns the unpaired fields only.
Status is document level, not per signer, so read the paired date fields to find out where each signer actually is. Because the two signers can complete out of order (see signerpreference on signrequest), SecondSignedDate can be populated while SignedDate is still empty.
SignedFileUrl is blank until a signed document exists; once populated it is the same document the signing webhook refers to as MediaUrl. CA3DayWait reports whether the record is subject to the three day review wait (see bypass3dayswait). Signed-document URLs do not require the API key to fetch, and file names can contain spaces, so URL-encode before fetching. RecordID is serialized here as a number and as a string elsewhere; normalize it to a string.
Observed in live traffic. This shape comes from real production responses captured by an integrator, not from a published contract. Send It By Text has not yet confirmed it as guaranteed, so treat unfamiliar or missing fields as possible rather than impossible, and parse defensively.
Returns the last unsigned signing request for the supplied mobile number. Useful for re-sending a signing link when a contact says they lost the original text or email. It is intended for individual signing requests and should not be used for package or group signature workflows.
Heads up: Two limits to know. mobiletel must be digits only and must include the country code, e.g. 18135551212. No +, no spaces, hyphens or parentheses, so an E.164 style value like +18135551212 will not work here. This is the one place in the API that wants the country code. And only the last unsigned request comes back, so if several documents are outstanding you will not see the others. Not compatible with package or group signature requests.
Parameters
request
String
required
Set to getsigninglink
mobiletel
Integer
required
Mobile number, digits only, including the country code. No +, spaces, hyphens or parentheses. Example: 18135551212
Returns: JSON. A detailed response schema for this call is being added; see Responses & errors for the general pattern.
Contact groups, contact statuses, and custom field definitions and data.
Add new group
POSTrequest=addnewgroup
Creates a new contact group for the company, so an automation can set up its own groups instead of someone creating them in the Send It By Text UI first. The group is created under the company that owns your API key; there is no company ID parameter.
Heads up:Duplicate handling and the response body are not documented yet. Until they are, do not assume this call returns the new GroupID, and do not assume it refuses a name that already exists. The safe pattern: call getcontactgroups first and check whether a group with that name already exists; create it only if it does not; then call getcontactgroups again to read the numeric GroupID you will pass to addcontactgroups, listclientbygroup and sendgroupmsg. Every group operation after this one takes the numeric ID, never the name.
Parameters
request
String
required
Set to addnewgroup
groupname
String
required
Name of the new group. Match it exactly when you look the group up again in getcontactgroups.
Returns: JSON. A detailed response schema for this call is being added; see Responses & errors for the general pattern.
Assigns a contact to one or more groups. This is the reliable way to set group membership; addclient and updateclient do not reliably assign groups.
Heads up:Use the internal ID, not a display ID. Do not pass ClientID or UniqueClientID here. Pass the internal ID returned by searchclient. Passing a display ID either fails or, worse, silently targets the wrong contact. Separately: use numeric group IDs (from getcontactgroups), never group names.
Parameters
request
String
required
Set to addcontactgroups
cid
Integer
required
Internal client record ID, returned as ID by searchclient. Not ClientID and not UniqueClientID.
Heads up:Use the internal ID, not a display ID. Do not pass ClientID or UniqueClientID here. Pass the internal ID returned by searchclient. Passing a display ID either fails or, worse, silently targets the wrong contact.
Parameters
request
String
required
Set to deletecontactgroups
cid
Integer
required
Internal client record ID, returned as ID by searchclient. Not ClientID and not UniqueClientID.
clientgroups
String
required
Comma separated group IDs
Returns: JSON. A detailed response schema for this call is being added; see Responses & errors for the general pattern.
Returns all available custom field definitions. Custom fields are configured per company, so always check the definitions before relying on a field label.
Heads up:contactid here is the visible Contact ID (UniqueClientID from searchclient), not the internal cid. This is the main exception to the usual ID rule; see the identifier guide.
Send It By Text can push events to your systems, so you do not have to poll.
Webhooks follow a different pattern from the standard API: they are HTTP POSTs from Send It By Text to a URL you host. To have a webhook configured for your account, contact developer support.
Signing status webhook
Fires when the status of a document sent for signature changes. Use it instead of polling getsignstatus.
Payloads arrive form-encoded, not as JSON. Observed signing webhook requests are sent as application/x-www-form-urlencoded POST fields. Parse them the way you would parse an ordinary HTML form post. Do not require a JSON body unless you have put your own adapter in front of the receiver. The example below is normalized to JSON for readability only; it is not the wire format.
Example payload (normalized to JSON for readability)
Webhook endpoints are open. There is no signature to validate. Webhooks configured in Company Settings are delivered without authentication: no HMAC, no signature header, nothing built in for your receiver to verify. Your receiver is the security boundary. Use an unguessable token in the receiver URL, put the endpoint behind an authenticated gateway, restrict by source IP where your platform allows it, and allowlist the exact route. Treat every payload field as untrusted input, and confirm anything consequential with getsignstatus before acting on it.
Handling guidance
Each event is sent exactly once. There is no retry. For the Viewed and Signed actions the system delivers the payload one time, and a delivery that fails is not replayed. If your receiver is down, slow, or throws, that event is gone. Build accordingly: accept the request, store the raw payload durably, and return 200 quickly, then do your processing from the stored copy. Never do the real work inline before responding.
Poll getsignstatus as your recovery path. Because missed deliveries are not resent, polling is the only way to reconcile. Treat the webhook as a latency optimization over polling, not as a guaranteed event stream, and reconcile anything financially or legally consequential. Ordering across events is not guaranteed either, so keep the receiver idempotent on RecordID: it costs nothing and protects you if you ever have more than one receiver configured.
Correlate on RecordID. It is the durable key shared by signrequest, getsignstatus and these payloads. PackageID also appears here, but do not build your primary correlation around it unless Send It By Text has confirmed a package-based contract for your workflow.
Treat RecordID as an opaque string, even where a response serializes it as a number. Form fields arrive as strings, signrequest returns it quoted, and getsignstatus returns it unquoted. Normalizing to string in your own system avoids a whole class of comparison bug.
The signed-document URL is named differently depending on where you read it:MediaUrl in observed webhook traffic, SignedFileUrl from getsignstatus. Same concept, so use whichever name the source you are parsing actually gives you.
Fetching signed documents: observed signed-document URLs do not require the API key. File names can contain spaces, so URL-encode spaces and other unsafe characters before fetching from curl, a server-side HTTP client, or a background job.
Ignore blank or incomplete signing events.
CID is the internal API cid; ContactID is the visible display ID (see the identifier guide).
Store raw payloads only in secure logs with PII controls.
Not confirmed yet, so do not design around it: expiry on signing links and signed-document URLs, and whether a two-signer envelope reports per-signer state, document-level state, or both. Ask us before you build on either.
Notes intake webhook
A webhook-style endpoint at https://useclientconnect.com/addnotes_webhook.aspx accepts a JSON body (with the key inside the body rather than a header) for pushing notes from external systems. Because it does not follow the standard API pattern, contact developer support for the current contract before building against it.
Code samples
The same call in the language you are already using, plus recipes for the most common workflows.
A reusable JavaScript helper
Wraps the request pattern once, then every call is a one-liner. Note the defensive error check; see Responses & errors.
JavaScript
async function postSendItByText(params) {
const response = await fetch("https://useclientconnect.com/api/clientconnect_v2.aspx", {
method: "POST",
headers: {
apikey: process.env.SENDITBYTEXT_API_KEY,
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json, text/plain, */*"
},
body: new URLSearchParams(params).toString()
});
const raw = await response.text();
try {
const parsed = JSON.parse(raw);
if (parsed?.message && /error|invalid|failed|exceeded/i.test(parsed.message)) {
throw new Error(parsed.message);
}
return parsed;
} catch (err) {
throw new Error(`Send It By Text API error: ${raw}`);
}
}
Recipe: find a contact and send an SMS
JavaScript
// 1. Find the contact
const matches = await postSendItByText({
request: "searchclient",
search: "8135551212"
});
const contact = matches[0];
// 2. Send the text, using the internal ID as cid
await postSendItByText({
request: "sendclienttext",
cid: String(contact.ID),
from: "8135551234",
to: "8135551212",
text: "Thanks for contacting us. We received your request.",
userid: "101"
});
Recipe: create a contact and assign a group
JavaScript
// 1. Create the contact and capture the returned ID
const created = await postSendItByText({
request: "addclient",
fname: "Jane",
lname: "Smith",
mobile: "8135551212",
email: "jane@example.com",
userid: "101"
});
const cid = created.UniqueId;
// 2. Assign the group separately (addclient does not do this reliably)
await postSendItByText({
request: "addcontactgroups",
cid: String(cid),
clientgroups: "42"
});
Python
Python
import os
import requests
url = "https://useclientconnect.com/api/clientconnect_v2.aspx"
headers = {"apikey": os.environ["SENDITBYTEXT_API_KEY"]}
data = {"request": "searchclient", "search": "Jane Smith"}
res = requests.post(url, headers=headers, data=data, timeout=30)
res.raise_for_status()
print(res.json())
using System.Net.Http;
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post,
"https://useclientconnect.com/api/clientconnect_v2.aspx");
request.Headers.Add("apikey",
Environment.GetEnvironmentVariable("SENDITBYTEXT_API_KEY"));
request.Content = new FormUrlEncodedContent(new Dictionary<string, string> {
{ "request", "searchclient" },
{ "search", "Jane Smith" }
});
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
response.EnsureSuccessStatusCode();
Console.WriteLine(body);
Support
Stuck on an integration, need a webhook configured, or found something these docs get wrong?
Contact developer support and a ticket goes straight to our API team. You can also email api@senditbytext.com directly or call (800) 800-4045. Support hours are Monday through Friday, 10:00 AM to 6:00 PM Eastern.
When reporting an issue, include the operation name, the parameters you sent (never your API key), the HTTP status, and the response body. That is usually everything we need to diagnose it on the first pass.
Changelog
A dated record of corrections and confirmed behavior, so you can tell what changed rather than wondering whether you misread something months ago. Where a behavior was confirmed directly with engineering, the entry says so.
September 22, 2026
addnewgroup confirmed live by engineering. Groups can be created entirely by API, so an automation no longer needs someone to set them up in the Send It By Text UI first. The group is created under the company that owns your API key; there is no company ID parameter.
Duplicate-name handling and the response body are not documented yet, so the endpoint now carries a safe pattern: check getcontactgroups for the name before creating, then read the numeric GroupID back from it afterwards.
September 16, 2026
Behavior confirmed by Send It By Text. This entry corrects a significant error in how this reference described bypass3dayswait.
Corrected:bypass3dayswait was described as skipping a review wait before the document is sent, which implied the signer would not receive it for three days. That is wrong. The hold is applied after signing, not before delivery. The signer is notified immediately and signs immediately; what is held is the completed document, for 3 calendar days, before it releases as signed.
New section: the California cancellation window, covering the full sequence. On completion the signer receives a cancel link on the same channel the signing request used, good for 3 days. Clicking it voids the document. No cancellation and the document releases automatically.
The cancel link is not suppressed by donotsend, because it is tied to a compliance event rather than to messaging. Suppressing your signing notification to deliver the link yourself does not switch off the consumer's cancellation right.
Omitting bypass3dayswait leaves the window in place, so opting out is deliberate. Documented explicitly, since a safe default is worth stating.
Clarified that the window is single signer only with no two-signer equivalent, and that it is an organization-level feature that has to be enabled.
Marked as not yet documented: what getsignstatus reports while a document is held, whether the signing webhook fires at signature or at release, and what a cancellation looks like through the API. CA3DayWait relates to this feature.
September 15, 2026
Field-verified against a live two-signer record. This entry corrects two things this reference published earlier.
Corrected, and this one matters for compliance workflows:signerpreference was described as making signing "strictly sequential" with "no parallel signing mode", which reads as though the API enforces an order. It does not. It controls notification order only. Both signing links are generated when the document is created and both are returned in the create response, so a signer who has not been notified yet can sign at any time using their link. Verified 2026-09-15: a document created with signerpreference=1, with the first signer unsigned and the second never notified, rendered the full signing flow for the second signer with no gate. If your workflow requires one party to sign before the other, enforce it in your own application.
Corrected:getsignstatus was documented as returning exactly six fields with Unsigned / Signed as the complete Status set. That holds for a single-signer document. A two-signer document also returns SecondViewedDate, SecondSignedDate, SecondSignedFileUrl and CombinedSignedFileUrl, and Unsigned By Both Signers has been observed as a Status value. The response schema and the Status guidance are updated, and the enum is marked as not fully enumerated. Do not write an exhaustive switch on Status; branch on the paired date fields instead.
Because signers can complete out of order, SecondSignedDate can be populated while SignedDate is still empty. Status is document level, so the paired date fields are the only way to tell where each signer actually is.
secondsigner sharpened: the value must be the literal string yes. Anything else is treated as no, including a numeric 1, and the request succeeds while creating a single-signer document. The type has been String since August 25; a report that it read Integer on this page was checked against the live page and the internal reference and did not hold.
September 14, 2026
Added a worked two-signer example to signrequest, showing secondsigner=yes with the second signer’s details and each signer on a different delivery channel. The existing two examples both covered a single signer, which left the most error-prone case without a reference request to copy.
Tightened the SecSignLink description: it is populated at creation when secondsigner=yes, and is an empty string otherwise.
August 31, 2026
API change, deployed and tested by engineering (F. Zia), verified against the internal reference before publishing.
New parameter:signrequest now accepts the optional donotsend. 1 suppresses the email and SMS notifications while still creating the request and returning SignLink in the response, so you can deliver the signing link through your own channel. Omitting it, or sending 0, leaves existing behavior unchanged.
This closes the one asymmetry between the two send endpoints on this feature: donotsend previously existed only on sendpretaggeddocumentforsignature, and this reference said so. It now works on both, and the pattern is written up under which endpoint do I use. The response field still differs by endpoint: SignLink versus SignUrl.
August 27, 2026
Behavior confirmed in writing by engineering (F. Zia). This round resolves the items the previous two entries left open, and corrects two things this reference published earlier.
Corrected:sendvia=bothis supported on sendpretaggeddocumentforsignature. Support was added in August 2026; this page previously said it was unavailable, which was true when written and is no longer true. sendvia values are also not case sensitive, on either endpoint.
Corrected: the signing webhook was documented as at-least-once delivery. Engineering confirms each event is sent exactly once with no retry mechanism. Guidance rewritten: store the payload durably and return 200 before processing, and poll getsignstatus to recover anything missed, since failed deliveries are never replayed.
Confirmed: webhook endpoints are open, with no authentication on delivery. The security guidance added on August 26 stands, now on engineering’s word rather than observation.
Added the template endpoint response schema, including the trap that error comes back as the string"false" rather than a boolean, which makes a naive truthiness check treat every success as a failure.
New section: types are not consistent across surfaces. RecordID identifies the same record everywhere but is serialized four different ways: a quoted string from signrequest, an unquoted integer from the template endpoint and getsignstatus, and a string in webhook form posts. Engineering’s recommendation, now published: treat it as a string in your own code. The two send endpoints also name their signing links differently, SignLink/SecSignLink versus SignUrl/SecSignUrl.
New section: One Click Sign and the automatic fallback. A signature-only document (^S, ^I, ^D) gives a one-tap execute experience where the company feature is enabled; adding any ^M, ^T or ^R tag silently switches every signer to the traditional field-by-field page. No parameter controls this.
Confirmed: filled field values are never returned as data. Text and radio selections land on the PDF only, and no response or webhook carries them. Extract from the signed document if you need the values.
Tag requiredness completed: ^I and ^R are required when present; ^D is never required because the system fills it.
getsignstatus response schema confirmed, and Unsigned / Signed confirmed as the completeStatus set.
pdftoimage explained: 1 needs the dynamically sized PDF pages feature plus a paid subscription, which is why it can return 402. Omit it unless you are working around a PDF-to-HTML rendering problem.
New warning in Responses & errors: error paths can answer with a 302 redirect to an error page, so clients that follow redirects read a failure as a 200 success. Disable redirect following and treat any 3xx as an error.
Rate limits re-confirmed by engineering. The published numbers are unchanged.
August 26, 2026
Operational notes added from live API traffic and captured webhook payloads supplied by a production integrator. Items sourced this way are labelled observed in live traffic on the page; they are accurate to real responses but are not yet engineering-confirmed contracts.
Corrected: the signing webhook example was presented as JSON with no caveat. Observed payloads are form-encoded POST fields; the JSON block is now labelled as normalized for readability.
Corrected: the previous guidance to "validate the webhook's auth token" implied a first-party signature that observed traffic does not contain. Replaced with an explicit statement that your receiver is the security boundary, plus what to do about it.
Added observed response schemas for signrequest and getsignstatus, including SignLink and SecSignLink being returned at creation time.
Added the eSignature failure envelope (Response.error / Response.message) to Responses & errors, which differs from the top-level shapes used elsewhere in the API.
RecordID is serialized as a string in some responses and a number in others. Documented the normalization rule, and that RecordID rather than PackageID is the correlation key.
Documented that the signed-document URL appears as MediaUrl in webhook traffic and SignedFileUrl from getsignstatus, that these URLs do not require the API key, and that file names may contain spaces.
Webhook delivery recorded as at-least-once with no ordering guarantee. Superseded on August 27: engineering confirms delivery is exactly once with no retry. Ordering is still not guaranteed.
pdftoimage=1 can return a 402 Payment Required upstream error, meaning the conversion path is not enabled for the account rather than that the request was malformed.
Marked as needing confirmation: webhook retry schedule and cutoff, URL expiry, the complete Status enum, the sendpretaggeddocumentforsignature response body, and the multi-signer callback sequence.
August 25, 2026
eSignature section expanded from an engineering review. Behavior confirmed by engineering (F. Zia); server timezone behavior confirmed by Send It By Text.
Added endpoint selection guidance: signrequest (your own tagged PDF, upload and send in one call, sendvia=both supported) versus sendpretaggeddocumentforsignature (stored template). Capability statements are now endpoint-scoped. Superseded in part on August 27: this entry originally recorded that the template endpoint accepted email or text only. That is no longer the case.
Documented that the second signer is not notified until the first finishes. Superseded in part on September 15: this entry originally called signing "strictly sequential" with "no parallel mode". That is true of notification order only. The API does not gate access, and either signer can sign at any time using their link.
Delivery channel is per signer: sendvia and secondsignersendvia are chosen independently.
Warning added: secondsigner takes precedence over the tags. A two-signer document sent with secondsigner=no or omitted becomes single-signer, silently.
Text fields (^M/^T) cap at 500 characters; over the cap the request errors and nothing is saved for that field.
^D auto-fills the date from the server in US Eastern time (daylight-saving aware), with the late-evening West Coast consequence spelled out.
bypass3dayswait: documented that it relates to a California consumer-rights requirement, and that omitting the parameter equals no. Superseded in part on September 16: this entry described it as a wait before sending. The hold is applied after signing. See the California cancellation window.
donotsend: suppresses only the notification; the signing link is still generated and the document remains signable.
Third and fourth signers documented as an organization-level feature that must be enabled for your account.
White tags rewritten as a reference table with constraints inline; noted that no checkbox tag exists.
userid descriptions across all endpoints now say it is the staff user performing the action and where to get one.
August 11, 2026
Corrections from an engineering review (F. Zia).
updateclient: id and cid are the same internal contact ID; either is accepted.
getsigninglink: mobiletel must include the country code, and the endpoint is not compatible with package or group signature requests.
getallnotesbyclientid returns Notes-tab notes only, not the audit trail.
adduser: omitted typeid creates a Normal User.
getclientaffiliate: an empty result returns No record found.
August 10, 2026
updateclient: userid documented as required (it was missing from earlier versions of these docs), and contact status changes are now recorded in the audit trail.
August 7, 2026
Portal launched: 119 operations, the identifier guide, rate limits, error handling, and code samples in five languages.