Message Response Handling

Complete guide to understanding and handling API responses when sending messages.

Response Structure

All API responses follow a consistent structure:

{
  "success": boolean,
  "data": object | null,
  "error": object | null,
  "meta": {
    "request_id": string,
    "timestamp": string,
    "version": "v3"
  }
}
FieldTypeDescription
successbooleantrue for 2xx responses, false for errors
dataobject | nullResponse payload (null on error)
errorobject | nullError details (null on success)
metaobjectRequest metadata including request_id for support

Success Response (202 Accepted)

When a message is successfully accepted:

{
  "success": true,
  "data": {
    "status": "QUEUED",
    "template_id": "7ba7b820-9dad-11d1-80b4-00c04fd430c8",
    "template_name": "order_confirmation",
    "recipients": [
      {
        "message_id": "8ba7b830-9dad-11d1-80b4-00c04fd430c8",
        "to": "+14155551234",
        "channel": "sms"
      }
    ]
  },
  "error": null,
  "meta": {
    "request_id": "req_7X9zKp2jDw",
    "timestamp": "2026-03-04T11:28:25.2096416+00:00",
    "version": "v3"
  }
}
const response = await client.messages.send({
  to: ['+1234567890'],
  template: { id: 'tmpl_123' }
});

// Access response data
console.log(response.data.status);      // "QUEUED"
console.log(response.data.template_id); // Template UUID
console.log(response.data.template_name);

// Access recipient info
const recipient = response.data.recipients[0];
console.log(recipient.message_id);      // Message UUID for tracking
console.log(recipient.to);              // Phone number
console.log(recipient.channel);         // "sms" or "whatsapp"

// Access metadata
console.log(response.meta.request_id);  // For support inquiries
console.log(response.meta.timestamp);   // Response timestamp
response = client.messages.send(
    to=["+1234567890"],
    template={"id": "tmpl_123"}
)

# Access response data
print(response.data.status)        # "QUEUED"
print(response.data.template_id)   # Template UUID
print(response.data.template_name)

# Access recipient info
recipient = response.data.recipients[0]
print(recipient.message_id)        # Message UUID for tracking
print(recipient.to)                # Phone number
print(recipient.channel)           # "sms" or "whatsapp"

# Access metadata
print(response.meta.request_id)    # For support inquiries
print(response.meta.timestamp)     # Response timestamp
response, err := client.Messages.Send(context.Background(), params)
if err != nil {
    log.Fatal(err)
}

// Access response data
fmt.Println(response.Data.Status)      // "QUEUED"
fmt.Println(response.Data.TemplateID)  // Template UUID
fmt.Println(response.Data.TemplateName)

// Access recipient info
recipient := response.Data.Recipients[0]
fmt.Println(recipient.MessageID)       // Message UUID for tracking
fmt.Println(recipient.To)              // Phone number
fmt.Println(recipient.Channel)         // "sms" or "whatsapp"

// Access metadata
fmt.Println(response.Meta.RequestID)   // For support inquiries
fmt.Println(response.Meta.Timestamp)   // Response timestamp
var response = client.messages().send(params);

// Access response data
System.out.println(response.data().status());       // "QUEUED"
System.out.println(response.data().templateId());   // Template UUID
System.out.println(response.data().templateName());

// Access recipient info
var recipient = response.data().recipients().get(0);
System.out.println(recipient.messageId());          // Message UUID for tracking
System.out.println(recipient.to());                 // Phone number
System.out.println(recipient.channel());            // "sms" or "whatsapp"

// Access metadata
System.out.println(response.meta().requestId());    // For support inquiries
System.out.println(response.meta().timestamp());    // Response timestamp
var response = await client.Messages.SendAsync(parameters);

// Access response data
Console.WriteLine(response.Data.Status);      // "QUEUED"
Console.WriteLine(response.Data.TemplateId);  // Template UUID
Console.WriteLine(response.Data.TemplateName);

// Access recipient info
var recipient = response.Data.Recipients[0];
Console.WriteLine(recipient.MessageId);       // Message UUID for tracking
Console.WriteLine(recipient.To);              // Phone number
Console.WriteLine(recipient.Channel);         // "sms" or "whatsapp"

// Access metadata
Console.WriteLine(response.Meta.RequestId);   // For support inquiries
Console.WriteLine(response.Meta.Timestamp);   // Response timestamp
$result = $client->messages->send(
    to: ['+1234567890'],
    template: ['id' => 'tmpl_123']
);

