> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.teekrr.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.teekrr.com/_mcp/server.

# Validate a recipient list (dry run)

POST https://api.teekrr.com/broadcasts/validate
Content-Type: application/json

Runs the same recipient validator the send endpoints use and returns per-row
diagnostics - **without** reserving credit, inserting messages, or enqueueing
anything. Use it to pre-flight a large recipient list before committing to a
broadcast.

**Required scope:** none beyond a valid API key.
**Rate limit:** 10 requests / 15 min / client

The `invalid[].code` values are the same ones the send endpoints return in
their `Recipient validation failed` response.


Reference: https://docs.teekrr.com/api-reference/broadcasts/validate-broadcast

## Authentication

- `Authorization` header (bearer token, required) — Bearer API keys are issued from the in-app `/api-management` page. Each key has a permission scope (`send_sms`, `send_whatsapp`, `send_email`) and an optional IP whitelist. The IP whitelist, when set, is matched by **exact string equality** against the resolved caller IP - CIDR ranges are not supported. An empty whitelist allows any source address.

## Request

### Body (application/json)

- `channel` (enum, required)
  - Allowed values: `sms`, `whatsapp`, `email`
- `recipients` (list of string, optional)
- `recipientVariables` (list of object, optional)
  - `msisdn` (string, optional) — For the sms / whatsapp channels
  - `email` (string, optional) — For the email channel
  - `variables` (map from string to any, optional)
  - `line` (integer, optional) — Source line number, echoed back in diagnostics.

## Response

### 200

Validation result

- `data` (object, optional)
  - `summary` (object, required)
    - `total` (integer, required)
    - `valid` (integer, required)
    - `invalid` (integer, required)
  - `valid` (list of object, required)
    - `row` (integer, required)
    - `value` (string, required) — The normalised recipient
    - `variables` (map from string to any, optional)
  - `invalid` (list of object, required)
    - `row` (integer, required) — 1-based index within the submitted list
    - `line` (integer, required) — Source line number
    - `field` (enum, required)
      - Allowed values: `msisdn`, `email`
    - `value` (string, required) — The rejected value
    - `code` (enum, required) — - `INVALID_MSISDN` - not a valid phone number after normalisation - `INVALID_EMAIL` - not a valid email address - `MISSING_VALUE` - the recipient field was empty - `DUPLICATE` - the same recipient appears on an earlier row (duplicates are rejected, not de-duplicated) - `MISSING_VARIABLE` - the template declares a variable this row does not supply - `UNKNOWN_VARIABLE` - the row supplies a variable the template does not declare
      - Allowed values: `INVALID_MSISDN`, `INVALID_EMAIL`, `MISSING_VALUE`, `DUPLICATE`, `MISSING_VARIABLE`, `UNKNOWN_VARIABLE`
    - `message` (string, required)

## Examples

### Validate SMS recipients

**Request**

```json
{
  "channel": "sms",
  "recipients": [
    "60123456789",
    "0123456789",
    "not-a-number"
  ]
}
```

**Response**

```json
{
  "data": {
    "summary": {
      "total": 3,
      "valid": 1,
      "invalid": 2
    },
    "valid": [
      {
        "row": 1,
        "value": "+60123456789"
      }
    ],
    "invalid": [
      {
        "row": 2,
        "line": 2,
        "field": "msisdn",
        "value": "0123456789",
        "code": "DUPLICATE",
        "message": "Duplicate recipient (also on row 1)"
      },
      {
        "row": 3,
        "line": 3,
        "field": "msisdn",
        "value": "not-a-number",
        "code": "INVALID_MSISDN",
        "message": "Must be a valid phone number (E.164 or Malaysia 01X format)"
      }
    ]
  }
}
```

**SDK Code**

```python Validate SMS recipients
import requests

url = "https://api.teekrr.com/broadcasts/validate"

payload = {
    "channel": "sms",
    "recipients": ["60123456789", "0123456789", "not-a-number"]
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript Validate SMS recipients
const url = 'https://api.teekrr.com/broadcasts/validate';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"channel":"sms","recipients":["60123456789","0123456789","not-a-number"]}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Validate SMS recipients
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.teekrr.com/broadcasts/validate"

	payload := strings.NewReader("{\n  \"channel\": \"sms\",\n  \"recipients\": [\n    \"60123456789\",\n    \"0123456789\",\n    \"not-a-number\"\n  ]\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Validate SMS recipients
require 'uri'
require 'net/http'

url = URI("https://api.teekrr.com/broadcasts/validate")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"channel\": \"sms\",\n  \"recipients\": [\n    \"60123456789\",\n    \"0123456789\",\n    \"not-a-number\"\n  ]\n}"

response = http.request(request)
puts response.read_body
```

```java Validate SMS recipients
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.teekrr.com/broadcasts/validate")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"channel\": \"sms\",\n  \"recipients\": [\n    \"60123456789\",\n    \"0123456789\",\n    \"not-a-number\"\n  ]\n}")
  .asString();
```

