# Send Your First WhatsApp Message

This tutorial walks you through sending a WhatsApp template message with parameter substitution, from authentication to handling the response.

## What you'll build

By the end of this tutorial, you'll send a WhatsApp notification message using the unified endpoint, with body parameters and optional SMS fallback.

## Prerequisites

- eCommunicate account with an API key
- A registered WhatsApp sender number
- An approved WhatsApp template with at least one body parameter


## Step 1: Set up authentication

Every API request requires your Developer Key in the `Authorization` header. Retrieve it using your account credentials, then store it securely in an environment variable.

cURL
```bash
# Retrieve your API key using your account credentials
curl -s -X POST \
  "https://api.payless4messaging.com/payless4messaging-service/WhatsApp/authenticate/retrieveApiKey" \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com", "password": "your-password", "clientRef": "CLIENT001"}'

# Save the returned apiKey as an environment variable
export ECOMM_API_KEY="your-api-key-uuid"
```

Node.js
```javascript
const BASE_URL = 'https://api.payless4messaging.com';

// Retrieve your API key using account credentials
const authResponse = await fetch(
  `${BASE_URL}/payless4messaging-service/WhatsApp/authenticate/retrieveApiKey`,
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      email: 'user@example.com',
      password: 'your-password',
      clientRef: 'CLIENT001',
    }),
  }
);
const { apiKey: API_KEY } = await authResponse.json();
console.log('Authenticated successfully');

// Reusable helper for authenticated requests
async function apiRequest(method, path, body) {
  const response = await fetch(`${BASE_URL}${path}`, {
    method,
    headers: {
      'Authorization': API_KEY,
      'Content-Type': 'application/json',
    },
    body: body ? JSON.stringify(body) : undefined,
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`API Error ${response.status}: ${error.message}`);
  }

  return response.json();
}
```

Python
```python
import os
import requests

BASE_URL = "https://api.payless4messaging.com"

# Retrieve your API key using account credentials
auth_response = requests.post(
    f"{BASE_URL}/payless4messaging-service/WhatsApp/authenticate/retrieveApiKey",
    json={
        "email": "user@example.com",
        "password": "your-password",
        "clientRef": "CLIENT001",
    },
)
API_KEY = auth_response.json()["apiKey"]
print("Authenticated successfully")

def api_request(method, path, json_body=None):
    """Reusable helper for authenticated requests."""
    response = requests.request(
        method,
        f"{BASE_URL}{path}",
        headers={
            "Authorization": API_KEY,
            "Content-Type": "application/json",
        },
        json=json_body,
    )
    response.raise_for_status()
    return response.json()
```

Java
```java
import java.net.URI;
import java.net.http.*;
import java.net.http.HttpResponse.BodyHandlers;

String BASE_URL = "https://api.payless4messaging.com";
HttpClient client = HttpClient.newHttpClient();

// Retrieve your API key using account credentials
HttpRequest authRequest = HttpRequest.newBuilder()
    .uri(URI.create(BASE_URL + "/payless4messaging-service/WhatsApp/authenticate/retrieveApiKey"))
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(
        "{\"email\":\"user@example.com\",\"password\":\"your-password\",\"clientRef\":\"CLIENT001\"}"))
    .build();
HttpResponse<String> authResponse = client.send(authRequest, BodyHandlers.ofString());
// Parse apiKey from response JSON
String API_KEY = /* extract apiKey from authResponse.body() */;

HttpResponse<String> apiRequest(String method, String path, String body) throws Exception {
    HttpRequest.Builder builder = HttpRequest.newBuilder()
        .uri(URI.create(BASE_URL + path))
        .header("Authorization", API_KEY)
        .header("Content-Type", "application/json");

    HttpRequest request = method.equals("GET")
        ? builder.GET().build()
        : builder.POST(HttpRequest.BodyPublishers.ofString(body)).build();

    return client.send(request, BodyHandlers.ofString());
}
```

## Step 2: Build the message payload

Construct the message with your template name, recipient, and body parameters.

cURL
```bash
cat <<'EOF' > message.json
{
  "channel": "WHATSAPP",
  "payload": {
    "sender": "+27123456789",
    "templateName": "seasonal_promo_text",
    "templateId": "your-template-uuid",
    "messageType": "NOTIFICATION",
    "messages": [
      {
        "recipients": [
          { "to": "+27987654321", "customerUserId": "ZA.1021033720668862", "ctId": "customer-tracking-001" }
        ],
        "components": [
          {
            "type": "body",
            "parameters": ["John"]
          }
        ]
      }
    ],
    "fallback": {
      "sendFallback": true,
      "unicode": false
    }
  }
}
EOF
```

Node.js
```javascript
const messagePayload = {
  channel: 'WHATSAPP',
  payload: {
    sender: '+27123456789',
    templateName: 'seasonal_promo_text',
    templateId: 'your-template-uuid',
    messageType: 'NOTIFICATION',
    messages: [
      {
        recipients: [
          { to: '+27987654321', ctId: 'customer-tracking-001' },
        ],
        components: [
          { type: 'body', parameters: ['John'] },
        ],
      },
    ],
    fallback: {
      sendFallback: true,
      unicode: false,
    },
  },
};
```

