Skip to main content
A Message Detail Record (MDR) describes a specific message request—including its current status, cost, and metadata. Telnyx creates an MDR when a message is submitted and updates it as the message progresses through delivery.

When to Use MDRs

Delivery Tracking

Check if a message was delivered, failed, or is still in progress.

Debugging

Investigate delivery issues by examining message status and error codes.

Cost Verification

Confirm message costs after delivery for billing reconciliation.

Audit Trail

Retrieve message history for compliance and record-keeping.

Retrieve an MDR

Fetch a message record using its UUID. The UUID is returned when you send a message and is also included in webhook events.
curl -X GET "https://api.telnyx.com/v2/messages/834f3d53-8a3c-4aa0-a733-7f2d682a72df" \
  -H "Authorization: Bearer YOUR_API_KEY"

Example Response

{
  "data": {
    "record_type": "message",
    "id": "834f3d53-8a3c-4aa0-a733-7f2d682a72df",
    "direction": "outbound",
    "type": "SMS",
    "messaging_profile_id": "16fd2706-8baf-433b-82eb-8c7fada847da",
    "from": {
      "phone_number": "+18445550001",
      "carrier": "Telnyx",
      "line_type": "VoIP"
    },
    "to": [
      {
        "phone_number": "+18665550002",
        "status": "delivered",
        "updated_at": "2019-01-23T18:10:02.574Z"
      }
    ],
    "text": "Hello, World!",
    "webhook_url": "https://www.example.com/hooks",
    "webhook_failover_url": "https://www.example.com/hooks-backup",
    "use_profile_webhooks": false,
    "encoding": "GSM-7",
    "parts": 1,
    "cost": {
      "amount": "0.0050",
      "currency": "USD"
    },
    "errors": [],
    "created_at": "2019-01-23T18:10:00.000Z",
    "updated_at": "2019-01-23T18:10:02.574Z",
    "valid_until": "2019-01-23T18:25:00.000Z"
  }
}

MDR Schema

FieldTypeDescription
idUUIDUnique identifier for the message request
directionstringinbound or outbound
typestringMessage type: SMS, MMS, or RCS
messaging_profile_idUUIDThe messaging profile used to send/receive
fromobjectSender details including phone_number, carrier, line_type
toarrayRecipients with phone_number, status, updated_at
textstringMessage body content
media_urlsarrayMedia attachment URLs (MMS only)
encodingstringCharacter encoding: GSM-7 or UCS-2
partsintegerNumber of message segments
costobjectamount and currency (may be null until finalized)
errorsarrayError details if delivery failed
webhook_urlstringURL for delivery status webhooks
webhook_failover_urlstringBackup webhook URL
use_profile_webhooksbooleanWhether to use profile-level webhooks
created_atISO 8601When the message was submitted
updated_atISO 8601Last status update time
valid_untilISO 8601Expiration time for pending messages
Cost may be null: When retrieved immediately after sending, cost may be null because pricing is calculated asynchronously. The final cost appears in the message.finalized webhook event.

Message Status

The status field in the to array indicates where the message is in its lifecycle.

Outbound Status Flow

Outbound Statuses

StatusDescriptionFinal?
queuedMessage accepted and queued for sendingNo
sentDelivered to carrier gatewayNo
deliveredCarrier confirmed delivery to handset✓ Yes
failedDelivery failed (see errors array)✓ Yes
gw_timeoutNo response from gateway✓ Yes
dlr_timeoutNo delivery receipt from carrier✓ Yes
Track delivery with webhooks: Rather than polling for status, configure a webhook URL to receive real-time status updates as message.sent, message.delivered, or message.finalized events.

Inbound Statuses

StatusDescription
receivedMessage received by Telnyx
deliveredMessage delivered to your webhook

Common Error Codes

When a message fails, the errors array contains details:
{
  "errors": [
    {
      "code": "40301",
      "title": "Destination number blocked",
      "detail": "The recipient has opted out of messages from this sender"
    }
  ]
}
Error CodeDescriptionResolution
40300Invalid destinationVerify the phone number format
40301Destination blockedRecipient has opted out—remove from list
40310Carrier rejectedMessage content may have triggered spam filters
40311UndeliverableNumber is unreachable (landline, disconnected)
40400Sender not registeredRegister for 10DLC or toll-free verification
40500Rate limit exceededSlow down sending or request higher limits
See the Error Codes Reference for the complete list.

Best Practices

Polling the MDR endpoint is inefficient and can hit rate limits. Instead, configure webhooks on your Messaging Profile to receive real-time updates:
  • message.sent — Message accepted by carrier
  • message.delivered — Confirmed delivery
  • message.finalized — Final status with cost
{
  "webhook_url": "https://your-app.com/webhooks/messaging",
  "webhook_failover_url": "https://your-app.com/webhooks/messaging-backup"
}
Save the id returned when you send a message. This UUID is required to retrieve the MDR later:
const response = await client.messages.send({
  from: '+15551234567',
  to: '+15559876543',
  text: 'Hello!'
});

// Store this for later tracking
const messageId = response.data.id;
The cost field is populated asynchronously. For accurate billing:
  1. Wait for the message.finalized webhook, OR
  2. Retrieve the MDR after a few seconds
// Cost may not be available immediately
if (message.cost === null) {
  // Wait for message.finalized webhook or retry later
}

Troubleshooting

Possible causes:
  • Invalid message ID format
  • Message ID from a different account
  • Message was never created (request was rejected at validation)
Solution: Verify the UUID format and check that the message send request returned a 201 status. Rejected requests don’t create MDRs.
Possible causes:
  • Message is rate-limited and waiting in queue
  • Gateway connection issue
Solution: Wait a few minutes. If still queued after 5 minutes, check system status for outages. Messages stuck beyond valid_until will fail.
Cause: Cost is calculated asynchronously after the message is sent.Solution: Either wait for the message.finalized webhook, or retrieve the MDR again after 5-10 seconds.

Next Steps