Texts
Send a Text
POST /v1/texts texts:write
We carry Texts on your own carrier account, and consent, registration, content and hours are yours: see the Texts guide. We refuse a number on Do Not Call or with texts revoked, and not one with no grant. A refused attempt still has its row, named by error.details.textId. A 201 means we asked the carrier: status says how that went, and unknown is never sent again automatically. Use an Idempotency-Key: a retry without one sends another Text.
Headers
Idempotency-KeystringMakes the request safe to retry. For 24h, a request with the same key and body replays the stored response (with
Idempotent-Replayed: true); the same key with a different request is a 422idempotency_key_reused, and while the first request is still running a 409idempotency_key_in_use. Server errors, 409s and 429s are not stored, except one that saysHcc-Should-Retry: false, such as504 call_outcome_unknown.
Body
-
tostring required -
bodystring required -
fromstringOne of your numbers. Without one, the workspace's caller ID default
Responses
- 201
The carrier was asked
Fields
-
dataText requiredOne SMS, inbound or outbound, including a refused attempt: the audit row of that attempt.
Fields
-
idstring required -
threadIdstring | null required -
direction"inbound" | "outbound" required -
source"desk" | "api" | "campaign" | "flow" | null requiredWho started it. An inbound Text has none
-
decisionstring requiredsent,received, or the refusal that stopped it (do_not_call, …) -
status"pending" | "unknown" | "accepted" | "sent" | "delivered" | "undelivered" | "failed" | "refused" | "received" required -
fromstring | null required -
tostring required -
bodystring | null requiredNull once retention has cleared it
-
segmentsinteger requiredAn estimate of the carrier's message count
-
keyword"stop" | "start" | "help" | null required -
possibleOptOutboolean requiredFree text that may be an opt-out. Nothing was revoked: deciding is your duty, by code or a person
-
leadIdstring | null requiredOnly when one Lead is on the number
-
consentIdstring | null requiredThe
textsgrant on file when this went, if any -
timezonestring | null requiredThe Lead's time zone, if known
-
failureReason"recipient_blocked" | "invalid_recipient" | "sender_not_registered" | "filtered" | "unreachable" | "rejected" | "unknown" | null required -
errorCodestring | null requiredThe carrier's own code
-
createdAtstring required -
sentAtstring | null required -
deliveredAtstring | null required -
failedAtstring | null required -
receivedAtstring | null required
-
-
- 400
Invalid request
- 401
Missing or invalid API key
- 402
A billing stop: outbound only. Inbound and STOP keep working
- 404
Not found
- 409
idempotency_key_in_use: a request with this key is still running - 422
do_not_call,opted_out,caller_id_not_owned,no_caller_id,messaging_unavailableornumber_not_registered, or anIdempotency-Keyreused with a different request - 429
rate_limited, or another limit named byerror.code
Get one Text, with its body
GET /v1/texts/{id} texts:read
Path
idstring required
Responses
- 200
The Text
Fields
-
dataText requiredOne SMS, inbound or outbound, including a refused attempt: the audit row of that attempt.
Fields
-
idstring required -
threadIdstring | null required -
direction"inbound" | "outbound" required -
source"desk" | "api" | "campaign" | "flow" | null requiredWho started it. An inbound Text has none
-
decisionstring requiredsent,received, or the refusal that stopped it (do_not_call, …) -
status"pending" | "unknown" | "accepted" | "sent" | "delivered" | "undelivered" | "failed" | "refused" | "received" required -
fromstring | null required -
tostring required -
bodystring | null requiredNull once retention has cleared it
-
segmentsinteger requiredAn estimate of the carrier's message count
-
keyword"stop" | "start" | "help" | null required -
possibleOptOutboolean requiredFree text that may be an opt-out. Nothing was revoked: deciding is your duty, by code or a person
-
leadIdstring | null requiredOnly when one Lead is on the number
-
consentIdstring | null requiredThe
textsgrant on file when this went, if any -
timezonestring | null requiredThe Lead's time zone, if known
-
failureReason"recipient_blocked" | "invalid_recipient" | "sender_not_registered" | "filtered" | "unreachable" | "rejected" | "unknown" | null required -
errorCodestring | null requiredThe carrier's own code
-
createdAtstring required -
sentAtstring | null required -
deliveredAtstring | null required -
failedAtstring | null required -
receivedAtstring | null required
-
-
- 400
Invalid request
- 401
Missing or invalid API key
- 404
Not found
- 429
rate_limited, or another limit named byerror.code
List Threads, the latest first
GET /v1/text-threads texts:read
Query
phonestringThe Threads of one number
channel"sms" | "email"The Threads of one Channel. Without it, all
limitintegercursorstring
Responses
- 200
A page of Threads
Fields
-
dataTextThread[] requiredFields
-
idstring required -
channel"sms" | "email" requiredWhere the conversation happens. Only
smstoday -
contactAddressstring requiredThe other party: their E.164 number, for SMS
-
ourAddressstring requiredOur end: your number, for SMS
-
phonestring | null requiredTheir number, on a Channel with phones
-
ourNumberstring | null requiredYour number, on a Channel with phones
-
leadIdstring | null required -
ownerRepIdstring | null required -
status"open" | "closed" required -
possibleOptOutAtstring | null requiredThe last time a Text here read like an opt-out. Nothing acts on it but you
-
lastTextAtstring required -
createdAtstring required
-
-
nextCursorstring | null required
-
- 400
Invalid request
- 401
Missing or invalid API key
- 404
Not found
- 429
rate_limited, or another limit named byerror.code
List Text flows
GET /v1/text-flows flows:read
Responses
- 200
The Text flows deployed
Fields
-
dataTextFlow[] requiredFields
-
idstring required -
namestring required -
versionstring required -
createdAtstring required -
updatedAtstring required
-
-
- 400
Invalid request
- 401
Missing or invalid API key
- 404
Not found
- 429
rate_limited, or another limit named byerror.code
Register Text flows
PUT /v1/text-flows flows:write
What hcc deploy sends (mode: "deploy"). A Text flow is its name and version; the logic is in your code. Inbound Texts reach one bound to their number with PATCH /v1/numbers/{id}. hcc.serve sends mode: "report" on startup, which deploys nothing: it sets the flow endpoint and answers with what is deployed. A deploy never removes a flow, or unbinds a number.
Body
-
flowsobject[] requiredFields
-
namestring required -
versionstring required -
hosting"hcc" | "self"
-
-
mode"report" | "deploy"Default
"report". -
endpointUrlstring -
uploadIdstringFrom
POST /v1/hosting/uploads: the hosted code that serves this deploy's definitions withhosting: 'hcc'. Required when there are any. The firstPUTthat carries it makes it the running hosted code. -
testUploadIdstringWith
alsoTest: the upload'stest.id, which runs the test copy'shosting: 'hcc'definitions. -
alsoTestbooleanDefault
false.
Responses
- 200
The registered Text flows
Fields
-
dataTextFlow[] requiredFields
-
idstring required -
namestring required -
versionstring required -
createdAtstring required -
updatedAtstring required
-
-
- 400
Invalid request
- 401
Missing or invalid API key
- 404
Not found
- 429
rate_limited, or another limit named byerror.code