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:

FormatStatus
14155552671Works — with country code
4155552671Works — 10-digit US number
(415) 555-2671May 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.

Tip:Use the message details endpoint to check a message's delivery status after sending. See 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 just echo).
  • 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 TypeWhen it fires
inbound_text.receivedAn incoming SMS/MMS is received from a contact
keyword.opt_inA contact opts in via a keyword
contact.createdA 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

EndpointLimit
Send message100 requests/second per account
List contacts/messages10 requests/second per account
All other endpoints30 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 groupIds in 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.

Pro tip: Use the message status endpoint to monitor queued messages:
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)