// Access response data
echo $result->data->status;        // "QUEUED"
echo $result->data->template_id;   // Template UUID
echo $result->data->template_name;

// Access recipient info
$recipient = $result->data->recipients[0];
echo $recipient->message_id;       // Message UUID for tracking
echo $recipient->to;               // Phone number
echo $recipient->channel;          // "sms" or "whatsapp"

// Access metadata
echo $result->meta->request_id;    // For support inquiries
echo $result->meta->timestamp;     // Response timestamp
result = sent_dm.messages.send(
  to: ["+1234567890"],
  template: { id: "tmpl_123" }
)

# Access response data
puts result.data.status        # "QUEUED"
puts result.data.template_id   # Template UUID
puts result.data.template_name

# Access recipient info
recipient = result.data.recipients[0]
puts recipient.message_id      # Message UUID for tracking
puts recipient.to              # Phone number
puts recipient.channel         # "sms" or "whatsapp"

# Access metadata
puts result.meta.request_id    # For support inquiries
puts result.meta.timestamp     # Response timestamp

Multi-Channel Response

When sending to multiple channels, you get one recipient entry per (recipient, channel) pair:

{
  "success": true,
  "data": {
    "status": "QUEUED",
    "template_id": "7ba7b820-9dad-11d1-80b4-00c04fd430c8",
    "template_name": "order_confirmation",
    "recipients": [
      {
        "message_id": "msg-uuid-1",
        "to": "+14155551234",
        "channel": "whatsapp"
      },
      {
        "message_id": "msg-uuid-2",
        "to": "+14155551234",
        "channel": "sms"
      }
    ]
  },
  "error": null,
  "meta": {
    "request_id": "req_7X9zKp2jDw",
    "timestamp": "2026-03-04T11:28:25.2096416+00:00",
    "version": "v3"
  }
}

Error Responses

400 Bad Request (Validation Error)

{
  "success": false,
  "data": null,
  "error": {
    "code": "VALIDATION_004",
    "message": "Request validation failed",
    "details": {
      "to": ["'to' must contain at least one recipient"],
      "template": ["'template' is required"]
    },
    "doc_url": "https://docs.sent.dm/errors/VALIDATION_004"
  },
  "meta": {
    "request_id": "req_7X9zKp2jDw",
    "timestamp": "2026-03-04T11:28:25.2096416+00:00",
    "version": "v3"
  }
}
import { ValidationError } from '@sentdm/sentdm';

try {
  const response = await client.messages.send({
    to: [],  // Empty array - will fail validation
    template: { id: 'tmpl_123' }
  });
} catch (error) {
  if (error instanceof ValidationError) {
    console.log(error.code);        // "VALIDATION_004"
    console.log(error.message);     // "Request validation failed"
    console.log(error.details);     // { to: [...], template: [...] }
    console.log(error.docUrl);      // Documentation URL
  }
}
from sent_dm.errors import ValidationError

try:
    response = client.messages.send(
        to=[],  # Empty list - will fail validation
        template={"id": "tmpl_123"}
    )
except ValidationError as e:
    print(e.code)        # "VALIDATION_004"
    print(e.message)     # "Request validation failed"
    print(e.details)     # {"to": [...], "template": [...]}
    print(e.doc_url)     # Documentation URL
response, err := client.Messages.Send(context.Background(), params)
if err != nil {
    if validationErr, ok := err.(*sentdm.ValidationError); ok {
        fmt.Println(validationErr.Code)     // "VALIDATION_004"
        fmt.Println(validationErr.Message)  // "Request validation failed"
        fmt.Println(validationErr.Details)  // Field-specific errors
        fmt.Println(validationErr.DocURL)   // Documentation URL
    }
}
try {
    var response = client.messages().send(params);
} catch (ValidationException e) {
    System.out.println(e.getCode());        // "VALIDATION_004"
    System.out.println(e.getMessage());     // "Request validation failed"
    System.out.println(e.getDetails());     // Field-specific errors
    System.out.println(e.getDocUrl());      // Documentation URL
}
try
{
    var response = await client.Messages.SendAsync(parameters);
}
catch (ValidationException ex)
{
    Console.WriteLine(ex.Code);        // "VALIDATION_004"
    Console.WriteLine(ex.Message);     // "Request validation failed"
    Console.WriteLine(ex.Details);     // Field-specific errors
    Console.WriteLine(ex.DocUrl);      // Documentation URL
}
use SentDM\Exceptions\ValidationException;

