> ## Documentation Index
> Fetch the complete documentation index at: https://docs.awaaz.de/llms.txt
> Use this file to discover all available pages before exploring further.

# XFin

## Overview

To align with industry standards, Awaaz.De uses REST APIs with simple and flexible architecture leading to easier implementation and integration. Partners need to integrate two APIs:

1. **Login API**: JWT-based authentication that allows partners to make a login request once and send multiple message requests using the same token. Partners only need to request login when the token expires. JWT access tokens are valid for 1 hour.
2. **Message API**: Used to schedule automated voice call messages to customers.

## Demo Code

Awaaz.De's API integration demo code is available on GitHub and explains how to authenticate via the Login API, obtain a token, and send automated voice calls using the Message API.

Demo code is available in:

* [Java](https://github.com/awaazde)
* [PHP](https://github.com/awaazde)
* [Python](https://github.com/awaazde)

***

## Login API

* Contact our team to set up your account.
* After account creation, you’ll receive key details like the tenant name, which you’ll need to include in every API request.

### Endpoint

```text theme={null}
POST https://api.awaaz.de/{tenant}/v1/account/login
```

### Request Body

```json theme={null}
{
  "email": "user@example.com",
  "password": "string"
}
```

### Request Parameters

| Parameter  | Type   | Description             |
| ---------- | ------ | ----------------------- |
| `email`    | string | User email for login    |
| `password` | string | User password for login |

### Success Response

**Status Code**: `200 OK`

```json theme={null}
{
  "token": "<JWT token which needs to be passed with Message API>",
  "user": {
    // Other user details
  }
}
```

The JWT token returned is active for 1 hour. This token must be passed with the Message API to successfully authenticate message requests.

### Error Response

**Status Code**: `401 Unauthorized`

```json theme={null}
{
  "non_field_errors": [
    "Unable to log in with provided credentials."
  ]
}
```

This error indicates an issue with the provided credentials.

***

## Message API

### Endpoint

```text theme={null}
POST https://api.awaaz.de/{tenant}/v1/xact/message/create_bulk
```

### Authentication

Include the JWT token obtained from the Login API in the request headers:

```text theme={null}
Authorization: Bearer <your-jwt-token>
```

### Request Body

```json theme={null}
{
  "transform_using_template": true,
  "data": [
    {
      "phone_number": "9099830084",
      "language": "Oriya",
      "day_of_month": "5",
      "installment_amount": "100",
      "month": "may",
      "template": "Payment Confirmation-Late Payers-short",
      "number_of_days": "2",
      "tags": ["clientID", "LAN", "group ID", "state", "branchname"]
    },
    {
      "phone_number": "9805830084",
      "language": "Oriya",
      "day_of_month": "5",
      "installment_amount": "100",
      "month": "may",
      "template": "Payment Confirmation-Late Payers-short",
      "number_of_days": "2",
      "tags": ["clientID", "LAN", "group ID", "state", "branchname"]
    }
  ]
}
```

> **Note**: Request data should be customized based on your template and language requirements.

### Request Parameters

| Parameter                  | Type    | Required | Description                                                                                                 |
| -------------------------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------- |
| `transform_using_template` | boolean | Yes      | Enable template-based transformation                                                                        |
| `data`                     | array   | Yes      | Array of message objects to be scheduled                                                                    |
| `instant_message`          | boolean | No       | Indicates whether the message should be sent immediately. You can schedule up to 5 messages with this flag. |

Following parameters are part of data field:

| Parameter      | Type   | Required | Description                                                |
| -------------- | ------ | -------- | ---------------------------------------------------------- |
| `phone_number` | string | Yes      | Phone number in international format (e.g., +9194XXXXXXXX) |
| `language`     | string | Yes      | Language to be used for the voice call                     |
| `template`     | string | Yes      | Template name (or ID) indicating the script for the call   |
| `tags`         | array  | No       | List of tags or additional metadata for the message        |

Additional parameters like `day_of_month`, `installment_amount`, `month`, and `number_of_days` are template-specific and should be included based on your template requirements.

### Rate Limits

* **5,000 messages** per request: approximately 30 seconds to process
* **10,000 messages** per request: approximately 45-50 seconds to process

### Success Response

**Status Code**: `201 Created`

```json theme={null}
[
  {
    "phone_number": "9099830084",
    "status": "created",
    "language": "Oriya",
    "templatelanguage": 87,
    "day_of_month": "5",
    "installment_amount": 100,
    "month": 5,
    "template": "Payment Confirmation-Late Payers-short",
    "number_of_days": 2,
    "id": 39937283
  },
  {
    "phone_number": "9805830084",
    "status": "created",
    "language": "Oriya",
    "templatelanguage": 87,
    "day_of_month": "5",
    "installment_amount": 100,
    "month": 5,
    "template": "Payment Confirmation-Late Payers-short",
    "number_of_days": 2,
    "id": 39937284
  }
]
```

All messages with `"status": "created"` have been successfully scheduled.

### Error Response

**Status Code**: `201 Created` (partial success)

```json theme={null}
[
  {
    "phone_number": "9805830084",
    "status": "created",
    "language": "Oriya",
    "templatelanguage": 87,
    "day_of_month": "5",
    "installment_amount": 100,
    "month": 5,
    "template": "Payment Confirmation-Late Payers-short",
    "number_of_days": 2,
    "id": 39937287
  },
  {
    "phone_number": "909930084",
    "status": "invalid",
    "language": "Oriya",
    "templatelanguage": 87,
    "day_of_month": "5",
    "installment_amount": 100,
    "month": 5,
    "template": "Payment Confirmation-Late Payers-short",
    "error": "phone_number: Invalid input provided",
    "number_of_days": 2
  }
]
```

Messages with `"status": "invalid"` contain errors. The `error` field provides details about what went wrong.

***

## Example Request and Response

### Request

```json theme={null}
{
  "transform_using_template": true,
  "data": [
    {
      "phone_number": "9099830084",
      "language": "Oriya",
      "day_of_month": "5",
      "installment_amount": "100",
      "month": "may",
      "template": "Payment Confirmation-Late Payers-short",
      "number_of_days": "2",
      "tags": ["clientID", "LAN", "group ID", "state", "branchname"]
    },
    {
      "phone_number": "1234",
      "language": "Oriya",
      "day_of_month": "5",
      "installment_amount": "100",
      "month": "may",
      "template": "Payment Confirmation-Late Payers-short",
      "number_of_days": "2",
      "tags": ["clientID", "LAN", "group ID", "state", "branchname"]
    }
  ]
}
```

### Response

```json theme={null}
[
  {
    "phone_number": "9099830084",
    "language": "Oriya",
    "day_of_month": "5",
    "installment_amount": "100",
    "month": "may",
    "template": "Payment Confirmation-Late Payers-short",
    "number_of_days": "2",
    "tags": ["clientID", "LAN", "group ID", "state", "branchname"],
    "templatelanguage": 87,
    "id": 39,
    "status": "created"
  },
  {
    "phone_number": "1234",
    "language": "Oriya",
    "day_of_month": "5",
    "installment_amount": "100",
    "month": "may",
    "template": "Payment Confirmation-Late Payers-short",
    "number_of_days": "2",
    "tags": ["clientID", "LAN", "group ID", "state", "branchname"],
    "templatelanguage": 87,
    "error": "phone_number: Invalid input provided",
    "status": "invalid"
  }
]
```

The first message was successfully created (status: `created`), while the second message failed due to an invalid phone number (status: `invalid`).

***

## GET - Message API

### 1. List Messages

Retrieve a paginated list of messages, with optional filters.

**Endpoint**

```text theme={null}
GET https://api.awaaz.de/{tenant}/v1/xact/message/
```

**Query Parameters**

| Parameter                | Type     | Required | Description                                                                                                                                                           |
| ------------------------ | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                     | integer  | No       | Exact match, or range: `id__gt`, `id__lt`, `id__gte`, `id__lte`, `id__in`                                                                                             |
| `send_on`                | datetime | No       | Range filter: `send_on__gt`, `send_on__lt`, `send_on__gte`, `send_on__lte`. Format: `YYYY-MM-DDTHH:MM:SS`                                                             |
| `templatelanguage`       | integer  | No       | Filter by template language ID                                                                                                                                        |
| `recipient_phone_number` | string   | No       | Filter by a single phone number, e.g. `+918887776666`                                                                                                                 |
| `tags`                   | string   | No       | Comma-separated, OR-matched, e.g. `tags=tag1,tag2`                                                                                                                    |
| `page`                   | integer  | No       | Page number to retrieve. Defaults to `1`.                                                                                                                             |
| `page_size_param`        | integer  | No       | Override the default page size. Defaults to `100`.                                                                                                                    |
| `include_transcription`  | boolean  | No       | Set to `true` to populate `call_transcription` on each attempt. Omitted or any other value leaves it `null`. See [Optional Attempt Fields](#optional-attempt-fields). |
| `include_audio_url`      | boolean  | No       | Set to `true` to populate `audio_url` on each attempt. Omitted or any other value leaves it `null`. See [Optional Attempt Fields](#optional-attempt-fields).          |

If no filters are applied, all messages are returned. Results are ordered newest first (`-created`) by default.

**Example Request**

```bash theme={null}
curl --location 'https://api.awaaz.de/{tenant}/v1/xact/message/?templatelanguage=5&send_on__gt=2024-01-01T00:00:00' \
-H 'Authorization: Bearer <your-jwt-token>'
```

**Example Response**

**Status Code**: `200 OK`

```json theme={null}
{
  "count": 523,
  "next": "https://api.awaaz.de/{tenant}/v1/xact/message/?page=2",
  "previous": null,
  "results": [
    {
      "id": 10002,
      "phone_number": "+918000219012",
      "contact": 5245,
      "templatelanguage": 5,
      "values": ["dynamic1", "dynamic2"],
      "send_on": "2026-05-28 12:13:15.215215+00:00",
      "send_by": "2026-05-28 18:30:00+00:00",
      "state": "Completed",
      "tags": ["overdue_reminder"],
      "owner": "Jane Doe",
      "created": "2026-05-28T17:43:15.432422+05:30",
      "modified": "2026-05-29T16:20:06.902181+05:30",
      "attempts": [
        {
          "id": 26718,
          "delivery_status": "Reached",
          "duration": "00:00:50",
          "execution_time": "2026-05-28T18:18:26.702796+05:30",
          "response": null,
          "created": "2026-05-28T18:17:31.735126+05:30",
          "entities": {
            "intent": "will_pay",
            "promise_date": "2026-06-05"
          },
          "audio_url": null,
          "call_transcription": null
        }
      ],
      "execution_state_reason_code": null,
      "child_values": {}
    }
  ]
}
```

> `audio_url` and `call_transcription` are `null` above because the request did not opt in to them. See [Optional Attempt Fields](#optional-attempt-fields).

**Error Responses**

| Status Code       | Body                                            | Cause                                                            |
| ----------------- | ----------------------------------------------- | ---------------------------------------------------------------- |
| `400 Bad Request` | `{"id": ["Enter a number."]}`                   | An integer-typed filter (e.g. `id`) received a non-numeric value |
| `400 Bad Request` | `{"send_on__gt": ["Enter a valid date/time."]}` | A datetime-typed filter received an unparseable value            |
| `404 Not Found`   | `{"detail": "Invalid page."}`                   | The requested `page` is beyond the available range               |

***

### 2. Retrieve a Single Message

Fetch full details for one message by its ID. Returns a flat JSON object - no pagination wrapper.

**Endpoint**

```text theme={null}
GET https://api.awaaz.de/{tenant}/v1/xact/message/{id}/
```

**Path Parameters**

| Parameter | Type    | Required | Description                       |
| --------- | ------- | -------- | --------------------------------- |
| `id`      | integer | Yes      | The ID of the message to retrieve |

**Query Parameters**

| Parameter               | Type    | Required | Description                                                                                                              |
| ----------------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------ |
| `include_transcription` | boolean | No       | Set to `true` to populate `call_transcription` on each attempt. See [Optional Attempt Fields](#optional-attempt-fields). |
| `include_audio_url`     | boolean | No       | Set to `true` to populate `audio_url` on each attempt. See [Optional Attempt Fields](#optional-attempt-fields).          |

Filter and pagination parameters do not apply to this endpoint, but the two opt-in parameters above are accepted here exactly as they are on the list endpoint.

**Example Request**

```bash theme={null}
curl --location 'https://api.awaaz.de/{tenant}/v1/xact/message/10002/?include_transcription=true&include_audio_url=true' \
-H 'Authorization: Bearer <your-jwt-token>'
```

**Example Response**

**Status Code**: `200 OK`

```json theme={null}
{
  "id": 10002,
  "phone_number": "+918000219012",
  "contact": 5245,
  "templatelanguage": 5,
  "values": ["dynamic1", "dynamic2"],
  "send_on": "2026-05-28 12:13:15.215215+00:00",
  "send_by": "2026-05-28 18:30:00+00:00",
  "state": "Completed",
  "tags": ["overdue_reminder"],
  "owner": "Jane Doe",
  "created": "2026-05-28T17:43:15.432422+05:30",
  "modified": "2026-05-29T16:20:06.902181+05:30",
  "attempts": [
    {
      "id": 26718,
      "delivery_status": "Reached",
      "duration": "00:00:50",
      "execution_time": "2026-05-28T18:18:26.702796+05:30",
      "response": null,
      "created": "2026-05-28T18:17:31.735126+05:30",
      "entities": {
        "intent": "will_pay",
        "promise_date": "2026-06-05"
      },
      "audio_url": "Audio URL will be available within 24 hours of the call time.",
      "call_transcription": [
        { "role": "assistant", "message": "Hello, am I speaking with Rahul?" },
        { "role": "user", "message": "Yes, speaking." },
        { "role": "assistant", "message": "This is a reminder about your pending EMI. Can you pay by the 5th?" },
        { "role": "user", "message": "Yes, I will pay by the 5th." }
      ]
    }
  ],
  "execution_state_reason_code": null,
  "child_values": {}
}
```

> Use this endpoint to poll for status updates on a specific message - see [Message States & Polling Guide](#message-states--polling-guide) below. Poll this endpoint rather than the list endpoint, since list results can shift across pages as new messages are created.

**Error Responses**

| Status Code     | Body                                                | Cause                                                                           |
| --------------- | --------------------------------------------------- | ------------------------------------------------------------------------------- |
| `404 Not Found` | `{"detail": "No Message matches the given query."}` | The `id` is a valid integer, but no message with that ID exists for this tenant |
| `404 Not Found` | `{"detail": "Not found."}`                          | The `id` in the URL is not a valid integer                                      |

***

### Response Fields Reference

| Field                         | Type             | Description                                                                                                               |
| ----------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `id`                          | integer          | Message ID                                                                                                                |
| `phone_number`                | string           | Recipient phone number                                                                                                    |
| `contact`                     | integer          | Contact ID                                                                                                                |
| `templatelanguage`            | integer          | Template language ID                                                                                                      |
| `values`                      | list             | Ordered list of dynamic values used to populate the template's variable placeholders, in positional order                 |
| `send_on`                     | datetime         | Scheduled send time                                                                                                       |
| `send_by`                     | datetime \| null | Deadline for delivery, `null` if not set                                                                                  |
| `state`                       | string           | Current message state - see [Message States & Polling Guide](#message-states--polling-guide)                              |
| `tags`                        | list             | Free-form list of strings attached to the message. Can be empty `[]`.                                                     |
| `owner`                       | string           | Name of the user who created the message                                                                                  |
| `created`                     | datetime         | Creation timestamp                                                                                                        |
| `modified`                    | datetime         | Last modified timestamp                                                                                                   |
| `attempts`                    | list             | List of attempt objects (see below), ordered by `execution_time` ascending - oldest first                                 |
| `execution_state_reason_code` | string \| null   | Failure reason if applicable - see [Execution State Reason Codes](#execution-state-reason-codes). Otherwise `null`.       |
| `child_values`                | object \| null   | Values for any child/follow-up messages spawned from this one. May be `null` or an empty object `{}` when not applicable. |

> **Note**: `send_on` and `send_by` are returned in a different timestamp format (e.g. `2026-05-28 12:13:15.215215+00:00`, space-separated) than `created`, `modified`, and the nested `attempts` timestamps (e.g. `2026-05-29T16:20:06.902181+05:30`, ISO 8601 with a `T` separator and local offset). Parse each accordingly.

#### Execution State Reason Codes

| Code                                | Meaning                           |
| ----------------------------------- | --------------------------------- |
| `insufficient_asset_bal`            | Insufficient asset balance        |
| `insufficient_template_bal`         | Insufficient template balance     |
| `can_not_convert_content`           | Inconvertible audio content       |
| `content_not_converted`             | Audio content not converted       |
| `static_content_deleted_or_renamed` | Static content deleted or renamed |
| `manually_stopped`                  | Manually stopped                  |
| `exceeded_send_by`                  | Exceeded send\_by datetime        |

> **Note**: this field currently returns the human-readable **Meaning** text shown above (e.g. `"Exceeded send_by datetime"`), not the snake\_case **Code**. Match against the Meaning column when checking this field's value.

**Attempt Object Fields**

| Field                | Type            | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| -------------------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                 | integer         | Attempt ID                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `delivery_status`    | string          | `Reached` - the call was answered. `Not Reached` - the call was attempted but not answered. `Not Reachable` - the number could not be dialed at all (e.g. DND-registered, invalid number).                                                                                                                                                                                                                                                                                                            |
| `duration`           | string          | Call duration, e.g. `00:00:50`                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `execution_time`     | datetime        | When the attempt was executed                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `created`            | datetime        | When the attempt record was created                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `response`           | integer \| null | Touchtone digit response, if applicable                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `entities`           | object \| null  | CAI attempts only: parsed NLU entities extracted during the call (e.g. `{"intent": "will_pay", "promise_date": "2026-06-05"}`). Populated from the last listen interaction that captured entities. `null` for IVR attempts, for CAI attempts where no entities were captured, or when the call did not reach a listen node.                                                                                                                                                                           |
| `audio_url`          | string \| null  | CAI attempts only, **opt-in**: pre-signed S3 URL to the full call recording (`.wav`), valid for 1 hour from the time of the API response. Requires `?include_audio_url=true`. `null` for IVR attempts, for CAI attempts where no recording exists, and whenever the parameter was not passed. If the recording has not yet been uploaded to S3, returns the literal string `"Audio URL will be available within 24 hours of the call time."`, so retry the endpoint after the pending window elapses. |
| `call_transcription` | list \| null    | CAI attempts only, **opt-in**: the conversation as an ordered list of `{"role": ..., "message": ...}` objects, oldest first. `role` is either `user` (the person on the call) or `assistant` (the AI agent). Requires `?include_transcription=true`. `null` for IVR attempts and whenever the parameter was not passed. An empty list `[]` means a CAI attempt that produced no conversation, for example a call that was not answered.                                                               |

> **Note on `entities`, `audio_url` and `call_transcription`**: all three are only meaningful for CAI (Cloud AI conversational) templates and will always be `null` for IVR. For CAI calls, entities are typically populated within seconds of call completion, while the audio URL depends on a nightly upload cron on the FreeSWITCH slave, so `audio_url` may show the pending message for up to 24h after the call before becoming a valid signed URL.

#### Optional Attempt Fields

`audio_url` and `call_transcription` are **off by default**. Each is returned as `null` unless the request explicitly opts in.

| Field                | Parameter                    |
| -------------------- | ---------------------------- |
| `call_transcription` | `include_transcription=true` |
| `audio_url`          | `include_audio_url=true`     |

Both parameters are accepted on the list endpoint and the retrieve endpoint alike, and they are independent, so passing one does not enable the other.

Only the exact value `true` enables a field, compared case-insensitively, so `true`, `True` and `TRUE` all work. Any other value, including `1`, `yes` and `false`, leaves the field `null`.

```bash theme={null}
# Transcripts only
curl --location 'https://api.awaaz.de/{tenant}/v1/xact/message/?include_transcription=true' \
-H 'Authorization: Bearer <your-jwt-token>'

# Both, on a single message
curl --location 'https://api.awaaz.de/{tenant}/v1/xact/message/10002/?include_transcription=true&include_audio_url=true' \
-H 'Authorization: Bearer <your-jwt-token>'
```

Both keys are **always present** in the response. A `null` value means either the attempt is not a CAI attempt or the request did not opt in.

***

### Message States & Polling Guide

| State         | Meaning                                          | Keep Polling?         |
| ------------- | ------------------------------------------------ | --------------------- |
| `Active`      | Created, awaiting scheduling                     | Yes                   |
| `Staging`     | Flows and recipients being generated             | Yes                   |
| `In Progress` | Attempts are actively being executed             | Yes                   |
| `Completed`   | All attempts finished                            | See below             |
| `Stopped`     | Manually stopped, or `send_by` deadline exceeded | **No - stop polling** |
| `Deleted`     | Soft-deleted                                     | **No - stop polling** |

**When to poll**

* **Before `send_on`**: no need to poll. The message will not transition to `In Progress` until its scheduled send time.
* **First attempt**: the first attempt is placed shortly after `send_on` and should be reflected in the API response **at least 30–35 minutes after `send_on`**. Do not expect the `attempts` array to be populated before then.
* **Backup / retry attempts**: if the first attempt does not result in `Reached`, backup attempts are placed later. The gap between attempts depends on the **backup duration configured on the template** (e.g. 1 hour, 3 hours) - poll at roughly that cadence rather than continuously. Each new attempt (with its own `delivery_status`, `entities`, `audio_url`) will appear in the `attempts` array as it runs. Continue polling until `state` becomes `Completed` or `Stopped`.
* **After `Completed`**: for IVR attempts, no further changes will occur - stop polling. For CAI attempts, `entities` are typically already present, but `audio_url` may still show the pending message. In that case, poll **once more after 24 hours** past the last attempt's `execution_time` to fetch the signed URL, then stop.

> `Completed` means all attempts finished executing - it does not mean the call was answered. Check the `attempts` array's `delivery_status` values to determine the actual outcome.

> If a message is `Stopped` with `execution_state_reason_code = "Insufficient asset balance"` (or another pre-execution reason), it may have `attempts: []` - no calls were placed, and there will be no entities or audio to fetch.

***

### Error Handling

### HTTP Status Codes

| Status Code | Description                                                  | Action                                                                                            |
| ----------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
| `200`       | The request has succeeded                                    | N/A                                                                                               |
| `201`       | The request has succeeded and a new message has been created | N/A                                                                                               |
| `400`       | Bad Request - Invalid or malformed data                      | Verify request format matches API documentation. Check the error message for specific data issues |
| `401`       | Unauthorized - User is not authenticated                     | Please log in using the Login API                                                                 |
| `403`       | Forbidden - Token expired                                    | Please re-login to obtain a new JWT token                                                         |
| `404`       | Not Found - URL is not recognized                            | Verify the API endpoint URL is correct                                                            |
| `502`       | Bad Gateway                                                  | Retry the operation after some time. If issue persists after multiple retries, contact Awaaz.De   |
| `504`       | Gateway Timeout                                              | Retry the operation after some time. If issue persists after multiple retries, contact Awaaz.De   |
| `500-599`   | Server Error                                                 | We monitor our systems for such errors. If not resolved within 24 hours, please contact Awaaz.De  |

**Common Error Scenarios**

#### Invalid Credentials

```json theme={null}
{
  "non_field_errors": [
    "Unable to log in with provided credentials."
  ]
}
```

**Solution**: Verify email and password are correct.

#### Invalid Phone Number

```json theme={null}
{
  "error": "phone_number: Invalid input provided",
  "status": "invalid"
}
```

**Solution**: Ensure phone number is in valid international format (e.g., +9194XXXXXXXX).

#### Expired Token

**Status Code**: `403 Forbidden`

**Solution**: Re-authenticate using the Login API to obtain a new JWT token. **Message Not Found** - `404 Not Found`

```json theme={null}
{
  "detail": "No Message matches the given query."
}
```

Verify the message `id` is correct and belongs to your tenant.

**Malformed ID in URL** - `404 Not Found`

```json theme={null}
{
  "detail": "Not found."
}
```

Ensure the `id` in the URL path is a valid integer.

**Invalid Filter Value** - `400 Bad Request`

```json theme={null}
{
  "id": ["Enter a number."]
}
```

Check that query parameter values match the expected type - integers for `id`-based filters, `YYYY-MM-DDTHH:MM:SS` for `send_on`-based filters.

**Invalid Page Number** - `404 Not Found`

```json theme={null}
{
  "detail": "Invalid page."
}
```

Check `next` in the previous response before requesting the next page, rather than incrementing a counter blindly.

***

## Best Practices

1. **Token Management**: Cache the JWT token and reuse it for multiple requests within the 1-hour validity period. Only request a new token when the current one expires.
2. **Batch Processing**: Group messages in batches of up to 10,000 for optimal performance.
3. **Error Handling**: Always check the `status` field in the response for each message. Messages can fail individually while others succeed.
4. **Phone Number Format**: Always use international format with country code (e.g., +91 for India).
5. **Retry Logic**: Implement exponential backoff for 502, 504, and 5xx errors.
6. **Rate Limiting**: Be mindful of processing times when sending large batches of messages.

***

## Support

For technical support or questions about API integration, please contact:

* **Email**: [info@awaaz.de](mailto:info@awaaz.de)

***
