1. Did the app decide to send?
Start in your own data. Find the event that should have triggered the message and check that it exists and carries the identifier you expect. If the event never happened, or the message was skipped because the recipient was flagged or ineligible, no provider is involved. A skip with no log line is itself a finding.
- Look for the event row, the send-log row and any skip reason.
- Check the eligibility or suppression flag on the recipient.
- Do not ask the provider anything yet.
2. Did the provider accept the request?
If the app tried to send, find the response. SendGrid's send endpoint returns 202, which means only accepted; Twilio returns a message with an initial status; Slack returns an ok field or an error string. A rejected request names its reason. A request that never left the server, because a worker was down or a rate limit was hit, shows as a missing or failed attempt.
- Look for the response code and error text in your logs, without keys.
- A rate-limit response needs the wait handled, not a quick retry.
- Compare the attempt count with the retry policy.
3. What did the provider's delivery trail say?
Accepted is not delivered. For email read the events for that message: processed, deferred, delivered, bounce, blocked or dropped. For SMS read the status history and any error code, remembering that the code values may change. For Slack, the response to the post call, an ok value or an error string, is what your app can record. Use the guides for each to interpret what you find.
- Email: the bounce and drop guide.
- SMS: the status and final-state guide.
- Chat: the alerting guide.
4. Did your app record what the provider said?
If the provider reports delivered but your app shows sent, the callback was not received, was rejected, or was applied in the wrong order. Check the signature handling and the exact URL that Twilio signed, or the raw-body verification for SendGrid, and confirm the handler returns a success status only after storing the event. This step explains many cases where the provider and the app disagree.
- Check the callback address, the response code and the rejection log.
- Check that replayed and late events do not overwrite a final state.
- Test with signed requests you generate, not live customers.
5. Is the sender set up to be trusted?
Only now look at the sender. For email, read the full headers of a test message and compare the DKIM and SPF domains with the From domain. For US SMS over 10DLC numbers, Twilio says registration is required. These are account and DNS matters that you control, and they explain why delivered messages land in junk or are filtered, not why a message never left your app.
- Email authentication guide for the header check.
- Registration and consent stay with the account holder.
- Inbox placement cannot be guaranteed by any of this.
6. Choose the smallest paid step that closes the gap
If the app has no delivery record at all, the single SendGrid or Twilio outcome builds one. If records exist but nobody reads them, the monthly delivery review fits. If several channels need one dependable layer, the notification project covers email, text and a staff alert for up to five events. Prices are untested, payment follows agreed checks and sign-off, and none promises that a recipient will see a message.
Sources and limits
- SendGrid: Event Webhook reference Checked 2026-10-11.
- Delivery outcomes are reported after the send request as bounce, blocked, dropped, deferred and delivered events.
- Twilio: track outbound message status Checked 2026-10-11.
- Delivery status reaches your app through callbacks that can arrive out of order.
- Slack: Web API rate limits Checked 2026-10-11.
- Posting is limited to about one message per second per channel and a 429 carries Retry-After.