# Authentication

Every request to the eCommunicate API requires a **Developer API key** in the `Authorization` header.

## How it works

The API uses a custom `apiKey` scheme. Pass your key directly in the `Authorization` header — no `Bearer` prefix, no Basic auth.

cURL
```bash
curl -X GET "https://api.payless4messaging.com/your-endpoint" \
  -H "Authorization: a1b2c3d4-e5f6-7890-abcd-ef1234567890"
```

Node.js
```javascript
const API_KEY = process.env.ECOMM_API_KEY;

const response = await fetch('https://api.payless4messaging.com/your-endpoint', {
  headers: { 'Authorization': API_KEY },
});
```

Python
```python
import os
import requests

API_KEY = os.environ["ECOMM_API_KEY"]

response = requests.get(
    "https://api.payless4messaging.com/your-endpoint",
    headers={"Authorization": API_KEY},
)
```

Java
```java
String API_KEY = System.getenv("ECOMM_API_KEY");

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.payless4messaging.com/your-endpoint"))
    .header("Authorization", API_KEY)
    .GET()
    .build();
```

## API key format

Your API key is a standard UUID:

```
a1b2c3d4-e5f6-7890-abcd-ef1234567890
```

## Retrieve your API key

Use this endpoint to obtain your API key using your account credentials. **No existing API key is required** — this is the recommended starting point for new integrations.

```
POST /payless4messaging-service/WhatsApp/authenticate/retrieveApiKey
```

**Request body:**

```json
{
  "email": "user@example.com",
  "password": "your-password",
  "clientRef": "CLIENT001"
}
```

**Response:**

```json
{
  "apiKey": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
```

cURL
```bash
curl -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"}'
```

Node.js
```javascript
const response = await fetch(
  'https://api.payless4messaging.com/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 } = await response.json();
console.log('Your API key:', apiKey);
```

Python
```python
import requests

response = requests.post(
    "https://api.payless4messaging.com/payless4messaging-service/WhatsApp/authenticate/retrieveApiKey",
    json={
        "email": "user@example.com",
        "password": "your-password",
        "clientRef": "CLIENT001",
    },
)

api_key = response.json()["apiKey"]
print("Your API key:", api_key)
```

Error response
If the credentials are invalid, the endpoint returns `401` with `{"error": "Invalid credentials"}`. Double-check your email, password, and client reference.

## Generate a new key

```
POST /payless4messaging-service/WhatsApp/SystemUsers/generateAPIKey
```

**Response:**

```json
{
  "token": "f6e5d4c3-b2a1-0987-6543-210fedcba987"
}
```

Immediate invalidation
Generating a new key **immediately invalidates** the previous one. All integrations using the old key will return `401 Unauthorized`. Update all your services before or immediately after regeneration.

## Error responses

