Troubleshooting
This guide walks you through common issues developers hit with the EZ Texting API and how to debug them. Start with the section that matches your problem, then follow the checklist from top to bottom.
Messages not delivering
Your message sent successfully but never reached the recipient. Check these in order:
1. Check opt-out status
The most common reason messages don't deliver is that the contact has opted out. Verify before sending:
curl https://a.eztexting.com/v1/contacts/1234567890 \
-H "Authorization: Basic $(echo -n 'user:pass' | base64)"Look for "optedOut": true in the response. If they're opted out, you cannot send them messages — that's a legal requirement (CAN-SPAM, GDPR, CASL, etc.). Remove them from your list or have them re-opt-in through your UI.
2. Check credit balance
Running out of credits is the second most common issue. Even if the API accepts your request, it won't send if credits are zero:
curl https://a.eztexting.com/v1/credits \
-H "Authorization: Basic $(echo -n 'user:pass' | base64)"If totalCredits is 0, purchase more via the dashboard or use the API:
curl -X POST https://a.eztexting.com/v1/credits/purchase \
-H "Authorization: Basic $(echo -n 'user:pass' | base64)" \
-H "Content-Type: application/json" \
-d '{"credits": 1000, "creditCardLastFour": "4242"}'3. Verify phone number format
Multiple phone number formats are accepted, but if delivery is failing, double-check the number is valid and doesn't contain formatting characters like parentheses or dashes. These all work:
| Format | Status |
|---|---|
14155552671 | Works — with country code |
4155552671 | Works — 10-digit US number |
(415) 555-2671 | May cause issues — formatting characters |
4. Check rate limits
If you're sending hundreds of messages rapidly, you may hit per-second limits. Look for 429 Too Many Requests responses. Back off for 30–60 seconds, then retry. For large campaigns, use batch endpoints or space requests over 5–10 seconds.
5. Verify the number isn't a landline
SMS delivery requires a mobile carrier. Landlines don't receive text messages. There's no API to check number type upfront; carriers reject delivery at send time. If you see status: "failed" with carrier-specific error messages, that's usually a landline or invalid number.
GET /v1/message-details/{id} in the reference.Authentication failures
You're getting 401 Unauthorized or auth headers are being rejected.
1. Check credentials format (Basic Auth)
Basic Auth requires Authorization: Basic [base64-encoded "username:password"]. The colon and correct order matter:
# Correct: username:password
echo -n "myemail@example.com:mypassword" | base64
# Output: bXllbWFpbEBleGFtcGxlLmNvbTpteXBhc3N3b3Jk
# Then use:
curl https://a.eztexting.com/v1/contacts \
-H "Authorization: Basic bXllbWFpbEBleGFtcGxlLmNvbTpteXBhc3N3b3Jk"Your username can be either your email or your EZ Texting username. Your password is your account password, not an API key.
2. Base64 encoding issues
Base64 encoding looks simple but is easy to mess up. Check your output character-by-character:
- Don't include newlines. Use
echo -n(not justecho). - Verify the colon is present between username and password.
- Use an online decoder to double-check: paste the base64 string and confirm you get "username:password" back.
3. OAuth token expiration
If you're using OAuth bearer tokens, they expire after 5400 seconds (90 minutes). Once expired, you'll get 401. Refresh before expiration:
curl -X POST https://a.eztexting.com/v1/tokens/refresh \
-H "Content-Type: application/json" \
-d '{"refreshToken": "your_refresh_token_here"}'Store the new access token and use it for subsequent requests. Refresh tokens are valid for 60 days.
4. Check Authorization header format (OAuth)
For OAuth, use Bearer [token] with a space between the word "Bearer" and your token:
# Correct:
curl https://a.eztexting.com/v1/contacts \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
# Wrong (no Bearer):
curl https://a.eztexting.com/v1/contacts \
-H "Authorization: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
# Wrong (Bearer but no space):
curl https://a.eztexting.com/v1/contacts \
-H "Authorization: BearereyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."Webhook not firing
You registered a webhook but it's not being called when events happen.
1. Verify the webhook URL is HTTPS
The EZ Texting API only calls HTTPS endpoints, never HTTP. If your URL is http://example.com/webhook, it will be rejected. Change it to https://example.com/webhook.
2. Confirm your server returns 200 quickly
Your endpoint must respond with HTTP 200 OK within 10 seconds. If it's slow or returns an error, the webhook is retried a few times then disabled. Test locally:
// Node.js example
app.post("/webhook", (req, res) => {
// Do NOT do async work here — respond immediately.
res.json({ ok: true }); // Send 200 right away
// Then handle the event async (outside the response):
handleEvent(req.body).catch(err => console.error("Event error:", err));
});3. Check the event type matches your registration
Webhooks are filtered by event type. Make sure you registered for the right one:
| Event Type | When it fires |
|---|---|
inbound_text.received | An incoming SMS/MMS is received from a contact |
keyword.opt_in | A contact opts in via a keyword |
contact.created | A new contact is created in your account |
If you only registered for contact.created but are looking for incoming messages, add a webhook for inbound_text.received.
4. Verify the webhook wasn't auto-disabled
After multiple failures (e.g., your endpoint was down, returned errors), the webhook is automatically disabled. Check the webhook status in the dashboard or re-create it:
curl https://a.eztexting.com/v1/webhooks/subscriptions \
-H "Authorization: Basic $(echo -n 'user:pass' | base64)"If a webhook shows "status": "disabled", re-enable it or delete and recreate it.
Rate limit errors (429)
You're getting 429 Too Many Requests. The API has per-second limits.
Current rate limits
| Endpoint | Limit |
|---|---|
| Send message | 100 requests/second per account |
| List contacts/messages | 10 requests/second per account |
| All other endpoints | 30 requests/second per account |
How to recover from 429
When you hit the limit, the response includes a Retry-After header (in seconds). Wait that long before retrying:
const delay = (ms) => new Promise(r => setTimeout(r, ms));
async function sendWithBackoff(message, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const res = await fetch("https://a.eztexting.com/v1/messages", {
method: "POST",
headers: { Authorization: "Bearer " + token },
body: JSON.stringify(message)
});
if (res.status === 429) {
const retryAfter = parseInt(res.headers.get("Retry-After")) || 60;
console.log("Rate limited. Waiting", retryAfter, "seconds...");
await delay(retryAfter * 1000);
continue;
}
return res.json();
} catch (err) {
console.error(err);
}
}
throw new Error("Max retries exceeded");
}Batching recommendations
For sending bulk messages, don't fire 1000 requests per second. Instead, send in waves:
- Send 50 messages, wait 1 second, send 50 more. This stays well under the limit and completes faster.
- For sending to groups, use
groupIdsin your message request instead of looping through individual numbers. - For campaigns, use the campaign endpoints which handle rate limiting internally.
Contact not found
You're trying to fetch or update a contact and getting a 404 Not Found error.
1. Check phone number format
Make sure the phone number you're looking up matches the format it was stored with. If you created the contact with 14155552671, look it up the same way:
curl https://a.eztexting.com/v1/contacts/14155552671 \
-H "Authorization: Basic $(echo -n 'user:pass' | base64)"2. Verify the contact wasn't deleted
If you deleted the contact from the dashboard or via the API, it's gone and cannot be re-fetched. Create a new contact with the same phone number instead:
curl -X POST https://a.eztexting.com/v1/contacts \
-H "Authorization: Basic $(echo -n 'user:pass' | base64)" \
-H "Content-Type: application/json" \
-d '{"phoneNumber": "+14155552671", "firstName": "John", "lastName": "Doe"}'3. Check filter syntax when listing contacts
If you're searching by name or email, make sure the filter syntax is correct. Common mistakes:
# Correct — filter by first name
curl "https://a.eztexting.com/v1/contacts?firstName=John" \
-H "Authorization: Basic $(echo -n 'user:pass' | base64)"
# Correct — filter by email
curl "https://a.eztexting.com/v1/contacts?email=john@example.com" \
-H "Authorization: Basic $(echo -n 'user:pass' | base64)"
# Wrong — spaces not encoded
curl "https://a.eztexting.com/v1/contacts?firstName=John Doe" \
-H "Authorization: Basic $(echo -n 'user:pass' | base64)"
# Correct — spaces encoded
curl "https://a.eztexting.com/v1/contacts?firstName=John%20Doe" \
-H "Authorization: Basic $(echo -n 'user:pass' | base64)"Message queued but not sent
The API returned 200 OK and assigned a message ID, but the message is stuck in "queued" state and never sends.
1. Check if it's scheduled for the future
If you included a sendAt parameter (ISO 8601 timestamp), the message is scheduled and won't send until that time arrives. Verify the timestamp is in the past or remove the field to send immediately:
# Scheduled for 5 minutes from now — won't send yet:
curl -X POST https://a.eztexting.com/v1/messages \
-H "Content-Type: application/json" \
-H "Authorization: Basic ..." \
-d '{
"toNumbers": ["14155552671"],
"message": "Hello",
"sendAt": "2025-04-20T14:35:00Z"
}'
# Send immediately (no sendAt):
curl -X POST https://a.eztexting.com/v1/messages \
-H "Content-Type: application/json" \
-H "Authorization: Basic ..." \
-d '{
"toNumbers": ["14155552671"],
"message": "Hello"
}'2. Verify you have sufficient credits
Your account may have started with credits but they ran out mid-campaign. Check your balance again:
curl https://a.eztexting.com/v1/credits \
-H "Authorization: Basic $(echo -n 'user:pass' | base64)"If credits are 0, queued messages won't send. Purchase more credits to resume the queue.
3. Check for carrier filtering rules
Some carriers have content filters that hold messages for review. Messages may stay queued for minutes or hours if flagged, then either send or fail. There's no way to bypass this from the API — if the message looks like spam (all caps, many links, keywords), it may be delayed. Resend with different content or contact support.
curl https://a.eztexting.com/v1/message-details/abc123 \
-H "Authorization: Basic $(echo -n 'user:pass' | base64)"Check the status field. It will change from "queued" to "sent" or "failed" as the carrier processes it.
Still stuck?
If none of these steps help, gather the following and contact support:
- The exact API endpoint you're calling
- A sample request (with credentials redacted)
- The full response, including HTTP status code and headers
- The message ID or contact ID (if applicable)
- When the issue started (specific date/time if possible)