# RunWild API — Reference

> **Base URL:** `https://api.runwild.ai` or `https://public-api-staging-e5e9.up.railway.app` if using staging env
> **Format:** All requests and responses use JSON unless stated otherwise.
> **This file:** `https://api.runwild.ai/agents.md`
> **Admin reference:** `https://api.runwild.ai/agentsAdmin.md`

RunWild is a CRM and AI communications platform. This API lets you manage contacts, send SMS/email/voice communications, interact with pipelines, and control the AI agent.

---

## Authentication

Include the following header on every request:

| Header | Value |
|--------|-------|
| `X-API-Key` | Your organization API key |

---

## Admin Access

All endpoints also accept admin password authentication. This is intended for server-to-server automation where a single credential needs to reach any organization.

| Header | Value |
|--------|-------|
| `X-API-Password` | Server-level admin password (`API_PASSWORD` env var) |

When using `X-API-Password`, you must also supply the target organization:

- **Agent endpoints** (`/agent/*`) — include `"organizationId": "<uuid>"` in the JSON body
- **All other endpoints** — add `?organizationId=<uuid>` as a query param

See the full admin reference at `https://api.runwild.ai/agentsAdmin.md`.

---

## Contacts

> Looking for calls, SMS, and emails? Use [`GET /communications`](#communications) — it returns a unified, filterable list of all communication events across contacts.

### List contacts
```
GET /contacts
```
**Query params:**
- `limit` — integer 1–200, default 50
- `cursor` — pagination cursor from previous response
- `search` — full-text search (name, email, phone)
- `email` — filter by exact email
- `phone` — filter by exact phone
- `stageId` — filter by pipeline stage UUID
- `ownerMembershipIds` — comma-separated membership UUIDs
- `isLost` — `true` | `false`
- `tags` — comma-separated tag strings
- `createdAfter`, `createdBefore`, `updatedAfter`, `updatedBefore` — ISO 8601 date-times

**Example:**
```
GET /contacts?search=cameron&tags=vip&limit=20
X-API-Key: <key>
```

**Response:**
```json
{
  "data": [{ "...": "Contact object" }],
  "nextCursor": "uuid | null",
  "total": 120
}
```

---

### Create contact
```
POST /contacts
```
**Body:**
```json
{
  "firstName": "Cameron",
  "lastName": "Tester",
  "email": "cameron@example.com",
  "phone": "+14158473505",
  "address": "123 Main St",
  "aiSummary": "Interested in premium plan.",
  "consentSms": true,
  "doNotContact": false,
  "validEmail": true,
  "validSms": true,
  "validVoice": true,
  "validWa": false,
  "stageId": "uuid-of-stage",
  "ownerMembershipIds": ["uuid-of-membership"],
  "isLost": false,
  "tags": ["lead", "vip"],
  "note": "Met at the conference."
}
```
`firstName` is required. All other fields optional.

**Response:** `201 Created` — Contact object.

---

### Get contact
```
GET /contacts/{contactId}
```
**Example:**
```
GET /contacts/3fa85f64-5717-4562-b3fc-2c963f66afa6
X-API-Key: <key>
```

**Response:** Contact object.

---

### Update contact
```
PATCH /contacts/{contactId}
```
**Body:** Any subset of contact fields (same shape as POST, all optional).

**Example — move to a new stage and tag as won:**
```json
{
  "stageId": "uuid-of-won-stage",
  "tags": ["won", "vip"]
}
```

**Example — mark do-not-contact:**
```json
{
  "doNotContact": true
}
```

**Response:** Updated contact object.

---

### Get contact timeline
```
GET /contacts/{contactId}/activities
```
Returns all activity, communication, and comment events for a contact in reverse chronological order.

**Query params:**
- `limit` — 1–100, default 50
- `cursor` — pagination cursor
- `type` — `activity` | `communication` | `comment`
- `before`, `after` — ISO 8601 date-time filters

**Example:**
```
GET /contacts/3fa85f64-5717-4562-b3fc-2c963f66afa6/activities?type=communication&limit=10
X-API-Key: <key>
```

**Response:**
```json
{
  "data": [{ "...": "ContactTimelineEvent object" }],
  "nextCursor": "string | null"
}
```

---

### Create contact note
```
POST /contacts/{contactId}/notes
```

**Example:**
```
POST /contacts/3fa85f64-5717-4562-b3fc-2c963f66afa6/notes
X-API-Key: <key>
Content-Type: application/json
```
```json
{
  "text": "Left voicemail and sent follow-up SMS."
}
```

**Response:** `201 Created` — ContactNote object.

---

### Get contact notes
```
GET /contacts/{contactId}/notes
```
**Response:**
```json
{
  "data": [
    {
      "id": "uuid",
      "body": "Called back, interested in annual plan.",
      "createdAt": "2024-01-01T00:00:00Z",
      "author": { "id": "uuid", "name": "Alice", "image": null }
    }
  ]
}
```

---

### List contact field definitions
```
GET /contacts/fields
```
Returns all default and custom field definitions for contacts.

**Response:**
```json
{
  "data": [
    {
      "key": "lead_source",
      "label": "Lead Source",
      "description": "Where the contact came from",
      "type": "select",
      "options": ["web", "referral", "ads"]
    }
  ]
}
```

---

## Contact Object

```json
{
  "id": "uuid",
  "firstName": "Cameron",
  "lastName": "Tester",
  "fullName": "Cameron Tester",
  "email": "cameron@example.com",
  "phone": "+14158473505",
  "address": "123 Main St",
  "image": "https://...",
  "aiSummary": "Interested in the premium plan.",
  "aiSummaryLastUpdatedAt": "2024-01-01T00:00:00Z",
  "consentSms": true,
  "doNotContact": false,
  "validEmail": true,
  "validSms": true,
  "validVoice": true,
  "validWa": false,
  "agentModeOverride": "auto_all | review_all | disabled | null",
  "stage": { "id": "uuid", "name": "Lead", "position": 1, "isWon": false },
  "pipeline": { "id": "uuid", "name": "Sales" },
  "owners": [{ "membershipId": "uuid", "name": "Alice", "email": "alice@org.com" }],
  "isLost": false,
  "tags": [{ "id": "uuid", "text": "vip" }],
  "lastActivityAt": "2024-01-01T00:00:00Z",
  "customFieldValues": { "lead_source": "referral" },
  "createdAt": "2024-01-01T00:00:00Z",
  "updatedAt": "2024-01-01T00:00:00Z"
}
```

---

## SMS

### Send SMS
```
POST /sms/message
```
**Body:**
```json
{
  "to": "+14158473505",
  "body": "Hi Cameron, just following up on your inquiry!",
  "from": "+18001234567",
  "mediaUrls": ["https://example.com/image.jpg"],
  "metadata": { "campaignId": "abc123" }
}
```
Either `from` or `messagingServiceSid` is required.

**Response:** `201 Created` — SmsMessage object.

---

### List SMS messages
```
GET /sms/messages
```
**Query params:**
- `to`, `from` — phone number filters
- `status` — `queued` | `sending` | `sent` | `failed` | `delivered` | `undelivered` | `received` | `read` | `canceled`
- `dateFrom`, `dateTo` — ISO 8601
- `limit` — 1–200, default 50
- `offset` — default 0

**Example:**
```
GET /sms/messages?to=%2B14158473505&status=delivered&limit=10
X-API-Key: <key>
```

**Response:**
```json
{ "data": [{ "...": "SmsMessage object" }], "total": 42 }
```

---

### Get SMS message
```
GET /sms/message/{messageId}
```
**Response:** SmsMessage object.

---

## SmsMessage Object

```json
{
  "id": "uuid",
  "providerMessageId": "SM1234567890abcdef",
  "to": "+14158473505",
  "from": "+18001234567",
  "body": "Hi Cameron, just following up!",
  "status": "delivered",
  "direction": "outbound-api",
  "numSegments": "1",
  "price": "-0.0075",
  "priceUnit": "USD",
  "dateCreated": "2024-01-01T00:00:00Z",
  "dateSent": "2024-01-01T00:00:01Z"
}
```

---

## Email

### Send email
```
POST /email/message
```
**Body:**
```json
{
  "to": "cameron@example.com",
  "subject": "Following up on your inquiry",
  "textBody": "Hi Cameron, just wanted to check in.",
  "htmlBody": "<p>Hi Cameron, just wanted to check in.</p>",
  "from": { "email": "alice@acme.com", "name": "Alice from Acme" },
  "replyTo": "alice@acme.com",
  "metadata": { "campaignId": "abc123" }
}
```
`to`, `subject`, and at least one of `textBody`/`htmlBody` are required.

**Response:** `201 Created` — EmailMessage object.

---

### List email messages
```
GET /email/messages
```
**Query params:**
- `to`, `from` — email filters
- `status` — `QUEUED` | `SENDING` | `SENT` | `DELIVERED` | `RECEIVED` | `FAILED` | `CANCELED` | `UNKNOWN`
- `dateFrom`, `dateTo` — ISO 8601
- `limit` — 1–200, default 50
- `offset` — default 0

**Response:**
```json
{ "data": [{ "...": "EmailMessage object" }], "total": 10 }
```

---

### Get email message
```
GET /email/message/{messageId}
```
**Response:** EmailMessage object.

---

## EmailMessage Object

```json
{
  "id": "uuid",
  "providerEmailId": "msg_abc123",
  "provider": "microsoft-graph",
  "direction": "OUTBOUND",
  "status": "DELIVERED",
  "from": { "email": "alice@acme.com", "name": "Alice from Acme" },
  "to": ["cameron@example.com"],
  "cc": [],
  "bcc": [],
  "subject": "Following up on your inquiry",
  "textBody": "Hi Cameron, just wanted to check in.",
  "htmlBody": "<p>Hi Cameron, just wanted to check in.</p>",
  "metadata": {},
  "sentAt": "2024-01-01T00:00:00Z",
  "deliveredAt": "2024-01-01T00:00:05Z",
  "openedAt": null
}
```

---

## Voice

### List calls
```
GET /voice/calls
```
**Query params:**
- `to`, `from` — phone filters
- `status` — `queued` | `ringing` | `in-progress` | `completed` | `busy` | `failed` | `no-answer` | `canceled`
- `direction` — `INBOUND` | `OUTBOUND`
- `dateFrom`, `dateTo` — ISO 8601
- `limit` — 1–200, default 50
- `offset` — default 0

**Example:**
```
GET /voice/calls?direction=INBOUND&status=completed&limit=25
X-API-Key: <key>
```

**Response:**
```json
{ "data": [{ "...": "VoiceCall object" }], "total": 5 }
```

---

### Get call
```
GET /voice/call/{callId}
```
**Response:** VoiceCall object.

---

## VoiceCall Object

```json
{
  "id": "uuid",
  "callSid": "CA1234567890abcdef1234567890abcdef",
  "from": "+18001234567",
  "to": "+14158473505",
  "status": "completed",
  "direction": "OUTBOUND",
  "duration": "42",
  "durationSeconds": 42,
  "price": "-0.0150",
  "priceUnit": "USD",
  "answeredBy": null,
  "callerName": "Acme Corp",
  "startTime": "2024-01-01T00:00:00Z",
  "endTime": "2024-01-01T00:00:42Z",
  "dateCreated": "2024-01-01T00:00:00Z"
}
```

---

## Communications

A unified view of all communications (voice calls, SMS, emails) across all contacts. Useful for building inbox-style dashboards, audit logs, or reporting. Communications correspond to `ContactActivity` records of type `communication`.

### List communications
```
GET /communications
```
Returns a cursor-paginated list of communication events in reverse-chronological order.

**Query params:**
- `from`, `to` — ISO 8601 date-time range filter (on `occurredAt`)
- `channels` — comma-separated: `voice`, `sms`, `email`
- `directions` — comma-separated: `INBOUND`, `OUTBOUND` (omit for both)
- `memberIds` — comma-separated member user UUIDs (filter by team member who handled it)
- `contactId` — UUID to restrict to a specific contact
- `limit` — 1–100, default 25
- `cursor` — opaque cursor from previous response `nextCursor`

**Example:**
```
GET /communications?channels=voice,sms&directions=INBOUND&from=2024-01-01T00:00:00Z&limit=25
X-API-Key: <key>
```

**Response:**
```json
{
  "data": [
    {
      "id": "uuid",
      "contactId": "uuid",
      "channel": "voice",
      "direction": "INBOUND",
      "status": "completed",
      "from": "+14158473505",
      "to": "+18001234567",
      "summary": null,
      "snippet": null,
      "providerId": "CA1234567890abcdef",
      "communicationId": null,
      "occurredAt": "2024-01-01T12:00:00Z",
      "contact": { "id": "uuid", "fullName": "Cameron Tester", "image": null },
      "callSid": "CA1234567890abcdef1234567890abcdef",
      "duration": 42,
      "hasTranscript": true,
      "recordingUri": "https://api.telnyx.com/...",
      "isVoicemail": false,
      "isMissedCall": false
    }
  ],
  "nextCursor": "string | null",
  "total": 120
}
```

Pass `nextCursor` as `cursor` in the next request. `nextCursor` is `null` when no more pages.

**Voice call classification** — for voice events the API computes two convenience booleans:

| Field | Condition |
|-------|-----------|
| `isVoicemail` | `direction = INBOUND` + has `recordingUri` + (voicemail flag set OR `status = no-answer`) |
| `isMissedCall` | `direction = INBOUND` + not voicemail + `status` is `no-answer`, `busy`, `failed`, or `canceled` |

Both fields are `null` for non-voice channels.

---

### Get communication detail
```
GET /communications/{communicationId}
```
Returns full detail for a single communication event, enriched with channel-specific data.

**Example:**
```
GET /communications/3fa85f64-5717-4562-b3fc-2c963f66afa6
X-API-Key: <key>
```

**Voice response:**
```json
{
  "id": "uuid",
  "contactId": "uuid",
  "channel": "voice",
  "direction": "INBOUND",
  "status": "completed",
  "from": "+14158473505",
  "to": "+18001234567",
  "occurredAt": "2024-01-01T12:00:00Z",
  "contact": { "id": "uuid", "fullName": "Cameron Tester", "image": null },
  "callSid": "CA1234567890abcdef1234567890abcdef",
  "isVoicemail": false,
  "isMissedCall": false,
  "voiceCallId": "uuid",
  "duration": 42,
  "queueTime": 3,
  "transcript": {
    "text": "Hello, thanks for calling.",
    "segments": [{ "start": 0, "end": 2.5, "text": "Hello,", "speaker": "A" }],
    "generatedAt": "2024-01-01T00:00:00Z"
  },
  "transcriptStatus": "DONE",
  "recordingUri": "https://api.telnyx.com/...",
  "startTime": "2024-01-01T12:00:00Z",
  "endTime": "2024-01-01T12:00:42Z",
  "metadata": { "...": "raw metadata" }
}
```

**SMS response:**
```json
{
  "id": "uuid",
  "channel": "sms",
  "direction": "OUTBOUND",
  "status": "delivered",
  "from": "+18001234567",
  "to": "+14158473505",
  "smsId": "uuid",
  "body": "Hi Cameron, just following up!",
  "sentAt": "2024-01-01T12:00:00Z",
  "occurredAt": "2024-01-01T12:00:00Z"
}
```

**Email response:**
```json
{
  "id": "uuid",
  "channel": "email",
  "direction": "OUTBOUND",
  "status": "DELIVERED",
  "subject": "Following up on your inquiry",
  "from": "alice@acme.com",
  "to": "cameron@example.com",
  "emailId": "uuid",
  "textBody": "Hi Cameron, just checking in.",
  "htmlBody": "<p>Hi Cameron, just checking in.</p>",
  "sentAt": "2024-01-01T12:00:00Z",
  "deliveredAt": "2024-01-01T12:00:05Z",
  "occurredAt": "2024-01-01T12:00:00Z"
}
```

---

## Pipelines

### List pipelines
```
GET /pipelines
```
**Response:**
```json
{
  "data": [{ "id": "uuid", "name": "Sales", "stageCount": 4 }],
  "total": 1
}
```

---

### Get pipeline
```
GET /pipelines/{pipelineId}
```
Returns full pipeline detail including stages, transitions, and actions.

**Response:**
```json
{
  "id": "uuid",
  "name": "Sales",
  "stages": [
    {
      "id": "uuid",
      "name": "New Lead",
      "position": 1,
      "isWon": false,
      "actions": [],
      "transitions": []
    }
  ]
}
```

---

## Organization

### Get organization
```
GET /organization
```
Returns the organization associated with your API key, including pipeline structure and agent config.

**Response:**
```json
{
  "id": "uuid",
  "name": "Acme Corp",
  "description": "We build great products.",
  "phone": "+18001234567",
  "email": "contact@acme.com",
  "website": "https://acme.com",
  "agentModeDefault": "auto_all",
  "agentUserId": "uuid",
  "pipelines": [
    {
      "id": "uuid",
      "name": "Sales",
      "stages": [{ "id": "uuid", "name": "New Lead", "position": 1, "isWon": false }],
      "transitions": []
    }
  ]
}
```

---

To fetch all communications (calls, SMS, emails) across the organization, see the [Communications](#communications) section.

---

## Members

### Get member
```
GET /members?membershipId={uuid}
```
**Response:**
```json
{
  "membershipId": "uuid",
  "role": "member",
  "isOwner": false,
  "user": {
    "id": "uuid",
    "name": "Alice Smith",
    "email": "alice@acme.com",
    "image": "https://..."
  }
}
```

---

## AI Agent

### Start an AI agent call
```
POST /agent/call
```
Initiates an outbound call using the organization's AI voice assistant.

**Body:**
```json
{
  "contactId": "uuid-of-contact",
  "phone": "+14158473505",
  "brief": "Follow up about their interest in the premium plan. Keep it short and friendly.",
  "dynamicVariables": {
    "customer_name": "Cameron",
    "goal": "book a demo"
  },
  "machineDetection": "Enable",
  "asyncAmd": true,
  "detectionMode": "message"
}
```
Either `contactId` or `phone` is required.

**Response:** `201 Created` — VoiceCall object.

---

### Send an AI agent email
```
POST /agent/mail
```
Sends an outbound email from the organization's agent user using their connected mail account (Microsoft or Google).

**Body:**
```json
{
  "to": "cameron@example.com",
  "subject": "Following up on your inquiry",
  "textBody": "Hi Cameron, just checking in about the premium plan.",
  "htmlBody": "<p>Hi Cameron, just checking in about the premium plan.</p>",
  "replyTo": "agent@acme.com",
  "contactId": "uuid-of-contact",
  "metadata": { "campaignId": "abc123" }
}
```
`to`, `subject`, and at least one of `textBody`/`htmlBody` are required.

**Response:** `201 Created` — EmailMessage object.

---

### List AI agent emails
```
GET /agent/mail
```
Returns cursor-paginated emails from the organization’s connected agent mailbox account.

**Query params:**
- `organizationId` — required when using `X-API-Password` instead of `X-API-Key`
- `to`, `from` — email filters
- `status` — `QUEUED` | `SENDING` | `SENT` | `DELIVERED` | `RECEIVED` | `FAILED` | `CANCELED` | `UNKNOWN`
- `dateFrom`, `dateTo` — ISO 8601
- `q` — case-insensitive search over subject/body
- `limit` — 1–100, default 25
- `cursor` — cursor from previous response

**Response:**
```json
{
  "data": [
    {
      "id": "18f6fddf1d44a2d1",
      "providerEmailId": "18f6fddf1d44a2d1",
      "provider": "google-gmail",
      "providerTransport": "MAILBOX",
      "direction": "INBOUND",
      "status": "RECEIVED",
      "from": { "email": "agent@acme.com", "name": "Acme Agent" },
      "to": ["cameron@example.com"],
      "subject": "Following up",
      "snippet": "Hi Cameron, just checking in about the premium plan.",
      "lastError": null,
      "sentAt": "2024-01-01T00:00:00Z",
      "deliveredAt": null,
      "openedAt": null
    }
  ],
  "nextCursor": "eyJwcm92aWRlciI6Imdvb2dsZS1nbWFpbCIsInRva2VuIjoiQWJjMTIzIn0",
  "total": 10
}
```

---

### Get AI agent email
```
GET /agent/mail/{messageId}
```
Returns one agent mailbox email by provider message ID.

**Query params:**
- `organizationId` — required when using `X-API-Password` instead of `X-API-Key`
- `includeEvents` — `true` to include stored RunWild delivery events when available

**Response:**
```json
{
  "data": { "...": "EmailMessage object with snippet/providerTransport/lastError" },
  "events": [
    {
      "id": "uuid",
      "providerEmailId": "18f6fddf1d44a2d1",
      "eventType": "email.sent",
      "status": "SENDING",
      "payload": { "recipient": "cameron@example.com" },
      "occurredAt": "2024-01-01T00:00:00Z",
      "createdAt": "2024-01-01T00:00:00Z"
    }
  ]
}
```

---

### Transcribe a call recording
```
POST /agent/transcribe
```
Transcribes a call recording using OpenAI Whisper. If a transcript already exists and `regenerate` is false, returns it immediately.

Recordings larger than 23 MB are queued to the background worker. Poll with the same `callSid` until `status` is no longer `processing`.

**Body:**
```json
{
  "callSid": "CA1234567890abcdef1234567890abcdef",
  "regenerate": false
}
```
`callSid` is required. `regenerate` defaults to `false`.

**Response:**
```json
{
  "status": "exists | generated | processing",
  "transcript": {
    "text": "Hello, this is a transcript.",
    "segments": [{ "start": 0, "end": 2.5, "text": "Hello,", "speaker": "A" }],
    "generatedAt": "2024-01-01T00:00:00Z"
  },
  "callSid": "CA1234567890abcdef1234567890abcdef"
}
```
- `status: "exists"` — transcript was already in the database; `transcript` is returned.
- `status: "generated"` — transcription completed inline; `transcript` is returned.
- `status: "processing"` — recording is large and has been queued; re-send the same request to poll.

All responses use HTTP `200`.

---

### Get agent info
```
GET /agent?organizationId={uuid}
X-API-Password: <password>
```
Returns the agent user configured for an organization. **Admin password only** — not available via API key.

**Response:**
```json
{
  "organizationId": "uuid",
  "agentUserId": "uuid",
  "agent": {
    "id": "uuid",
    "name": "Acme Agent",
    "firstName": "Agent",
    "lastName": null,
    "email": "agent@acme.com",
    "phone": null,
    "image": null,
    "personality": "You are a helpful assistant for Acme Corp.",
    "locale": "en-US",
    "defaultCallDevice": "browser",
    "isAgent": true,
    "createdAt": "2024-01-01T00:00:00Z",
    "updatedAt": "2024-01-01T00:00:00Z"
  }
}
```

---

### Update agent
```
PATCH /agent
X-API-Password: <password>
```
Updates the agent user for an organization. Creates a new agent user if none exists. **Admin password only.**

**Body:**
```json
{
  "organizationId": "uuid",
  "agent": {
    "name": "Acme Agent",
    "firstName": "Agent",
    "lastName": null,
    "email": "agent@acme.com",
    "personality": "You are a friendly assistant for Acme Corp. Keep responses concise.",
    "locale": "en-US",
    "defaultCallDevice": "browser"
  }
}
```
`organizationId` is required. All `agent` fields optional.

**Response:** Updated agent object (same shape as `GET /agent`).

---

## Error Responses

All errors follow this shape:

```json
{ "error": "Human-readable message" }
```

| Status | Meaning |
|--------|---------|
| `400` | Bad request — invalid or missing parameters |
| `401` | Unauthorized — missing or invalid API key |
| `403` | Forbidden — insufficient permissions |
| `404` | Not found |
| `409` | Conflict — e.g. duplicate idempotencyKey |
| `422` | Unprocessable — valid request but business logic failed (e.g. missing provider config) |
| `502` | Provider error — upstream communication provider failed |
| `503` | Service unavailable — e.g. worker not configured |

---

## Common Patterns

**Paginating contacts:**
```
GET /contacts?limit=50
→ use response.nextCursor for next page
GET /contacts?limit=50&cursor={nextCursor}
```

**Finding a contact by phone then sending SMS:**
```
GET /contacts?phone=+14158473505
→ get contact.id
POST /sms/message  { "to": "+14158473505", "body": "Hi Cameron!" }
```

**Moving a contact to a new stage:**
```
PATCH /contacts/{contactId}
{ "stageId": "uuid-of-new-stage" }
```

**Getting pipeline stages to find valid stageIds:**
```
GET /pipelines
→ pick pipeline id
GET /pipelines/{pipelineId}
→ stages[].id for use in contact stageId
```

**Triggering an AI call then transcribing it:**
```
POST /agent/call  { "contactId": "uuid", "brief": "Follow up on demo request." }
→ response.callSid
POST /agent/transcribe  { "callSid": "CA..." }
→ poll until status != "processing"
```

**Fetching a contact's recent communications:**
```
GET /communications?contactId={contactId}&limit=10
→ data[].id for detail lookups
GET /communications/{id}
→ full detail including transcript/body
```

**Listing all inbound voice calls this week:**
```
GET /communications?channels=voice&directions=INBOUND&from=2024-01-01T00:00:00Z&to=2024-01-07T23:59:59Z
```
