# Copilot: real-time help for human agents

> The copilot listens to your agents' calls and suggests what to say: objection answers, knowledge-base facts, the checklist, required disclosures and coaching nudges. Build copilot profiles over the API, attach them to queues, and read what the copilot did on each call.

The **copilot** works for your human agents, not instead of them. It listens to an agent's call as it happens and puts cards next to it in the agent workspace: the answer to the objection the customer just raised, a fact from your knowledge base, the next item on the checklist, the disclosure the agent hasn't read yet, a nudge to stop talking and ask a question. Supervisors see its alerts on their board.

What the copilot knows and suggests comes from a **copilot profile**: the playbook for one kind of call, such as policy renewals or customer service. Supervisors build profiles in the app, under **Copilot & knowledge**; over the API you create and change them from code, keep them in your own version control, and attach them to queues.

> **Plans**
>
> The copilot is part of every contact-centre plan and is billed per copilot seat. The Developer plan doesn't include it.

## Start from a template

Two templates cover the common cases: `sales-outbound` (opening, discovery, offer, objections and close) and `customer-service`. List them to see everything they set:

**cURL**

```sh
# The ready-made profiles you can start from.
curl https://api.morevoice.ai/v1/copilot_profiles/templates \
  -H "Authorization: Bearer $MOREVOICE_API_KEY"
```

**Node.js**

```ts title="list-templates.ts"
import MoreVoice from "@morevoice/sdk";

const mv = new MoreVoice(); // reads MOREVOICE_API_KEY

// The ready-made profiles you can start from.
for await (const template of mv.copilotProfiles.listTemplates()) {
	console.log(template.template_id, template.name, "—", template.description);
}
```

**Python**

```python title="list_templates.py"
import os

import requests

API = "https://api.morevoice.ai/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['MOREVOICE_API_KEY']}"}

# The ready-made profiles you can start from.
response = requests.get(f"{API}/copilot_profiles/templates", headers=HEADERS, timeout=30)
response.raise_for_status()
for template in response.json()["data"]:
    print(template["template_id"], template["name"], "—", template["description"])
```

Create a profile from a template and override what is yours. Fields you send replace the template's; everything else comes from it:

**cURL**

```sh
# A customer-service profile from the template, with your company, an objection and a disclosure the agent must read.
curl https://api.morevoice.ai/v1/copilot_profiles \
  -H "Authorization: Bearer $MOREVOICE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "customer-service",
    "name": "Policy renewals",
    "company": "Acme Insurance",
    "product": "Home and car insurance",
    "language": "he",
    "objections": [
      {
        "key": "price",
        "label": "Too expensive",
        "triggers": ["יקר מדי", "זה יקר"],
        "response": "Compare the renewal with the cost of a claim without cover, then offer the annual payment discount.",
        "kb_query": "renewal discounts"
      }
    ],
    "compliance": {
      "required_disclosures": [
        {
          "key": "recording",
          "label": "Recording notice",
          "phrases": ["השיחה מוקלטת"],
          "script": "לידיעתך, השיחה מוקלטת לצורכי שירות ובקרה.",
          "deadline_seconds": 30
        }
      ]
    }
  }'
```

**Node.js**

```ts title="create-profile.ts"
import MoreVoice from "@morevoice/sdk";

const mv = new MoreVoice(); // reads MOREVOICE_API_KEY

// A customer-service profile from the template, with your company, an objection and a disclosure the agent must read.
const profile = await mv.copilotProfiles.create({
	template_id: "customer-service",
	name: "Policy renewals",
	company: "Acme Insurance",
	product: "Home and car insurance",
	language: "he",
	objections: [
		{
			key: "price",
			label: "Too expensive",
			triggers: ["יקר מדי", "זה יקר"],
			response: "Compare the renewal with the cost of a claim without cover, then offer the annual payment discount.",
			kb_query: "renewal discounts",
		},
	],
	compliance: {
		required_disclosures: [
			{
				key: "recording",
				label: "Recording notice",
				phrases: ["השיחה מוקלטת"],
				script: "לידיעתך, השיחה מוקלטת לצורכי שירות ובקרה.",
				deadline_seconds: 30,
			},
		],
	},
});
console.log(profile.id, `version ${profile.version}`, `${profile.objections.length} objections`);
```