Python
```python
message_payload = {
    "channel": "WHATSAPP",
    "payload": {
        "sender": "+27123456789",
        "templateName": "seasonal_promo_text",
        "templateId": "your-template-uuid",
        "messageType": "NOTIFICATION",
        "messages": [
            {
                "recipients": [
                    {"to": "+27987654321", "ctId": "customer-tracking-001"}
                ],
                "components": [
                    {"type": "body", "parameters": ["John"]},
                ],
            }
        ],
        "fallback": {
            "sendFallback": True,
            "unicode": False,
        },
    },
}
```

Java
```java
String messagePayload = """
    {
      "channel": "WHATSAPP",
      "payload": {
        "sender": "+27123456789",
        "templateName": "seasonal_promo_text",
        "templateId": "your-template-uuid",
        "messageType": "NOTIFICATION",
        "messages": [
          {
            "recipients": [
              { "to": "+27987654321", "ctId": "customer-tracking-001" }
            ],
            "components": [
              { "type": "body", "parameters": ["John"] }
            ]
          }
        ],
        "fallback": {
          "sendFallback": true,
          "unicode": false
        }
      }
    }
    """;
```

### Key fields explained

| Field | Purpose |
|  --- | --- |
| `channel` | `"WHATSAPP"` for WhatsApp, `"SMS"` for direct SMS |
| `sender` | Your registered WhatsApp number with country code |
| `templateName` | The approved template name (lowercase, underscores) |
| `templateId` | UUID of the template from Template Management |
| `messageType` | `"NOTIFICATION"` for templates, `"CHAT"` for session messages |
| `recipients[].to` | Recipient phone number in E.164 format |
| `recipients[].customerUserId` | Recipient BSUID (e.g. `ZA.1021033720668862`) — a durable, business-scoped id you can send to instead of, or alongside, `to`. When both are present, `to` takes precedence |
| `recipients[].ctId` | Your internal tracking ID (optional but recommended) |
| `components[].parameters` | Values substituted into `{{1}}`, `{{2}}`, etc. |
| `fallback.sendFallback` | `true` to auto-send SMS if recipient isn't on WhatsApp |


## Step 3: Send the message

cURL
```bash
curl -X POST \
  "https://api.payless4messaging.com/whatsapp-api-service/v2/messages" \
  -H "Authorization: $ECOMM_API_KEY" \
  -H "Content-Type: application/json" \
  -d @message.json
```

Node.js
```javascript
const result = await apiRequest(
  'POST',
  '/whatsapp-api-service/v2/messages',
  messagePayload
);

console.log(`Status: ${result.status}`);
console.log(`Request ID: ${result.requestId}`);
console.log(`Channel: ${result.channel}`);
```

Python
```python
result = api_request(
    "POST",
    "/whatsapp-api-service/v2/messages",
    message_payload,
)

print(f"Status: {result['status']}")
print(f"Request ID: {result['requestId']}")
print(f"Channel: {result['channel']}")
```

Java
```java
HttpResponse<String> response = apiRequest(
    "POST",
    "/whatsapp-api-service/v2/messages",
    messagePayload
);

System.out.println("Status code: " + response.statusCode());
System.out.println("Body: " + response.body());
```

## Step 4: Handle the response

A successful send returns **202 Accepted**:

```json
{
  "status": "ACCEPTED",
  "message": "Request accepted",
  "requestId": "c3a1f8e2-4b6d-11ee-be56-0242ac120002",
  "channel": "WHATSAPP",
  "timestamp": "2026-01-15T10:30:00Z",
  "response": "Request Accepted for Processing"
}
```

Asynchronous processing
`202 Accepted` means the message is **queued**, not delivered. Track delivery via [Webhooks](/guides/webhooks) or the Message Reports API.

### Check delivery status

```bash
curl -s -X GET \
  "https://api.payless4messaging.com/whatsapp-api-service/message/status/gstid?gstid=tracking-uuid" \
  -H "Authorization: $ECOMM_API_KEY"
```

**Response:**

```json
{
  "latestMessageStatus": "DELIVERED",
  "statusTimestamp": "2026-01-15T10:30:05Z",
  "ctId": "customer-tracking-001",
  "gstId": "tracking-uuid"
}
```

**Delivery lifecycle:** `ACCEPTED` > `SENT` > `DELIVERED` > `READ` > `PLAYED` (or `FAILED`)

## Next steps

| Tutorial | What you'll build |
|  --- | --- |
| [Set Up Webhooks](/tutorials/setup-webhooks) | Receive delivery receipts automatically |
| [WhatsApp Messaging Guide](/guides/whatsapp-messaging) | Media templates, carousels, and buttons |
| [Conversations Guide](/guides/conversations) | Long-lived conversations beyond 24 hours |