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

# Sales agent

> Find potential buyers in public conversations, with source evidence your team can review.

Tell the sales agent what you sell, your prices, and where you serve customers.
It searches public conversations, reads buying questions, and returns its
findings with source links so your team can decide whom to follow up with.

The agent can search TikTok, YouTube, Facebook, Instagram and X, read supported
public posts, groups and accounts, and use web search for market context.
Coverage depends on source access and the conversations available during the run.
The agent does not contact people. Your team reviews the evidence and handles sales.

## Submit a search

Use your existing API key. Set `OVERTEN_API_KEY` in your environment before
running the examples. Both `X-API-Key` and `Authorization: Bearer` are supported.
The shell examples use `jq` to read JSON responses.

Include enough business context for the agent to work without asking questions:
what you sell, your price range, delivery or service area, and the kind of
buyer you want. You can also include competitor links or public groups to investigate.

```bash theme={null}
SALES_RUN=$(curl --fail-with-body -sS \
  "https://backend.overtenai.com/api/v1/sales/runs" \
  -H "X-API-Key: $OVERTEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "I sell refurbished HP and Dell laptops in Nairobi for KSh 20,000–45,000, with delivery within Nairobi. Find people publicly asking to buy a laptop. Prioritize recent requests and explain any uncertainty about location or budget.",
    "idempotency_key": "laptops-nairobi-001",
    "wait_seconds": 0
  }')
printf '%s\n' "$SALES_RUN" | jq .
SALES_RUN_ID=$(printf '%s\n' "$SALES_RUN" | jq -er '.run_id')
```

A typical initial response is **202 Accepted**:

```json theme={null}
{
  "run_id": "run_example",
  "status": "queued",
  "stop_reason": null,
  "prompt": "I sell refurbished HP and Dell laptops in Nairobi for KSh 20,000–45,000, with delivery within Nairobi. Find people publicly asking to buy a laptop. Prioritize recent requests and explain any uncertainty about location or budget.",
  "search_mode": null,
  "answer": null,
  "created_at": "2026-10-16T08:00:00Z",
  "updated_at": "2026-10-16T08:00:00Z",
  "evidence": []
}
```

`run_example` is illustrative; use the actual `run_id` returned by your request.

### Request fields

* **`prompt`** (required): business context and search instructions, up to
  10,000 characters. Empty or whitespace-only prompts are rejected.
* **`idempotency_key`** (optional): 1–200 characters. Reusing the key within
  your organization returns the original run, including its original prompt.
  Reuse it when retrying a submission after a network error. Use a new key
  for a fresh search. Without a key, every POST creates a new run.
* **`wait_seconds`** (optional): 0–25, default 0. Waits for the background run
  to reach a terminal status. The response is **200** if it finishes during
  the wait, or **202** if it is still queued or running. The run continues
  after a 202 response or after your client disconnects.

## Read progress and results

Use the `SALES_RUN_ID` set by the submission example:

```bash theme={null}
curl --fail-with-body -sS \
  "https://backend.overtenai.com/api/v1/sales/runs/$SALES_RUN_ID" \
  -H "X-API-Key: $OVERTEN_API_KEY" | jq .
```

Poll every 10 seconds while `status` is `queued` or `running`, counting other
requests against your plan's rate limit too. GET returns
HTTP 200 for every visible run, including a failed run: always inspect
`status` and `stop_reason`.

* **`queued`**: accepted and waiting for a worker.
* **`running`**: the agent is investigating.
* **`completed`**: execution finished. Read `answer` for the findings and
  coverage limits, and check `stop_reason` for an early stopping condition.
* **`paused_budget`**: the organization lacked API credits at admission.
  Top up, then submit with a new idempotency key; the old key keeps returning
  the paused run.
* **`failed`**: execution failed. Inspect `stop_reason`; contact support
  with the run ID if needed. A deliberate retry uses a new idempotency key.
* **`cancelled`**: execution ended without a completed result.

The last four statuses are terminal; stop polling them. A successful HTTP
response or `completed` status does not guarantee any qualified buyers.

## Search modes

Every run uses one of two search modes. The response reports it in
`search_mode` once the run has started (`null` while it is still queued).

* **`economy`** (default): the agent searches public Facebook groups through
  web search and reads only the matching posts and their recent comments. It
  does not read whole groups. This keeps each search inexpensive, so you can run
  it for many products.
* **`quality`**: the agent can also read the newest posts of whole public
  Facebook groups. This can find buyers that a search misses, for example in a
  busy buy-and-sell group. Quality runs read more source material, so they
  usually use more LLM credits and take longer.