**Python**

```python title="create_profile.py"
import os

import requests

API = "https://api.morevoice.ai/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['MOREVOICE_API_KEY']}"}

# A customer-service profile from the template, with your company, an objection and a disclosure the agent must read.
response = requests.post(
    f"{API}/copilot_profiles",
    headers=HEADERS,
    json={
        "template_id": "customer-service",
        "name": "Policy renewals",
        "company": "Acme Insurance",
        "product": "Home and car insurance",
        "language": "he",
        "objections": [
            {
                "key": "price",
                "label": "Too expensive",
                "triggers": ["יקר מדי", "זה יקר"],
                "response": "Compare the renewal with the cost of a claim without cover, then offer the annual payment discount.",
                "kb_query": "renewal discounts",
            }
        ],
        "compliance": {
            "required_disclosures": [
                {
                    "key": "recording",
                    "label": "Recording notice",
                    "phrases": ["השיחה מוקלטת"],
                    "script": "לידיעתך, השיחה מוקלטת לצורכי שירות ובקרה.",
                    "deadline_seconds": 30,
                }
            ]
        },
    },
    timeout=30,
)
response.raise_for_status()
profile = response.json()
print(profile["id"], f"version {profile['version']}", f"{len(profile['objections'])} objections")
```

A new organisation gets one profile per template the first time it lists its profiles, so there is always something to start from.

## What a profile holds