try {
    $result = $client->messages->send(to: [], template: ['id' => 'tmpl_123']);
} catch (ValidationException $e) {
    echo $e->getCode();        // "VALIDATION_004"
    echo $e->getMessage();     // "Request validation failed"
    print_r($e->getDetails()); // Field-specific errors
    echo $e->getDocUrl();      // Documentation URL
}
begin
  result = sent_dm.messages.send(to: [], template: { id: "tmpl_123" })
rescue Sentdm::ValidationError => e
  puts e.code        # "VALIDATION_004"
  puts e.message     # "Request validation failed"
  puts e.details     # Field-specific errors
  puts e.doc_url     # Documentation URL
end

401 Unauthorized

Invalid or missing API key:

{
  "success": false,
  "data": null,
  "error": {
    "code": "AUTH_001",
    "message": "Invalid API key",
    "details": null,
    "doc_url": "https://docs.sent.dm/errors/AUTH_001"
  }
}

404 Not Found

Template not found:

{
  "success": false,
  "data": null,
  "error": {
    "code": "RESOURCE_002",
    "message": "Template not found",
    "details": null,
    "doc_url": "https://docs.sent.dm/errors/RESOURCE_002"
  }
}

429 Rate Limit

Too many requests:

{
  "success": false,
  "data": null,
  "error": {
    "code": "BUSINESS_002",
    "message": "Rate limit exceeded. Retry after 60 seconds",
    "details": {
      "retry_after": 60,
      "limit": 200,
      "window": "60s"
    },
    "doc_url": "https://docs.sent.dm/errors/BUSINESS_002"
  }
}

402 Payment Required

Insufficient balance:

{
  "success": false,
  "data": null,
  "error": {
    "code": "BUSINESS_003",
    "message": "Account balance is insufficient to send this message",
    "details": {
      "balance": 0.50,
      "required": 0.75
    },
    "doc_url": "https://docs.sent.dm/errors/BUSINESS_003"
  }
}

Error Code Reference

CodeHTTPDescriptionAction
VALIDATION_002400Invalid phone number formatCheck E.164 format
VALIDATION_004400Missing required fieldsCheck request body
AUTH_001401Invalid API keyVerify API key
RESOURCE_002404Template not foundCheck template ID
RESOURCE_003404Contact not foundCheck contact ID
BUSINESS_002429Rate limit exceededImplement backoff
BUSINESS_003402Insufficient balanceAdd credits
BUSINESS_005400Template not approvedCheck template status
INTERNAL_001500Server errorRetry with backoff

Handling Errors in Production

import { SentDm, ValidationError, RateLimitError, BusinessError } from '@sentdm/sentdm';

const client = new SentDm();

async function sendWithRetry(params: MessageSendParams, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await client.messages.send(params);
    } catch (error) {
      // Don't retry validation errors
      if (error instanceof ValidationError) {
        throw error;
      }

      // Handle rate limits with exponential backoff
      if (error instanceof RateLimitError) {
        const delay = Math.pow(2, attempt) * 1000;
        await sleep(delay);
        continue;
      }

      // Handle insufficient balance
      if (error instanceof BusinessError && error.code === 'BUSINESS_003') {
        await alertBillingTeam();
        throw error;
      }

      // Last attempt - throw error
      if (attempt === maxRetries) {
        throw error;
      }
    }
  }
}
import time
from sent_dm import SentDm
from sent_dm.errors import ValidationError, RateLimitError, BusinessError

client = SentDm()

def send_with_retry(params, max_retries=3):
    for attempt in range(1, max_retries + 1):
        try:
            return client.messages.send(**params)
        except ValidationError:
            # Don't retry validation errors
            raise
        except RateLimitError as e:
            # Handle rate limits with exponential backoff
            if attempt < max_retries:
                delay = 2 ** attempt
                time.sleep(delay)
                continue
            raise
        except BusinessError as e:
            if e.code == 'BUSINESS_003':
                alert_billing_team()
            raise
import (
    "context"
    "fmt"
    "time"
    "github.com/sentdm/sent-dm-go"
)

