Skip to main content
500CallsDocs
Browse the docs

Message status, listing and cancelling

Statuses

StatusMeaningFinal?Charged?
scheduledAccepted with a future send_atNoHeld
queuedAccepted and waiting to be sent, or being sentNoHeld
sentA route accepted the messageNoYes
deliveredThe handset received itYesYes
undeliveredThe network could not deliver itYes, but can still become deliveredYes
expiredNo delivery receipt arrived within 72 hoursYes, but can still become delivered or undeliveredYes
rejectedRefused before any route accepted itYesNo
failedNo route could send itYesNo
cancelledCancelled before it was sentYesNo

Nothing ever leaves delivered. When a message is rejected, failed, cancelled, undelivered or expired, error.code says why; see message error codes. A message's status never gains a new value within v1.

Get one message

cURL
curl 'https://api.messaging.500calls.com/v1/messages/msg_01M3KR6CEGVQVV5PV6Q9J1G6SP' \  -H "Authorization: Bearer $API_KEY_500CALLS"
Python
import osimport requestsresponse = requests.get(    "https://api.messaging.500calls.com/v1/messages/msg_01M3KR6CEGVQVV5PV6Q9J1G6SP",    headers={        "Authorization": f"Bearer {os.environ['API_KEY_500CALLS']}",    },    timeout=30,)print(response.status_code, response.json())
Node.js
const response = await fetch("https://api.messaging.500calls.com/v1/messages/msg_01M3KR6CEGVQVV5PV6Q9J1G6SP", {  headers: {    Authorization: `Bearer ${process.env.API_KEY_500CALLS}`,  },});console.log(response.status, await response.json());
PHP
<?php$curl = curl_init('https://api.messaging.500calls.com/v1/messages/msg_01M3KR6CEGVQVV5PV6Q9J1G6SP');curl_setopt_array($curl, [    CURLOPT_HTTPHEADER => [        'Authorization: Bearer ' . getenv('API_KEY_500CALLS'),    ],    CURLOPT_RETURNTRANSFER => true,]);$body = curl_exec($curl);$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);echo $status, PHP_EOL, $body, PHP_EOL;

The response adds status_history: every status the message has had, with its time. An unknown ID, a malformed ID and another organisation's message all answer 404 not_found.

List messages

GET /v1/messages lists messages newest first, 50 to a page by default and at most 100. Filter by status and source (repeat either for several values), from, to, client_reference, campaign_id, created_after and created_before.

With neither time bound, the last 7 days are listed. One request spans at most 92 days by default, and messages are kept for 12 months. Pass the response's next_cursor back as cursor for the next page; it is null on the last page. To page without gaps or repeats while new messages arrive, send both created_after and created_before.

cURL
curl 'https://api.messaging.500calls.com/v1/messages?client_reference=order-78123&limit=10' \  -H "Authorization: Bearer $API_KEY_500CALLS"
Python
import osimport requestsresponse = requests.get(    "https://api.messaging.500calls.com/v1/messages?client_reference=order-78123&limit=10",    headers={        "Authorization": f"Bearer {os.environ['API_KEY_500CALLS']}",    },    timeout=30,)print(response.status_code, response.json())
Node.js
const response = await fetch("https://api.messaging.500calls.com/v1/messages?client_reference=order-78123&limit=10", {  headers: {    Authorization: `Bearer ${process.env.API_KEY_500CALLS}`,  },});console.log(response.status, await response.json());
PHP
<?php$curl = curl_init('https://api.messaging.500calls.com/v1/messages?client_reference=order-78123&limit=10');curl_setopt_array($curl, [    CURLOPT_HTTPHEADER => [        'Authorization: Bearer ' . getenv('API_KEY_500CALLS'),    ],    CURLOPT_RETURNTRANSFER => true,]);$body = curl_exec($curl);$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);echo $status, PHP_EOL, $body, PHP_EOL;

Cancel a message

POST /v1/messages/{id}/cancel cancels a message that is still scheduled or queued and releases its held funds, which return to your available balance within about a minute. Send no body, or an empty JSON object {}.

  • Only messages sent through the API are cancelled here; a campaign's messages are cancelled with their campaign.
  • A message that is already cancelled comes back unchanged with 200, so retrying is safe.
  • A message that is being sent, already sent, or in a final status answers 409 invalid_state_transition.
  • A send made straight after a cancel can still get 402 insufficient_balance if it needs the money the cancel frees: wait until available has risen in your balance.
cURL
curl -X POST 'https://api.messaging.500calls.com/v1/messages/msg_01M3KR6CEGVQVV5PV6Q9J1G6SP/cancel' \  -H "Authorization: Bearer $API_KEY_500CALLS" \  -H 'Content-Type: application/json' \  -H 'Idempotency-Key: order-78123-otp' \  -d '{}'
Python
import osimport requestsresponse = requests.post(    "https://api.messaging.500calls.com/v1/messages/msg_01M3KR6CEGVQVV5PV6Q9J1G6SP/cancel",    headers={        "Authorization": f"Bearer {os.environ['API_KEY_500CALLS']}",        "Idempotency-Key": "order-78123-otp",    },    json={},    timeout=30,)print(response.status_code, response.json())
Node.js
const response = await fetch("https://api.messaging.500calls.com/v1/messages/msg_01M3KR6CEGVQVV5PV6Q9J1G6SP/cancel", {  method: "POST",  headers: {    Authorization: `Bearer ${process.env.API_KEY_500CALLS}`,    "Content-Type": "application/json",    "Idempotency-Key": "order-78123-otp",  },  body: JSON.stringify({}),});console.log(response.status, await response.json());
PHP
<?php$curl = curl_init('https://api.messaging.500calls.com/v1/messages/msg_01M3KR6CEGVQVV5PV6Q9J1G6SP/cancel');curl_setopt_array($curl, [    CURLOPT_CUSTOMREQUEST => 'POST',    CURLOPT_HTTPHEADER => [        'Authorization: Bearer ' . getenv('API_KEY_500CALLS'),        'Content-Type: application/json',        'Idempotency-Key: order-78123-otp',    ],    CURLOPT_POSTFIELDS => json_encode(new stdClass()),    CURLOPT_RETURNTRANSFER => true,]);$body = curl_exec($curl);$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);echo $status, PHP_EOL, $body, PHP_EOL;

Poll or listen

Polling GET /v1/messages/{id} works, but it spends your rate limit. A webhook tells your server when a status changes instead.