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

# DialNexa API fundamentals

> Authenticate DialNexa API requests, paginate list responses, handle errors, and add production-safe retries.

DialNexa API fundamentals cover the four behaviors every production client needs: Bearer API key authentication, endpoint-specific pagination, structured error handling, and safe retry decisions. Use this page after the [Quickstart](/docs/api-reference/quickstart) and before sending real customer traffic.

## Before you build a DialNexa API client

* Create an API key in **Settings > API Keys**.
* Use `https://api.dialnexa.com/v1` as the base URL.
* Keep API keys in server-side environment variables or a secrets manager.
* Read the individual endpoint page before relying on its request, response, side-effect, or retry behavior.

## Authenticate DialNexa API requests

An API key has a key ID and secret separated by a colon. Send the complete `key_id:secret` value as a Bearer token on every request.

```bash theme={null}
curl https://api.dialnexa.com/v1/voices \
  -H "Authorization: Bearer YOUR_API_KEY"
```

A missing, revoked, expired, or malformed key returns `401 Unauthorized`.

```json theme={null}
{
  "statusCode": 401,
  "message": "Unauthorized",
  "error": "Unauthorized"
}
```

Every operation under **Endpoints** supports Bearer API key authentication. Never send an API key from browser or mobile client code, commit it to source control, or include it in logs and screenshots.

### Avoid common authentication mistakes

* Send the full `key_id:secret` value, not only the key ID.
* Use the `Authorization: Bearer ...` header, not `x-api-key`.
* Keep `/v1` in the configured base URL.
* Use separate keys for development, staging, and production.
* Rotate a key immediately after suspected exposure.