```php Validate SMS recipients
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.teekrr.com/broadcasts/validate', [
  'body' => '{
  "channel": "sms",
  "recipients": [
    "60123456789",
    "0123456789",
    "not-a-number"
  ]
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp Validate SMS recipients
using RestSharp;

var client = new RestClient("https://api.teekrr.com/broadcasts/validate");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"channel\": \"sms\",\n  \"recipients\": [\n    \"60123456789\",\n    \"0123456789\",\n    \"not-a-number\"\n  ]\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift Validate SMS recipients
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "channel": "sms",
  "recipients": ["60123456789", "0123456789", "not-a-number"]
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.teekrr.com/broadcasts/validate")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### Validate email recipients with variables

**Request**

```json
{
  "channel": "email",
  "recipientVariables": [
    {
      "email": "ali@example.com",
      "variables": {
        "name": "Ali"
      }
    },
    {
      "email": "broken@",
      "variables": {
        "name": "Fatimah"
      }
    }
  ]
}
```

**Response**

```json
{
  "data": {
    "summary": {
      "total": 2,
      "valid": 1,
      "invalid": 1
    },
    "valid": [
      {
        "row": 1,
        "value": "ali@example.com",
        "variables": {
          "name": "Ali"
        }
      }
    ],
    "invalid": [
      {
        "row": 2,
        "line": 2,
        "field": "email",
        "value": "broken@",
        "code": "INVALID_EMAIL",
        "message": "Invalid email format"
      }
    ]
  }
}
```

**SDK Code**

```python Validate email recipients with variables
import requests

url = "https://api.teekrr.com/broadcasts/validate"

payload = {
    "channel": "email",
    "recipientVariables": [
        {
            "email": "ali@example.com",
            "variables": { "name": "Ali" }
        },
        {
            "email": "broken@",
            "variables": { "name": "Fatimah" }
        }
    ]
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript Validate email recipients with variables
const url = 'https://api.teekrr.com/broadcasts/validate';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"channel":"email","recipientVariables":[{"email":"ali@example.com","variables":{"name":"Ali"}},{"email":"broken@","variables":{"name":"Fatimah"}}]}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Validate email recipients with variables
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.teekrr.com/broadcasts/validate"

	payload := strings.NewReader("{\n  \"channel\": \"email\",\n  \"recipientVariables\": [\n    {\n      \"email\": \"ali@example.com\",\n      \"variables\": {\n        \"name\": \"Ali\"\n      }\n    },\n    {\n      \"email\": \"broken@\",\n      \"variables\": {\n        \"name\": \"Fatimah\"\n      }\n    }\n  ]\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Validate email recipients with variables
require 'uri'
require 'net/http'

url = URI("https://api.teekrr.com/broadcasts/validate")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"channel\": \"email\",\n  \"recipientVariables\": [\n    {\n      \"email\": \"ali@example.com\",\n      \"variables\": {\n        \"name\": \"Ali\"\n      }\n    },\n    {\n      \"email\": \"broken@\",\n      \"variables\": {\n        \"name\": \"Fatimah\"\n      }\n    }\n  ]\n}"

response = http.request(request)
puts response.read_body
```

```java Validate email recipients with variables
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.teekrr.com/broadcasts/validate")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"channel\": \"email\",\n  \"recipientVariables\": [\n    {\n      \"email\": \"ali@example.com\",\n      \"variables\": {\n        \"name\": \"Ali\"\n      }\n    },\n    {\n      \"email\": \"broken@\",\n      \"variables\": {\n        \"name\": \"Fatimah\"\n      }\n    }\n  ]\n}")
  .asString();
```

```php Validate email recipients with variables
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.teekrr.com/broadcasts/validate', [
  'body' => '{
  "channel": "email",
  "recipientVariables": [
    {
      "email": "ali@example.com",
      "variables": {
        "name": "Ali"
      }
    },
    {
      "email": "broken@",
      "variables": {
        "name": "Fatimah"
      }
    }
  ]
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp Validate email recipients with variables
using RestSharp;

var client = new RestClient("https://api.teekrr.com/broadcasts/validate");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"channel\": \"email\",\n  \"recipientVariables\": [\n    {\n      \"email\": \"ali@example.com\",\n      \"variables\": {\n        \"name\": \"Ali\"\n      }\n    },\n    {\n      \"email\": \"broken@\",\n      \"variables\": {\n        \"name\": \"Fatimah\"\n      }\n    }\n  ]\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift Validate email recipients with variables
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "channel": "email",
  "recipientVariables": [
    [
      "email": "ali@example.com",
      "variables": ["name": "Ali"]
    ],
    [
      "email": "broken@",
      "variables": ["name": "Fatimah"]
    ]
  ]
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.teekrr.com/broadcasts/validate")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```