| Field | What it sets |
| --- | --- |
| `goal`, `tone`, `company`, `product`, `language` | What the call is for, how the agent should sound, who they represent, and the calls' language. |
| `stages` | The call's stages, each with a `checklist`. A checklist item can tick itself when the agent says one of its `phrases`, and has a question (`ask`) the copilot suggests while it is open. |
| `script` | Instead of the linear stages, an **agent-script** [flow](https://docs.morevoice.ai/guides/flows/) that drives the call branch by branch (`flow_id`, and `version`: `published` or a pinned number). |
| `objections` | The objection library: what the customer may say (`triggers` match at once, `examples` match by meaning), the recommended `response`, a `follow_up`, and a `kb_query` whose facts are attached to the card. |
| `kb` | Which knowledge-base documents the copilot may answer from (`document_ids`; empty: all), reference `links`, and the confidence (`min_score`) above which it shows a knowledge card unasked. |
| `fields` | Details the copilot picks out of the conversation (a budget, a policy number, a date), with a `type` and an optional `pattern`. |
| `compliance` | `required_disclosures`: what the agent must say, the exact `script` to read if they missed it, and when (`deadline_seconds`, or before leaving a stage). `forbidden_phrases`: what they mustn't say, why, and safer wording (`instead`). |
| `coaching` | Thresholds for nudges: the agent's share of talk time, monologue length, dead air, speaking rate. |
| `alerts` | Conditions to flag on the call (a keyword, negative sentiment, silence, a long call, a request for a manager), their `severity`, and whether to raise them on the supervisor board. |
| `qa_rubric` | The [QA](https://docs.morevoice.ai/guides/qa/) rubric these calls are scored against, instead of the organisation's. |
| `model` | How suggestions are made: the model and its fallback, latency and cost controls (`speculative`, `deep_path`, `max_calls_per_minute`, `priority`). |

`PATCH /v1/copilot_profiles/{id}` changes a profile: nested objects (`kb`, `compliance`, `coaching`, `script`, `model`) merge with the stored values, and lists (`stages`, `objections`, `alerts`…) replace them, so send the whole list. Every change raises the profile's `version`; calls that start afterwards use the new version, and calls in progress keep theirs. `POST /v1/copilot_profiles/{id}/duplicate` copies a profile, for an A/B test of two playbooks.

The help centre explains each part of a profile from the supervisor's side: [copilot profiles](https://help.morevoice.ai/en/copilot/profiles/), [objections](https://help.morevoice.ai/en/copilot/objections/) and [fields and compliance](https://help.morevoice.ai/en/copilot/fields-and-compliance/).

## Give agents a profile

Calls answered from a queue get the queue's profile:

**cURL**

```sh
# Agents who answer the Support queue get this profile on every call.
curl -X PATCH https://api.morevoice.ai/v1/queues/$QUEUE_ID \
  -H "Authorization: Bearer $MOREVOICE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "copilot_profile_id": "'"$COPILOT_PROFILE_ID"'" }'
```

**Node.js**

```ts title="attach-to-queue.ts"
import MoreVoice from "@morevoice/sdk";

const mv = new MoreVoice(); // reads MOREVOICE_API_KEY

// Agents who answer the Support queue get this profile on every call.
const queue = await mv.queues.update(process.env.QUEUE_ID!, { copilot_profile_id: process.env.COPILOT_PROFILE_ID! });
console.log(queue.name, queue.copilot_profile_id);
```

**Python**

```python title="attach_to_queue.py"
import os

import requests

API = "https://api.morevoice.ai/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['MOREVOICE_API_KEY']}"}

# Agents who answer the Support queue get this profile on every call.
response = requests.patch(
    f"{API}/queues/{os.environ['QUEUE_ID']}",
    headers=HEADERS,
    json={"copilot_profile_id": os.environ["COPILOT_PROFILE_ID"]},
    timeout=30,
)
response.raise_for_status()
queue = response.json()
print(queue["name"], queue["copilot_profile_id"])
```

For calls they place themselves, agents pick a profile in the workspace before dialling. Deleting a profile leaves its queues without one, and their calls run without the copilot's playbook.

## See what the copilot did

`GET /v1/calls/{id}/copilot_events` lists what the copilot showed on a call and what the agent did with it: cards shown, used or dismissed, knowledge searches and deeper answers, each with its time in the call, the card's kind, how long the suggestion took and the profile version:

**cURL**

```sh
# What the copilot showed on a call, and what the agent did with it.
curl https://api.morevoice.ai/v1/calls/$CALL_ID/copilot_events \
  -H "Authorization: Bearer $MOREVOICE_API_KEY"
```

**Node.js**

```ts title="copilot-events.ts"
import MoreVoice from "@morevoice/sdk";

const mv = new MoreVoice(); // reads MOREVOICE_API_KEY

// What the copilot showed on a call, and what the agent did with it.
for await (const event of mv.calls.listCopilotEvents(process.env.CALL_ID!)) {
	console.log(`${(event.at_ms / 1000).toFixed(1)}s`, event.type, event.kind ?? "", event.card_id ?? "", event.latency_ms ?? "");
}
```

**Python**

```python title="copilot_events.py"
import os

import requests

API = "https://api.morevoice.ai/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['MOREVOICE_API_KEY']}"}

# What the copilot showed on a call, and what the agent did with it.
response = requests.get(f"{API}/calls/{os.environ['CALL_ID']}/copilot_events", headers=HEADERS, timeout=30)
response.raise_for_status()
for event in response.json()["data"]:
    print(f"{event['at_ms'] / 1000:.1f}s", event["type"], event["kind"] or "", event["card_id"] or "", event["latency_ms"] or "")
```

Use it to see which objection answers agents actually use, or to tune `kb.min_score` when knowledge cards are dismissed more than used. [Insights](https://docs.morevoice.ai/guides/qa/#insights) sums up copilot adoption over a period, and the [QA scorecard](https://docs.morevoice.ai/guides/qa/) of each call shows how the playbook was followed.

> **Test a profile on yourself**
>
> Profiles are configuration, shared by test and live keys. To try one, attach it to a queue that only you answer, call the queue, and read the copilot events afterwards.