func sendWithRetry(ctx context.Context, client *sentdm.Client, params sentdm.MessageSendParams, maxRetries int) (*sentdm.MessageResponse, error) {
    for attempt := 1; attempt <= maxRetries; attempt++ {
        response, err := client.Messages.Send(ctx, params)
        if err == nil {
            return response, nil
        }

        // Don't retry validation errors
        if _, ok := err.(*sentdm.ValidationError); ok {
            return nil, err
        }

        // Handle rate limits with exponential backoff
        if _, ok := err.(*sentdm.RateLimitError); ok {
            if attempt < maxRetries {
                delay := time.Duration(1<<attempt) * time.Second
                time.Sleep(delay)
                continue
            }
        }

        // Handle insufficient balance
        if businessErr, ok := err.(*sentdm.BusinessError); ok && businessErr.Code == "BUSINESS_003" {
            alertBillingTeam()
        }

        return nil, err
    }
    return nil, fmt.Errorf("max retries exceeded")
}
import dm.sent.client.SentDmClient;
import dm.sent.errors.*;

public class MessageSender {
    private final SentDmClient client;

    public MessageSendResponse sendWithRetry(MessageSendParams params, int maxRetries) {
        for (int attempt = 1; attempt <= maxRetries; attempt++) {
            try {
                return client.messages().send(params);
            } catch (ValidationException e) {
                // Don't retry validation errors
                throw e;
            } catch (RateLimitException e) {
                // Handle rate limits with exponential backoff
                if (attempt < maxRetries) {
                    try {
                        Thread.sleep((long) Math.pow(2, attempt) * 1000);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        throw new RuntimeException(ie);
                    }
                    continue;
                }
                throw e;
            } catch (BusinessException e) {
                if ("BUSINESS_003".equals(e.getCode())) {
                    alertBillingTeam();
                }
                throw e;
            }
        }
        throw new RuntimeException("Max retries exceeded");
    }
}
using Sentdm;
using Sentdm.Errors;

public class MessageSender
{
    private readonly SentDmClient _client;

    public async Task<MessageResponse> SendWithRetryAsync(
        MessageSendParams parameters,
        int maxRetries = 3)
    {
        for (int attempt = 1; attempt <= maxRetries; attempt++)
        {
            try
            {
                return await _client.Messages.SendAsync(parameters);
            }
            catch (ValidationException)
            {
                // Don't retry validation errors
                throw;
            }
            catch (RateLimitException)
            {
                // Handle rate limits with exponential backoff
                if (attempt < maxRetries)
                {
                    var delay = TimeSpan.FromSeconds(Math.Pow(2, attempt));
                    await Task.Delay(delay);
                    continue;
                }
                throw;
            }
            catch (BusinessException ex) when (ex.Code == "BUSINESS_003")
            {
                await AlertBillingTeamAsync();
                throw;
            }
        }
        throw new Exception("Max retries exceeded");
    }
}
use SentDM\Client;
use SentDM\Exceptions\ValidationException;
use SentDM\Exceptions\RateLimitException;
use SentDM\Exceptions\BusinessException;

class MessageSender
{
    private Client $client;

    public function sendWithRetry(array $params, int $maxRetries = 3)
    {
        for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
            try {
                return $this->client->messages->send(...$params);
            } catch (ValidationException $e) {
                // Don't retry validation errors
                throw $e;
            } catch (RateLimitException $e) {
                // Handle rate limits with exponential backoff
                if ($attempt < $maxRetries) {
                    sleep(2 ** $attempt);
                    continue;
                }
                throw $e;
            } catch (BusinessException $e) {
                if ($e->getCode() === 'BUSINESS_003') {
                    $this->alertBillingTeam();
                }
                throw $e;
            }
        }
        throw new Exception('Max retries exceeded');
    }
}
class MessageSender
  def initialize(client)
    @client = client
  end

  def send_with_retry(params, max_retries: 3)
    max_retries.times do |attempt|
      begin
        return @client.messages.send(**params)
      rescue Sentdm::ValidationError
        # Don't retry validation errors
        raise
      rescue Sentdm::RateLimitError
        # Handle rate limits with exponential backoff
        if attempt < max_retries - 1
          sleep(2 ** (attempt + 1))
          retry
        end
        raise
      rescue Sentdm::BusinessError => e
        if e.code == 'BUSINESS_003'
          alert_billing_team
        end
        raise
      end
    end
    raise 'Max retries exceeded'
  end
end

On this page