| Status | Message | Cause | Resolution |
|  --- | --- | --- | --- |
| `401` | `Unauthorized user` | Missing or invalid API key | Verify your key — see [Troubleshooting a 401](#troubleshooting-a-401) |
| `403` | `Forbidden` | Valid key but insufficient permissions | Contact support to verify your account permissions |


## Troubleshooting a 401

A `401 Unauthorized` means the key the server received is not one it recognises. Before changing any
code, **confirm which key your account actually has** — there are two ways to check, and they should
agree.

### 1. Check the key in the web UI

Sign in to the eCommunicate web UI and open your API key settings. This shows the key currently active
on your account, which is the value your integration must send.

### 2. Check the key via the API

Call the [retrieve endpoint](#retrieve-your-api-key) with your account credentials. It requires **no
existing API key**, so it still works when every other call is returning `401`:

cURL
```bash
curl -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"}'
```

Java
```java
String body = """
    {"email":"user@example.com","password":"your-password","clientRef":"CLIENT001"}
    """;

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.payless4messaging.com"
        + "/payless4messaging-service/WhatsApp/authenticate/retrieveApiKey"))
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(body))
    .build();

HttpResponse<String> response = HttpClient.newHttpClient()
    .send(request, HttpResponse.BodyHandlers.ofString());

// 200 → the body carries the current apiKey
// 401 → the credentials themselves are wrong, not the API key
System.out.println(response.statusCode() + " " + response.body());
```

If this call itself returns `401` with `{"error": "Invalid credentials"}`, the problem is your email,
password or `clientRef` — not your API key.

### 3. Compare against what you're sending

Once you know the correct key, check the value your integration puts in the header. A valid key is a
**36-character UUID**; any other length means it was truncated or padded between your secret store and
the request.

Java
```java
String apiKey = System.getenv("ECOMM_API_KEY");

if (apiKey == null || apiKey.isBlank()) {
    throw new IllegalStateException("ECOMM_API_KEY is not set");
}
apiKey = apiKey.trim();   // strip trailing newlines from files and shell exports

// Safe to log — never log the full key
logger.info("API key length={} suffix=...{}",
    apiKey.length(), apiKey.substring(apiKey.length() - 4));
```

Node.js
```javascript
const apiKey = (process.env.ECOMM_API_KEY ?? '').trim();

if (!apiKey) throw new Error('ECOMM_API_KEY is not set');

// Safe to log — never log the full key
console.log(`API key length=${apiKey.length} suffix=...${apiKey.slice(-4)}`);
```

Python
```python
api_key = os.environ.get("ECOMM_API_KEY", "").strip()

if not api_key:
    raise RuntimeError("ECOMM_API_KEY is not set")

# Safe to log — never log the full key
print(f"API key length={len(api_key)} suffix=...{api_key[-4:]}")
```

### Common causes

| Cause | Fix |
|  --- | --- |
| `Authorization` header missing | Add it to **every** request — there is no session or cookie fallback |
| `Bearer` prefix added | Pass the key on its own; this API uses a custom `apiKey` scheme |
| Key was regenerated elsewhere | Retrieve the current key — [regeneration permanently invalidates the old one](#generate-a-new-key) |
| Key belongs to a different client | Retrieve the key for the `clientRef` you're integrating against |
| Whitespace or newline in the value | Trim it before sending |
| Key copied from a truncated log | Compare against the UI — a valid key is 36 characters |


401 vs 403
`401` means the key wasn't recognised — verify and fix the key. `403` means the key **was** recognised
but your account isn't permitted to perform that operation — retrieving the key again won't help.

Still stuck after both checks agree? Contact
[support@ecommunicate.co.za](mailto:support@ecommunicate.co.za) with the `requestId` from the error
response. See [Error Handling](/guides/error-handling#401-unauthorized) for the full response format.

## Security best practices

Protect your API key
Your API key grants full access to your eCommunicate account. Treat it like a password.

### Store keys securely

Never hardcode your API key. Use environment variables or a secrets manager:

Environment variable
```bash
# .env (never commit this file)
ECOMM_API_KEY=a1b2c3d4-e5f6-7890-abcd-ef1234567890

# Access in your code
# Node.js: process.env.ECOMM_API_KEY
# Python:  os.environ["ECOMM_API_KEY"]
# Java:    System.getenv("ECOMM_API_KEY")
```

AWS Secrets Manager
```javascript
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';

const client = new SecretsManagerClient({ region: 'af-south-1' });
const { SecretString } = await client.send(
  new GetSecretValueCommand({ SecretId: 'ecomm/api-key' })
);
const API_KEY = JSON.parse(SecretString).ECOMM_API_KEY;
```

### Key rules

| Rule | Detail |
|  --- | --- |
| **Server-side only** | Make API calls from your backend, never from browsers or mobile apps |
| **HTTPS always** | All eCommunicate endpoints require HTTPS — never downgrade to HTTP |
| **Rotate periodically** | Use the generate endpoint to rotate keys on a regular schedule |
| **Separate environments** | Use different keys for staging and production where possible |
| **Monitor usage** | Check your Analytics dashboard for unexpected activity |
| **Restrict access** | Use the User Management endpoints to control who can access your account |
| **Never log keys** | Ensure your logging framework redacts the `Authorization` header |