Webhook requests use a separate HMAC signature. See [Webhooks](/docs/api-reference/webhooks/index#signature-verification) for raw-body verification examples.

## Paginate DialNexa list responses

DialNexa list endpoints do not use one universal response envelope. Most paginated endpoints accept `page` and `limit`, but their record keys, metadata fields, defaults, and maximums vary. Treat each endpoint page as the source of truth.

```bash theme={null}
curl "https://api.dialnexa.com/v1/workflows?page=2&limit=50" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

| Endpoint                                                                                                                                                                                                                                                                                                          | Records live in | Pagination fields                                                                           |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | ------------------------------------------------------------------------------------------- |
| [`GET /v1/workflows`](/docs/api-reference/v1/workflows/list)                                                                                                                                                                                                                                                           | `data`          | `meta.totalItems`, `meta.itemsPerPage`, `meta.totalPages`, `meta.currentPage`               |
| [`GET /v1/batch-calls`](/docs/api-reference/v1/batches/list)                                                                                                                                                                                                                                                           | `items`         | Top-level `total`, `page`, and `limit`                                                      |
| [`GET /v1/organization-phone-numbers`](/docs/api-reference/v1/phone-numbers/list)                                                                                                                                                                                                                                      | `items`         | Top-level `total`, `page`, and `limit`                                                      |
| [`GET /v1/voices`](/docs/api-reference/v1/voices/list)                                                                                                                                                                                                                                                                 | `voices`        | Top-level `total`, `page`, `limit`, and `totalPages`                                        |
| [`GET /v1/user-webhooks`](/docs/api-reference/v1/webhooks/list)                                                                                                                                                                                                                                                        | `webhooks`      | Top-level `total`, `page`, `limit`, and `totalPages`; default limit is `10`                 |
| [`GET /v1/agents`](/docs/api-reference/v1/agents/list)                                                                                                                                                                                                                                                                 | `agents`        | None. The full list is returned.                                                            |
| [`GET /v1/voices/s2s`](/docs/api-reference/v1/voices/s2s)                                                                                                                                                                                                                                                              | `voices`        | None. The full list is returned.                                                            |
| [`GET /v1/workflows/{workflowId}/leads`](/docs/api-reference/v1/workflow-leads/list)                                                                                                                                                                                                                                   | `data`          | Top-level `count`                                                                           |
| [`GET /v1/calls`](/docs/api-reference/v1/calls/list)                                                                                                                                                                                                                                                                   | Root JSON array | Accepts `page` and `limit`, but returns no pagination metadata; documented maximum is `200` |
| [`GET /v1/knowledge-base`](/docs/api-reference/v1/knowledge-base/list)                                                                                                                                                                                                                                                 | `items`         | `meta.totalItems`, `meta.itemsPerPage`, `meta.totalPages`, `meta.currentPage`               |
| [`GET /v1/languages`](/docs/api-reference/v1/languages/list), [`GET /v1/llms`](/docs/api-reference/v1/llms/list), [`GET /v1/llms/fallback`](/docs/api-reference/v1/llms/fallback), [`GET /v1/transcribers`](/docs/api-reference/v1/transcribers/list), and [`GET /v1/transcribers/fallback`](/docs/api-reference/v1/transcribers/fallback) | Root JSON array | None. Each catalog is returned in full.                                                     |

For an endpoint with `meta.totalPages`, continue until the current page reaches the reported total:

```typescript theme={null}
async function fetchAllWorkflows(apiKey: string) {
  const workflows = [];
  let page = 1;

  while (true) {
    const response = await fetch(
      `https://api.dialnexa.com/v1/workflows?page=${page}&limit=100`,
      { headers: { Authorization: `Bearer ${apiKey}` } }
    );
    if (!response.ok) throw new Error(`List workflows failed: ${response.status}`);

    const { data, meta } = await response.json();
    workflows.push(...data);
    if (page >= meta.totalPages) break;
    page += 1;
  }

  return workflows;
}
```

For calls, stop when a page contains fewer records than the requested `limit`. Catalog endpoints return their complete lists and should not be paged.

## Handle DialNexa API errors

Failed API requests return a structured error object. The `message` field can be a string or an array of validation messages.

```json theme={null}
{
  "statusCode": 400,
  "message": [
    "phone_number must be in E.164 format",
    "agent_id should not be empty"
  ],
  "error": "Bad Request"
}
```

| Field        | Type            | Meaning                                                                 |
| ------------ | --------------- | ----------------------------------------------------------------------- |
| `statusCode` | integer         | HTTP status code for the failure                                        |
| `message`    | string or array | Human-readable error details, including field-level validation messages |
| `error`      | string          | Short HTTP status name, such as `Bad Request` or `Not Found`            |

### Common HTTP status codes

| Code                        | Meaning                                                                             | What to do                                                        |
| --------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| `400 Bad Request`           | The body or query failed validation.                                                | Fix required fields, formats, enums, or unknown properties.       |
| `401 Unauthorized`          | The API key is missing, invalid, expired, or revoked.                               | Send the complete active key as a Bearer token.                   |
| `403 Forbidden`             | The key lacks permission, or Telephony Config blocks the destination.               | Check workspace access and destination settings.                  |
| `404 Not Found`             | The resource does not exist, belongs to another workspace, or the route is retired. | Confirm the resource ID and use a documented `/v1` endpoint.      |
| `409 Conflict`              | The operation conflicts with current resource state.                                | Read the resource and choose a valid next action.                 |
| `422 Unprocessable Entity`  | An endpoint reports semantic validation failure.                                    | Handle this only where the endpoint contract documents it.        |
| `429 Too Many Requests`     | A gateway or service rate limit was reached.                                        | Retry with backoff and honor `Retry-After` when present.          |
| `500 Internal Server Error` | DialNexa encountered an unexpected failure.                                         | Retry safe reads; verify writes before deciding whether to retry. |

Always check `response.ok` before using a response body:

```typescript theme={null}
async function dialNexaRequest(path: string, init: RequestInit = {}) {
  const response = await fetch(`https://api.dialnexa.com/v1${path}`, init);
  const body = await response.json();

  if (!response.ok) {
    const message = Array.isArray(body.message)
      ? body.message.join(", ")
      : body.message;
    throw new Error(`DialNexa ${body.statusCode}: ${message}`);
  }

  return body;
}
```

## Build reliable API clients

A timeout does not prove that a request failed. Repeating a write can create a duplicate resource, purchase another number, or place another call.

<Warning>
  The current v1 contract does not define a platform-wide idempotency header or one universal rate limit. Do not invent an idempotency header or hard-code a global request limit.
</Warning>

| Operation class          | Examples                                                     | Automatic retry decision                                                 |
| ------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------------------ |
| Read only                | List agents, get call, list voices                           | Safe after network errors, `429`, and transient `5xx` responses.         |
| State transition         | Publish agent, pause workflow, resume batch                  | Read the resource first and retry only if the transition did not happen. |
| Resource creation        | Create agent, webhook, knowledge base, purchase phone number | Search or list by stable business data before creating again.            |
| Billable external action | Create call, create batch call                               | Never retry blindly. Reconcile recent calls or batches first.            |
| Destructive action       | Delete agent, knowledge base, phone number, or webhook       | Read first and treat a later `404` as a possible completed deletion.     |

Set connection and total request timeouts that fit your application. For retry-safe requests, use capped exponential backoff with jitter and honor `Retry-After` when the response includes it.

```typescript theme={null}
async function getWithRetry(url: string, apiKey: string, attempts = 5) {
  for (let attempt = 0; attempt < attempts; attempt += 1) {
    const response = await fetch(url, {
      headers: { Authorization: `Bearer ${apiKey}` },
      signal: AbortSignal.timeout(15_000),
    });

    if (response.ok) return response.json();
    if (response.status !== 429 && response.status < 500) {
      throw new Error(`Non-retryable response: ${response.status}`);
    }

    const retryAfter = Number(response.headers.get("retry-after"));
    const delayMs = Number.isFinite(retryAfter)
      ? retryAfter * 1000
      : Math.min(30_000, 500 * 2 ** attempt) + Math.random() * 250;
    await new Promise((resolve) => setTimeout(resolve, delayMs));
  }

  throw new Error("Retry budget exhausted");
}
```

For calls, attach a stable correlation value in `metadata`, save the returned DialNexa call ID, and reconcile recent calls after an ambiguous timeout.

## Verify your API client

Before production traffic:

1. Confirm an authenticated list request returns `200`.
2. Test each list parser against the endpoint's actual response envelope.
3. Verify string and array error messages are both handled.
4. Confirm `4xx` failures are not retried automatically.
5. Simulate a transient read failure and confirm backoff is capped.
6. Simulate a timed-out write and confirm the client reads or reconciles before retrying.
7. Redact API keys, secrets, phone numbers, and sensitive metadata from logs.

## Troubleshoot API client behavior

| Problem                                  | What to check                                                                                                          |
| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Every request returns `401`              | Confirm the client sends the complete API key in `Authorization: Bearer YOUR_API_KEY`.                                 |
| A list parser returns no records         | Check whether records live in `data`, `items`, a resource-specific key, or the root array.                             |
| A former unversioned route returns `404` | Find the supported operation under **Endpoints** and rebuild the request against its documented `/v1` path and schema. |
| A write may have happened twice          | Stop retries, list or read the affected resource, and reconcile with your stable business data.                        |
| Requests retry indefinitely              | Set a maximum attempt count, cap the delay, and classify responses before retrying.                                    |

## Related pages

* [Quickstart](/docs/api-reference/quickstart): make and verify your first request.
* [TypeScript and Python examples](/docs/api-reference/code-examples): copy complete integration workflows.
* [Webhooks](/docs/api-reference/webhooks/index): verify signatures and process events.
* [AI coding agent integration](/docs/api-reference/ai-coding-agent-integration): generate a repository-specific implementation plan and code changes.