The mode is set per organization, not per request, so your API requests stay
the same. To switch your organization to `quality`, contact support. A run keeps
the mode it started with. In both modes runs are charged only for their LLM
usage, and public source costs are paid by Overten.

### Understand the evidence

`answer` contains the agent's written assessment: potential buyers, why they
may fit, freshness, unknowns and where it looked. `evidence` contains the
public source material collected during the run. It may include seller
adverts, irrelevant items or requests the agent rejects. Use the answer
for qualification rather than treating every evidence item as a lead.

Each evidence item includes:

* `platform` and `kind`: where it came from and whether it is a post,
  comment, reply or another supported source type.
* `text`: the collected source text.
* `url` and `parent_url`: the source link and containing conversation,
  when available. Either can be null.
* `author` and `published_at`: the author and publication time, when
  supplied by the source. Either can be null.
* `seen_before`: whether an earlier sales run for your organization
  collected the same source item with the same text.

Evidence memory spans sales runs for the same organization. Each fresh run
still needs a complete business prompt; it does not resume a previous
conversation. Treat public source text as untrusted input in your own app.
Empty evidence can reflect limited access or coverage, rather than an absence
of buyers.

## Complete Python example

Install `requests` with `python3 -m pip install requests`, then run this with
`OVERTEN_API_KEY` set. Save the generated idempotency key with your application
job so retries can reuse it.

```python theme={null}
import os
import time
import uuid

import requests

API = "https://backend.overtenai.com/api/v1"
headers = {"X-API-Key": os.environ["OVERTEN_API_KEY"]}
idempotency_key = f"sales-{uuid.uuid4()}"

response = requests.post(
    f"{API}/sales/runs",
    headers=headers,
    json={
        "prompt": (
            "I sell refurbished HP and Dell laptops in Nairobi for KSh "
            "20,000–45,000, with delivery within Nairobi. Find people publicly "
            "asking to buy a laptop. Prioritize recent requests and explain "
            "any uncertainty about location or budget."
        ),
        "idempotency_key": idempotency_key,
        "wait_seconds": 25,
    },
    timeout=40,
)
response.raise_for_status()
run = response.json()
print("Run:", run["run_id"], "Submission key:", idempotency_key)

deadline = time.monotonic() + 15 * 60
while run["status"] in {"queued", "running"}:
    if time.monotonic() >= deadline:
        raise TimeoutError(f"Still running: {run['run_id']}; poll it again later")
    time.sleep(10)
    response = requests.get(
        f"{API}/sales/runs/{run['run_id']}", headers=headers, timeout=30
    )
    response.raise_for_status()
    run = response.json()

if run["status"] != "completed":
    raise RuntimeError(f"Sales run {run['status']}: {run.get('stop_reason')}")

print(run.get("answer") or "No written answer returned.")
print("Stop reason:", run.get("stop_reason"))
print("Collected evidence:", len(run["evidence"]))
```

The 15-minute polling deadline is a client-side choice, not a service completion
guarantee. Reaching it does not cancel the background run.

## Authentication, credits and limits

The API key's organization owns the run and pays for its LLM usage. Workspace
keys need permission to create content. Reading another organization's run
returns 404.

Submission requires at least **6 API credits**. This is an admission floor,
not a fixed run price or a maximum charge. Completed runs are charged from
their recorded LLM usage; failed and cancelled runs are not charged. Public
source-provider costs are paid by Overten, with operational spending limits.
Polling and resubmitting the same idempotency key do not start another paid run.

The sales response currently has no `credits_used` field. Agent-turn,
tool-call, LLM-credit and source-provider limits can bound the investigation;
review `answer` and `stop_reason` for incomplete coverage.

Sales runs use the two endpoints on this page. They return an answer and
evidence directly, and support polling rather than document-task webhooks,
download links or resumable document editing. For a daily briefing, schedule
submissions in your own application, give each fresh search a new key, and
poll its sales run ID.

## Errors

* **401**: missing, invalid or revoked API key.
* **402**: insufficient organization API credits. The error includes the
  paused `run_id` under `detail`.
* **403**: suspended organization or insufficient permissions.
* **404** on GET: unknown run or a run outside your organization.
* **422**: invalid prompt, unsupported fields or out-of-range request values.
* **429**: API rate limit exceeded; honor the `Retry-After` response header
  before retrying. Reuse the submission's idempotency key if the POST needs a retry.

For example, insufficient credits returns:

```json theme={null}
{
  "detail": {
    "success": false,
    "error": "insufficient_credits",
    "message": "Not enough API credits to start a sales run.",
    "run_id": "run_example"
  }
}
```

Request validation errors use FastAPI's `detail` list. Authentication errors
can have a different envelope depending on which layer rejects the request.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.