# Leads

People who called, chatted or filled in a form. Reading needs `leads:read`, creating needs `leads:write` (plus `outbound:write` to request an AI callback).

Base URL: `https://vocenya.com/api/public/v1`. Authenticate with `Authorization: Bearer $VOCENYA_API_KEY`.

- [List leads](#list-leads): `GET /leads`
- [Create a lead](#create-lead): `POST /leads`
- [Get a lead](#get-lead): `GET /leads/{lead}`

## List leads

`GET https://vocenya.com/api/public/v1/leads`

Required scope: `leads:read`

Your leads, newest first.

### Query parameters

- `phone_number` (string, optional): Leads with this phone number, in any common US format. Up to 32 characters.
- `email` (string (email), optional): Up to 255 characters.
- `since` (string (date-time), optional): Leads created at or after this time (ISO 8601).
- `until` (string (date-time), optional): Leads created before this time (ISO 8601).
- `per_page` (integer, optional): How many items per page, 1 to 100. Minimum 1. Maximum 100.
- `cursor` (string or null, optional): The `meta.next_cursor` (or `meta.prev_cursor`) of the previous page. Up to 1024 characters.

### Responses

- `200`: Paginated set of `LeadResource`
- `401`: The API key is missing, invalid, expired or revoked.
- `403`: The key does not have the `leads:read` scope.
- `422`: Validation error
- `429`: Too many requests for this key. Wait for the `Retry-After` seconds.

### Response attributes (200)

- `data` (array of objects, required)
  - `id` (integer, required)
  - `livemode` (boolean, required): `true` for live data, `false` for test data (created with a `vk_test_` key).
  - `name` (string or null, required)
  - `phone_number` (string or null, required)
  - `email` (string or null, required)
  - `reason` (string, optional): Why they got in touch. Not included for HIPAA-mode accounts.
  - `urgency` (string or null, required)
  - `details` (object or null, optional): Extra fields captured with the lead. Not included for HIPAA-mode accounts.
  - `custom_fields` (object, optional): The custom fields the AI collected, as `{key: {label, value}}`. Not included for HIPAA-mode accounts.
  - `source` (string, required): Where the lead came from. One of: `api`, `call`, `chat`, `outbound_call`, `website`.
  - `call_id` (integer or null, required)
  - `chat_id` (integer or null, required)
  - `outbound_call_id` (integer or null, required)
  - `created_at` (string or null, required)
- `links` (object, required)
  - `first` (string or null, required)
  - `last` (string or null, required)
  - `prev` (string or null, required)
  - `next` (string or null, required)
- `meta` (object, required)
  - `path` (string or null, required): Base path for paginator generated URLs.
  - `per_page` (integer, required): Number of items shown per page. Minimum 0.
  - `next_cursor` (string or null, required): The "cursor" that points to the next set of items.
  - `prev_cursor` (string or null, required): The "cursor" that points to the previous set of items.

### Example request

```bash
curl https://vocenya.com/api/public/v1/leads \
  -H "Authorization: Bearer $VOCENYA_API_KEY"
```

```javascript
const response = await fetch('https://vocenya.com/api/public/v1/leads', {
  headers: {
    Authorization: `Bearer ${process.env.VOCENYA_API_KEY}`,
  },
});

const data = await response.json();
console.log(data);
```

```php
$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://vocenya.com/api/public/v1/leads', [
    'headers' => ['Authorization' => 'Bearer '.getenv('VOCENYA_API_KEY')],
]);

$data = json_decode((string) $response->getBody(), true);
print_r($data);
```

```python
import os

import requests

response = requests.get(
    "https://vocenya.com/api/public/v1/leads",
    headers={"Authorization": f"Bearer {os.environ['VOCENYA_API_KEY']}"},
)

print(response.json())
```

### Example response (200)

```json
{
  "data": [
    {
      "id": 1,
      "livemode": true,
      "name": "Maria Rivera",
      "phone_number": "+15125550123",
      "email": "maria@example.com",
      "reason": "string",
      "urgency": "high",
      "details": {},
      "custom_fields": {},
      "source": "api",
      "call_id": 1,
      "chat_id": 1,
      "outbound_call_id": 1,
      "created_at": "2026-10-02T14:03:11+00:00"
    }
  ],
  "links": {
    "first": "string",
    "last": "string",
    "prev": "string",
    "next": "string"
  },
  "meta": {
    "path": "string",
    "per_page": 1,
    "next_cursor": "string",
    "prev_cursor": "string"
  }
}
```


## Create a lead

`POST https://vocenya.com/api/public/v1/leads`

Required scope: `leads:write`

Add a lead from your own system, for example a form on another site. It shows up in the portal
and is pushed to your CRM like any other lead.

Send `callback` to have the AI call the person back straight away (speed-to-lead). The key then
also needs the `outbound:write` scope. It needs outbound calling switched on, an active Pro plan and an AI receptionist, and is not available
in HIPAA mode. The call still follows calling hours, Do Not Call lists and daily limits; the
queued call is returned as `callback`.

### Request body

- `name` (string or null, optional): Up to 255 characters.
- `phone_number` (string or null, optional): A US phone number in any common format. Needed for a callback. Up to 32 characters.
- `email` (string (email) or null, optional): Up to 255 characters.
- `reason` (string, required): Why they got in touch. Up to 2000 characters.
- `urgency` (string or null, optional): Up to 50 characters.
- `details` (array of strings or null, optional): Any extra fields you want to keep with the lead (up to 50 keys). Up to 50 items.
- `callback` (object, optional): Ask the AI to call the person back now (speed-to-lead). Only send this when the person agreed to an AI call: `consent_text` must be the exact wording they agreed to. Needs the `outbound:write` scope as well as `leads:write`.
  - `consent_text` (string, optional): At least 20 characters. Up to 2000 characters.
  - `source_url` (string (uri) or null, optional): The page where they agreed. Up to 2048 characters.
  - `ip_address` (string or null, optional): Their IP address when they agreed.
  - `user_agent` (string or null, optional): Their browser's user agent when they agreed. Up to 512 characters.

### Responses

- `201`: The lead, and the AI callback when one was requested.
- `401`: The API key is missing, invalid, expired or revoked.
- `403`: A `callback` was sent but the key does not have the `outbound:write` scope (`missing_scope`).
- `422`: Validation failed, or a callback was requested but cannot be made (`speed_to_lead_unavailable`, `invalid_number`).
- `429`: Too many requests for this key. Wait for the `Retry-After` seconds.

### Response attributes (201)

- `data` (object, required)
  - `id` (integer, required)
  - `livemode` (boolean, required): `true` for live data, `false` for test data (created with a `vk_test_` key).
  - `name` (string or null, required)
  - `phone_number` (string or null, required)
  - `email` (string or null, required)
  - `reason` (string, optional): Why they got in touch. Not included for HIPAA-mode accounts.
  - `urgency` (string or null, required)
  - `details` (object or null, optional): Extra fields captured with the lead. Not included for HIPAA-mode accounts.
  - `custom_fields` (object, optional): The custom fields the AI collected, as `{key: {label, value}}`. Not included for HIPAA-mode accounts.
  - `source` (string, required): Where the lead came from. One of: `api`, `call`, `chat`, `outbound_call`, `website`.
  - `call_id` (integer or null, required)
  - `chat_id` (integer or null, required)
  - `outbound_call_id` (integer or null, required)
  - `created_at` (string or null, required)
- `callback` (object or null, required)
  - `id` (integer, required)
  - `livemode` (boolean, required): `true` for live data, `false` for test data (created with a `vk_test_` key).
  - `source` (string, required): What started the call: your system (`api`), a speed-to-lead callback, a campaign or an agent. | | |---| | `speed_to_lead` <br/> The AI calls back someone who just asked to be contacted. | | `agent_dial` <br/> A GH Live agent dials a number by hand from the workspace. | | `campaign` <br/> The AI works through a client's campaign contact list. | | `api` <br/> The client's own system queued an AI call through the public API or platform MCP server. | One of: `speed_to_lead`, `agent_dial`, `campaign`, `api`.
  - `purpose` (string, required): `marketing` needs marketing consent; `informational` covers reminders and follow-ups the person asked for. One of: `marketing`, `informational`.
  - `status` (string, required): One of: `queued`, `scheduled`, `blocked`, `connecting_agent`, `dialing`, `in_progress`, `completed`, `no_answer`, `voicemail`, `failed`.
  - `contact_name` (string or null, required)
  - `phone_number` (string, required)
  - `blocked_reason` (string or null, required): Why the call was blocked or delayed, as a code.
  - `blocked_reason_message` (string or null, required): Why the call was blocked or delayed, in words.
  - `scheduled_for` (string or null, required): When a delayed call is due to ring (calling hours, daily limit).
  - `attempts` (integer, required)
  - `answered_at` (string or null, required)
  - `answered_by` (string or null, required): Who picked up, from answering machine detection: `human`, `machine` or `unknown`. `null` when there was no result (not answered, or an agent's call). One of: `human`, `machine`, `unknown`.
  - `voicemail_left_at` (string or null, required): When the AI finished leaving a message on voicemail, under the account's "leave a message" setting.
  - `ended_at` (string or null, required)
  - `duration_seconds` (integer or null, required)
  - `summary` (string or null, optional): The AI's summary of the call. Not included for HIPAA-mode accounts.
  - `transcript` (array of objects or null, optional): The conversation, turn by turn. Only on `GET /outbound-calls/{id}`, and never for HIPAA-mode accounts.
    - `role` (string, required)
    - `text` (string, required)
  - `created_at` (string or null, required)

### Example request

```bash
curl -X POST https://vocenya.com/api/public/v1/leads \
  -H "Authorization: Bearer $VOCENYA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Maria Rivera",
  "email": "maria@example.com",
  "reason": "Wants a quote for a water heater replacement",
  "urgency": "high",
  "callback": {
    "consent_text": "I agree to receive an automated AI phone call from Rivera Plumbing about my request."
  }
}'
```

```javascript
const response = await fetch('https://vocenya.com/api/public/v1/leads', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.VOCENYA_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "name": "Maria Rivera",
    "email": "maria@example.com",
    "reason": "Wants a quote for a water heater replacement",
    "urgency": "high",
    "callback": {
      "consent_text": "I agree to receive an automated AI phone call from Rivera Plumbing about my request."
    }
  }),
});

const data = await response.json();
console.log(data);
```

```php
$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://vocenya.com/api/public/v1/leads', [
    'headers' => ['Authorization' => 'Bearer '.getenv('VOCENYA_API_KEY')],
    'json' => [
        'name' => 'Maria Rivera',
        'email' => 'maria@example.com',
        'reason' => 'Wants a quote for a water heater replacement',
        'urgency' => 'high',
        'callback' => [
            'consent_text' => 'I agree to receive an automated AI phone call from Rivera Plumbing about my request.',
        ],
    ],
]);

$data = json_decode((string) $response->getBody(), true);
print_r($data);
```

```python
import os

import requests

response = requests.post(
    "https://vocenya.com/api/public/v1/leads",
    headers={"Authorization": f"Bearer {os.environ['VOCENYA_API_KEY']}"},
    json={
        "name": "Maria Rivera",
        "email": "maria@example.com",
        "reason": "Wants a quote for a water heater replacement",
        "urgency": "high",
        "callback": {
            "consent_text": "I agree to receive an automated AI phone call from Rivera Plumbing about my request.",
        },
    },
)

print(response.json())
```

### Example response (201)

```json
{
  "data": {
    "id": 1,
    "livemode": true,
    "name": "Maria Rivera",
    "phone_number": "+15125550123",
    "email": "maria@example.com",
    "reason": "string",
    "urgency": "high",
    "details": {},
    "custom_fields": {},
    "source": "api",
    "call_id": 1,
    "chat_id": 1,
    "outbound_call_id": 1,
    "created_at": "2026-10-02T14:03:11+00:00"
  },
  "callback": {
    "id": 1,
    "livemode": true,
    "source": "speed_to_lead",
    "purpose": "marketing",
    "status": "queued",
    "contact_name": "Maria Rivera",
    "phone_number": "+15125550123",
    "blocked_reason": "string",
    "blocked_reason_message": "string",
    "scheduled_for": "string",
    "attempts": 1,
    "answered_at": "2026-10-02T14:03:11+00:00",
    "answered_by": "human",
    "voicemail_left_at": "2026-10-02T14:03:11+00:00",
    "ended_at": "2026-10-02T14:03:11+00:00",
    "duration_seconds": 1,
    "summary": "string",
    "transcript": [
      {
        "role": "string",
        "text": "string"
      }
    ],
    "created_at": "2026-10-02T14:03:11+00:00"
  }
}
```


## Get a lead

`GET https://vocenya.com/api/public/v1/leads/{lead}`

Required scope: `leads:read`

### Path parameters

- `lead` (integer, required): The lead id.

### Responses

- `200`: `LeadResource`
- `401`: The API key is missing, invalid, expired or revoked.
- `403`: The key does not have the `leads:read` scope.
- `404`: No record with that id belongs to your account.
- `429`: Too many requests for this key. Wait for the `Retry-After` seconds.

### Response attributes (200)

- `data` (object, required)
  - `id` (integer, required)
  - `livemode` (boolean, required): `true` for live data, `false` for test data (created with a `vk_test_` key).
  - `name` (string or null, required)
  - `phone_number` (string or null, required)
  - `email` (string or null, required)
  - `reason` (string, optional): Why they got in touch. Not included for HIPAA-mode accounts.
  - `urgency` (string or null, required)
  - `details` (object or null, optional): Extra fields captured with the lead. Not included for HIPAA-mode accounts.
  - `custom_fields` (object, optional): The custom fields the AI collected, as `{key: {label, value}}`. Not included for HIPAA-mode accounts.
  - `source` (string, required): Where the lead came from. One of: `api`, `call`, `chat`, `outbound_call`, `website`.
  - `call_id` (integer or null, required)
  - `chat_id` (integer or null, required)
  - `outbound_call_id` (integer or null, required)
  - `created_at` (string or null, required)

### Example request

```bash
curl https://vocenya.com/api/public/v1/leads/311 \
  -H "Authorization: Bearer $VOCENYA_API_KEY"
```

```javascript
const response = await fetch('https://vocenya.com/api/public/v1/leads/311', {
  headers: {
    Authorization: `Bearer ${process.env.VOCENYA_API_KEY}`,
  },
});

const data = await response.json();
console.log(data);
```

```php
$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://vocenya.com/api/public/v1/leads/311', [
    'headers' => ['Authorization' => 'Bearer '.getenv('VOCENYA_API_KEY')],
]);

$data = json_decode((string) $response->getBody(), true);
print_r($data);
```

```python
import os

import requests

response = requests.get(
    "https://vocenya.com/api/public/v1/leads/311",
    headers={"Authorization": f"Bearer {os.environ['VOCENYA_API_KEY']}"},
)

print(response.json())
```

### Example response (200)

```json
{
  "data": {
    "id": 1,
    "livemode": true,
    "name": "Maria Rivera",
    "phone_number": "+15125550123",
    "email": "maria@example.com",
    "reason": "string",
    "urgency": "high",
    "details": {},
    "custom_fields": {},
    "source": "api",
    "call_id": 1,
    "chat_id": 1,
    "outbound_call_id": 1,
    "created_at": "2026-10-02T14:03:11+00:00"
  }
}
```

