Request SMS opt-in confirmation
POST/api/v1/contacts/{id}/sms-opt-in-request
POST /contacts/{id}/sms-opt-in-request
curl -X POST 'https://www.unitpost.com/api/v1/contacts/con_123/sms-opt-in-request' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'User-Agent: my-app/1.0' \
-H 'Content-Type: application/json' \
-d '{ }'Response · 200
{
"object": "sms",
"id": "id_123",
"to": "customer@example.com",
"from": "you@yourdomain.com",
"direction": "outbound",
"status": "queued",
"type": "transactional",
"body": "string",
"segments": 0,
"in_reply_to": "string",
"received_at": "2026-01-01T00:00:00.000Z",
"looks_like_opt_out": false,
"country": "string",
"scheduled_at": "2026-01-01T00:00:00.000Z",
"last_error": "string",
"error": {
"code": "out_of_reach",
"message": "string"
},
"created_at": "2026-01-01T00:00:00.000Z"
}Text the contact "Reply YES to get our marketing texts" (double opt-in). A YES within 72 hours records a verified marketing opt-in; until then their existing consent stands. It only upgrades consent already on record: a number with none, one that opted out, or one already waiting for a reply is a 409 and nothing is sent. It sends a real text from a number they can reply to and charges your wallet, so pass an Idempotency-Key: a retry with the same key returns the same text instead of sending another. phone picks which of the contact's numbers (E.164); default its primary one. Any other body key is a 422. Requires the sms:send capability.
Parameters2 fields
| Field | Type | Description |
|---|---|---|
idrequired | string · path | — |
Idempotency-Key | string · header | Any unique string (1–256 characters, e.g. a UUID). Retrying with the same key and the same body within 24 hours returns the first result instead of sending again; the same key with a different body is `409 invalid_idempotent_request`, and one still in flight is `409 concurrent_idempotent_requests`. |
Request body1 field
| Field | Type | Description |
|---|---|---|
phone | string | Which of the contact's numbers to text (E.164). Default: its primary number. |
Response · 200 fields17 fields
| Field | Type | Description |
|---|---|---|
objectrequired | string | — |
idrequired | string | — |
torequired | string | — |
fromrequired | string | null | — |
directionrequired | enumoutbound | inbound | — |
statusrequired | enumqueued | scheduled | sending | sent | delivered | failed | suppressed | canceled | received | — |
typerequired | enumtransactional | marketing | — |
bodyrequired | string | null | — |
segmentsrequired | integer | Billed units. Outbound: segments. Inbound: 1 when the received text was charged (1¢ in the US and Canada), else 0. |
in_reply_torequired | string | null | Inbound only: your text this reply answers (`sms_…`), when the carrier says which. Null otherwise. |
received_atrequired | string | null | Inbound only: when the reply reached Unitpost. Order a conversation by this; carriers don't guarantee order. Null on outbound. |
looks_like_opt_outrequired | boolean | Inbound only: the text reads like a request to stop texting ("please stop texting me") but was not an opt-out keyword, so no opt-out was recorded. Decide whether to record one (POST /v1/contacts/{id}/sms-consent, action opt_out). False for an exact keyword such as STOP (already recorded) and on outbound. |
countryrequired | string | null | — |
scheduled_atrequired | string | null | — |
last_errorrequired | string | null | Legacy free-text error. Prefer `error.code`. |
errorrequired | object | null | Set on `suppressed` messages: the stable skip reason and its sentence. Null otherwise. |
created_atrequired | string | — |
200400401403404409422