> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developer.jelliu.co/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developer.jelliu.co/_mcp/server.

# List agents

GET https://api.jelliu.co/api/agents

Returns the workspace's agents, newest first (`created_at` descending). Soft-deleted agents are
never returned.

Pagination is keyset on `created_at`: to fetch the next page, pass the `created_at` of the last
agent you received as `cursor`. The response carries no pagination metadata — a page shorter than
`limit` is the last one. Two agents created in the same millisecond straddling a page boundary can
be skipped, because the cursor comparison is strict. See [Pagination](/pagination).

List items carry a projection of the agent: `chat_system_prompt`, `tool_profile`, `base_prompt_version`, `workflow`,
`tool_surface_version`, `high_risk`, `paused_by`, `paused_reason`, `action_limits` and
`deleted_at` are only returned by `GET /api/agents/{agentId}`.

**Consistency.** Each page is cached for up to 120 seconds per workspace and query. Creates,
updates and deletes made through this API clear the cache immediately; changes made by background
processes (provider provisioning, tool-surface sync, pausing) can take up to 120 seconds to show.

**Access**

* **Required scope:** `read`. Any workspace role can call it.
* **Rate limit:** General API — 120 (Starter), 200 (Growth), 300 (Business) or 600 (Enterprise) requests/min per workspace. See [Rate limits](/rate-limits).
* **Plan:** Available on every plan.

Reference: https://developer.jelliu.co/api-reference/agents/get-agents

## Authentication

- `Authorization` header (bearer token, required) — Workspace API key: `jl_` followed by 64 lowercase hex characters, created by the workspace owner in the dashboard (**Settings → API Keys**) and sent as `Authorization: Bearer jl_...`. The plaintext is shown once, at creation; Jelliu stores only a SHA-256 hash. A workspace can hold up to 25 active keys. | Scope | GET / HEAD | POST / PUT / PATCH / DELETE | Admin-only routes | | --- | --- | --- | --- | | `read` | Yes | No | No | | `write` | Yes | Yes | No | | `full` | Yes | Yes | Yes | Operations restricted to admins or owners reject keys without the `full` scope with `403`, and say so in their description. No key, whatever its scope, can mint or revoke API keys or rotate a webhook secret — that requires a signed-in owner session. A revoked key stops authenticating within about 10 seconds. See [Authentication](/authentication).

## Request

### Query parameters

- `limit` (integer, optional, default: 50) — Maximum agents to return, 1-200. Defaults to 50. A value outside the range or not an integer is rejected with `400` (not clamped).
- `cursor` (datetime, optional) — Return agents created strictly before this instant: the `created_at` of the last agent of the previous page. ISO 8601 in UTC with a `Z` suffix; an offset such as `-05:00` or a date without time is rejected with `400`.
- `search` (string, optional) — Case-insensitive substring match on the agent name. Trimmed; 1-200 characters after trimming (a whitespace-only value is rejected with `400`). `%` and `_` match literally.

## Response

### 200

A page of agents (list projection).

- `data` (list of object, required) — Agents, newest first. Empty array when there are none.
  - `id` (string, optional) — Unique identifier of the agent.
  - `tenant_id` (string, optional) — Workspace that owns the agent.
  - `name` (string, optional) — Display name.
  - `voice_id` (string, optional) — Voice id.
  - `language` (enum, optional) — Main language (see `CreateAgentInput.language` for each value).
    - Allowed values: `es`, `es-CO`, `es-MX`, `es-AR`, `es-neutral`, `en`, `en-US`, `pt`, `pt-BR`
  - `category` (enum, optional) — The agent's objective (see `CreateAgentInput.category` for each value); `null` when neither the agent nor its template sets one.
    - Allowed values: `sales`, `support`, `scheduling`, `surveys`, `collections`, `retention`, `notifications`, `interview`, `language_assessment`, `personal`, `general`
  - `template_id` (string, optional, nullable) — Linked objective template, or `null`.
  - `system_prompt` (string, optional) — The workspace's own prompt layer (may be empty).
  - `first_message` (string, optional) — Opening line (generated from the agent and business name when none was given).
  - `chat_system_prompt` (string, optional, nullable) — Legacy text-channel prompt; not writable through this API.
  - `dynamic_variables` (map from string to string, optional) — Stored key/value strings. Not currently applied to conversations.
  - `objection_handlers` (map from string to string, optional) — Stored objection → answer map. Not currently applied to conversations.
  - `escalation_rules` (list of object, optional) — Stored escalation rules. Not currently applied to conversations.
    - `trigger` (enum, required) — Condition that fires the rule: `negative_sentiment`, `explicit_request` (the contact asks for a human), `repeated_objection`, `timeout`.
      - Allowed values: `negative_sentiment`, `explicit_request`, `repeated_objection`, `timeout`
    - `threshold` (double, required) — Numeric threshold for the trigger. Must be 0 or greater — negative values are rejected with `400`.
    - `action` (enum, required) — What to do when the rule fires: `transfer_to_human`, `end_call_politely`, `schedule_callback`.
      - Allowed values: `transfer_to_human`, `end_call_politely`, `schedule_callback`
  - `transfer_rules` (list of object, optional) — In-call hand-offs to other agents.
    - `targetAgentId` (string, required) — Id of the agent that takes over. It must be provisioned; otherwise the rule is skipped when the provider configuration is built.
    - `condition` (string, required) — Natural-language condition the agent evaluates to decide the hand-off (1-500 characters).
    - `transferMessage` (string, optional) — What the agent says right before handing off (up to 500 characters).
  - `transfer_phone_number` (string, optional, nullable) — E.164 number live calls may be transferred to.
  - `max_call_duration_seconds` (integer, optional) — Hard cap on voice call length, in seconds.
  - `response_delay_seconds` (integer, optional) — Fixed pause before replying on text channels (0 = use human pacing).
  - `human_pacing_enabled` (boolean, optional) — Whether text replies use the growing human-pacing delay.
  - `enable_voicemail_detection` (boolean, optional) — Whether outbound calls detect voicemail and hang up.
  - `disclose_ai` (boolean, optional, nullable) — `null` means the country policy decides whether the agent says it is an AI.
  - `expressive_mode` (boolean, optional) — Expressive voice delivery.
  - `tool_call_sound_enabled` (boolean, optional) — Sound while a tool runs on voice calls.
  - `text_normalisation_type` (enum, optional) — How numbers and symbols become speakable text: `elevenlabs` or `system_prompt`.
    - Allowed values: `system_prompt`, `elevenlabs`
  - `turn_timeout` (double, optional) — Seconds of silence before the voice agent takes the turn.
  - `turn_eagerness` (enum, optional) — Turn-taking eagerness: `patient`, `normal`, `eager`. Legacy values `low`, `medium` and `high` may still appear on older agents.
    - Allowed values: `patient`, `normal`, `eager`, `low`, `medium`, `high`
  - `tts_speed` (double, optional) — Speech rate (0.5-2).
  - `tts_stability` (double, optional) — Voice stability (0-1).
  - `tts_similarity_boost` (double, optional) — Adherence to the original voice (0-1).
  - `department` (string, optional, nullable) — Routing department.
  - `skills` (list of string, optional, nullable) — Routing skills.
  - `allowed_toolkits` (list of string, optional, nullable) — Connected-app slugs this agent may use. `null` = no restriction; `[]` = none.
  - `disabled_tools` (map from string to list of string, optional, nullable) — Per-app denylist of tool slugs.
  - `toolkit_access` (map from string to enum, optional, nullable) — Per-app access level (`read` or `read_write`). An app not listed has `read_write`.
    - Allowed values: `read`, `read_write`
  - `mcp_enabled` (boolean, optional) — Whether the agent may carry MCP servers.
  - `tool_surface_status` (enum, optional) — State of the agent's connected-app tool surface at the providers: `synced` (applied), `syncing` (a change is being applied), `error` (the last sync failed; see `tool_surface_error` — re-send the same value to retry), `pending_provisioning` (saved, applied once the agent is provisioned).
    - Allowed values: `syncing`, `synced`, `error`, `pending_provisioning`
  - `tool_surface_error` (string, optional, nullable) — Reason the last tool-surface sync failed, or `null`.
  - `tool_surface_updated_at` (datetime, optional, nullable) — When the tool-surface status last changed.
  - `paused_at` (datetime, optional, nullable) — When the agent was paused (it stops taking actions and conversations), or `null` when running. Managed by the pause endpoints.
  - `created_at` (datetime, optional) — Creation time (UTC). Also the list pagination cursor.
  - `updated_at` (datetime, optional) — Last change to the row (UTC).
  - `tool_profile` (string, optional, nullable) — Retired; kept for older agents.
  - `base_prompt_version` (string, optional, nullable) — Fingerprint of the platform prompt layers last pushed to the provider; `null` before provisioning.
  - `workflow` (object, optional, nullable) — Stored conversation graph, or `null` (see `GET /api/agents/{agentId}/workflow`). Only returned by `GET /api/agents/{agentId}` and `PATCH`.
    - `version` (enum, required) — Schema version. Always `1`.
      - Allowed values: `1`
    - `entryNodeId` (string, required) — Node the conversation enters first; must be one of `nodes`.
    - `nodes` (list of object, required) — Phases (`subagent`) and terminal nodes (`end`).
      - `id` (string, required) — Node id, unique in the graph; `start` and `start_node` are reserved.
      - `type` (enum, required) — Node kind.
        - Allowed values: `subagent`, `end`
      - `label` (string, required) — Human-readable name shown in the dashboard and the provider's editor.
      - `prompt` (string, optional) — Subagent only — phase instructions appended to the agent's base prompt.
      - `llm` (string, optional) — Subagent only — provider LLM id for this phase. Not validated by Jelliu; an unknown id makes the push fail (502) and the previous graph is kept.
      - `turn` (object, optional) — Subagent only — per-phase turn taking. Must set at least one field; unknown keys are rejected.
        - `eagerness` (enum, optional) — How quickly the agent takes its turn.
          - Allowed values: `patient`, `normal`, `eager`
        - `timeoutSeconds` (double, optional) — Seconds of silence after which the agent takes the turn.
      - `entryBehavior` (enum, optional) — Subagent only — what happens when the phase is entered. Omit to leave the provider default.
        - Allowed values: `auto`, `generate_immediately`
      - `toolIds` (list of string, optional) — Subagent only — voice-engine tool ids added for this phase. Not Jelliu tool UUIDs.
      - `knowledgeBaseIds` (list of string, optional) — Subagent only — provider knowledge-base document ids added for this phase.
      - `requires` (list of string, optional) — Subagent only — manifest of tool names the phase cannot work without; alternatives joined with `|`. Stored for readiness checks; not sent to the provider and not enforced when saving.
    - `edges` (list of object, required) — Transitions between nodes, evaluated by the provider during the conversation.
      - `id` (string, required) — Edge id, unique in the graph.
      - `source` (string, required) — Node the edge leaves; must not be an `end` node.
      - `target` (string, required) — Node the edge enters; must differ from `source`.
      - `condition` (object, required) — `unconditional` (taken immediately; not allowed into an `end` node); `llm` with `text` (3–500 chars) judged by the model; or `expression` with `expression` and `textFallback` (20–600 chars, the same decision in words). Expression edges are sent to the provider using `textFallback`.
        - `type` (enum, required) — Condition kind.
          - Allowed values: `unconditional`, `llm`, `expression`
        - `text` (string, optional) — `llm` only. The condition in plain language.
        - `expression` (object, optional) — Expression tree over `state` variables and literals, used by `expression` edges. Leaves are `variable`, `number`, `boolean`, `string` and `llm`; `and`/`or` take 2–6 `children`; comparisons (`lt`, `lte`, `gt`, `gte`, `eq`, `neq`) take `left` and `right`. Every `variable` must name a variable declared in the workflow's `state`. Stored as authored. When pushed through `PUT /api/agents/{agentId}/workflow`, the edge is rendered with its `textFallback` instead, so this tree documents the intended rule rather than being evaluated by the provider.
          - `type` (enum, required) — Node kind: `variable`, `number`, `boolean`, `string`, `llm`, `and`, `or`, `lt`, `lte`, `gt`, `gte`, `eq`, `neq`.
            - Allowed values: `variable`, `number`, `boolean`, `string`, `llm`, `and`, `or`, `lt`, `lte`, `gt`, `gte`, `eq`, `neq`
          - `name` (string, optional) — `variable` only. A name declared in `state`.
          - `value` (any, optional) — `number`, `boolean` or `string` literal (strings max 200 chars).
          - `text` (string, optional) — `llm` only. The question the model answers.
          - `returns` (enum, optional) — `llm` only. Type of the model's answer.
            - Allowed values: `boolean`, `number`
          - `children` (list of object, optional) — `and`/`or` only. Operands.
          - `left` (object, optional) — Expression tree over `state` variables and literals, used by `expression` edges. Leaves are `variable`, `number`, `boolean`, `string` and `llm`; `and`/`or` take 2–6 `children`; comparisons (`lt`, `lte`, `gt`, `gte`, `eq`, `neq`) take `left` and `right`. Every `variable` must name a variable declared in the workflow's `state`. Stored as authored. When pushed through `PUT /api/agents/{agentId}/workflow`, the edge is rendered with its `textFallback` instead, so this tree documents the intended rule rather than being evaluated by the provider.
          - `right` (object, optional) — Expression tree over `state` variables and literals, used by `expression` edges. Leaves are `variable`, `number`, `boolean`, `string` and `llm`; `and`/`or` take 2–6 `children`; comparisons (`lt`, `lte`, `gt`, `gte`, `eq`, `neq`) take `left` and `right`. Every `variable` must name a variable declared in the workflow's `state`. Stored as authored. When pushed through `PUT /api/agents/{agentId}/workflow`, the edge is rendered with its `textFallback` instead, so this tree documents the intended rule rather than being evaluated by the provider.
        - `textFallback` (string, optional) — `expression` only. The same decision written as a judgement; this is what the provider evaluates.
    - `templateId` (string, optional) — Informational — the template this graph started from. Stored, not sent to the provider.
    - `state` (list of object, optional) — Variables the agent scores during the conversation through the `update_state` system tool, for `expression` edges to read. Declaring any attaches that tool and seeds each variable with a placeholder value at the provider.
      - `name` (string, required) — Variable name referenced by `variable` expressions.
      - `description` (string, required) — The scoring rubric the model follows to set the value. Be concrete about the scale.
      - `valueType` (enum, required) — Type of the value.
        - Allowed values: `number`, `boolean`, `string`
  - `tool_surface_version` (integer, optional) — Counter bumped on every queued tool-surface sync.
  - `high_risk` (boolean, optional) — When true (and the workspace enabled approvals) behaviour changes need a second user's approval.
  - `paused_by` (string, optional, nullable) — Identity (user or API key) that paused the agent, or `null`.
  - `paused_reason` (string, optional, nullable) — Reason given when pausing, or `null`.
  - `action_limits` (map from string to any, optional, nullable) — Per-agent action budget (`perMinute`, `perHour`, `perDay`, `perConversation`, `maxSpendPerDayCents`), managed by the agent controls endpoints; `null` = no limits.
  - `deleted_at` (any, optional) — Always `null` on returned agents (deleted agents are never returned).
  - `channels` (list of enum, optional) — Channels this agent answers on (from the `supported_channels` column): `voice` (phone and web voice), `whatsapp`, `email`, `webchat`, `instagram`, `messenger`. Empty array if the column is null.
    - Allowed values: `voice`, `whatsapp`, `email`, `webchat`, `instagram`, `messenger`
  - `objective` (string, optional, nullable) — Same value as `category` (kept for older clients).
  - `analysis` (object, optional) — Post-call analysis run by the voice provider on the transcript. `successEvaluation` criteria decide whether a conversation succeeded (they drive outcomes and conversion metrics); `dataCollection` fields are extracted from the transcript. Criterion ids starting with `qa_` are reserved for platform quality evaluation and a NEW one is rejected with `400` (ids already stored keep saving).
    - `successEvaluation` (list of object, optional) — Success criteria (up to 30).
      - `id` (string, required) — Stable identifier (1-100 characters). Must not start with `qa_` unless already stored.
      - `description` (string, required) — What the evaluator checks in the transcript (5-1000 characters). Write it in the agent's language.
    - `dataCollection` (list of object, optional) — Fields to extract (up to 40).
      - `id` (string, required) — Stable identifier (1-100 characters); results are keyed by it.
      - `dataType` (enum, required) — Type of the extracted value: `string`, `boolean`, `integer` or `number`.
        - Allowed values: `string`, `boolean`, `integer`, `number`
      - `description` (string, required) — Extraction instruction for the evaluator (5-1000 characters).
      - `enum` (list of string, optional) — Closed vocabulary for a `string` field (1-100 characters each after trimming, no duplicates, up to 50). Sending it on any other `dataType` is rejected with `400`. An empty array means no constraint.
  - `safety` (object, optional) — Guardrails — topics the agent refuses to discuss. Pushed to the voice provider's guardrails.
    - `blockedInputTopics` (list of string, optional) — Topics the agent declines when the contact raises them (up to 20, 200 characters each).
    - `blockedOutputTopics` (list of string, optional) — Topics the agent must never talk about (up to 20, 200 characters each).
  - `privacy` (object, optional) — PII redaction and data retention. On create the defaults below are stored even when the block is omitted; on update nothing is defaulted.
    - `redactPiiAudio` (boolean, optional, default: true) — Delete the transcript and personal data at the voice provider after processing.
    - `deleteAudioAfterProcessing` (boolean, optional, default: true) — Delete the call audio at the voice provider after processing.
    - `retentionDays` (integer, optional, default: 90) — Retention window in days (0-365) stored with the agent. For agents derived from a category base (every provisioned agent today) the provider-side audio retention follows the workspace plan.
  - `mcpEnabled` (boolean, optional) — Mirror of `mcp_enabled` (`true` when the column is absent).
  - `toolkitAccessEnforced` (boolean, optional) — Whether per-app `toolkit_access` restrictions are enforced by the platform (currently always `true`).
  - `total_calls` (integer, optional) — Currently always 0 on these endpoints; use the analytics endpoints for call counts.
  - `conversion_rate` (double, optional) — Currently always 0 on these endpoints; use the analytics endpoints.
  - `calls_today` (integer, optional) — Currently always 0 on these endpoints; use the analytics endpoints.
  - `avg_sentiment` (double, optional, nullable) — Currently always `null` on these endpoints (null means no data, never neutral).

## Errors

### 400 Bad Request Error

Invalid `limit`, `cursor` or `search`.

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

### 401 Unauthorized Error

No usable credential. Either the `Authorization` header is missing or is not a well-formed `Bearer jl_…` key, or the key is unknown, revoked or expired. Do not retry with the same key. See [Authentication](/authentication#401-unauthorized).

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

### 403 Forbidden Error

Authenticated, but not allowed: the key's scope does not cover the method, the route is restricted to workspace admins/owners and the key is not `full`, or the workspace is suspended (`TENANT_SUSPENDED`). See [Authentication](/authentication#scopes).

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

### 429 Too Many Requests Error

Rate limit exceeded for the current 60-second window (`RATE_LIMIT_EXCEEDED`). Wait `Retry-After` seconds, then retry. The `message` differs per limiter; the code does not. See [Rate limits](/rate-limits).

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

## Examples

**Response**

```json
{
  "data": [
    {
      "id": "7c1e4b2a-9d3f-4e8a-b5c6-2f1a0d9e8b7c",
      "tenant_id": "2d9f6c1e-3b4a-4c5d-9e8f-7a6b5c4d3e2f",
      "name": "Sofía — Ventas",
      "voice_id": "21m00Tcm4TlvDq8ikWAM",
      "language": "es-CO",
      "category": "sales",
      "template_id": null,
      "system_prompt": "Eres Sofía, asesora comercial de Cafés del Huila. Ayudas a los clientes a elegir el plan de suscripción de café que mejor se ajusta a su consumo.",
      "first_message": "Hola, le habla Sofía de Cafés del Huila. ¿Cómo está?",
      "dynamic_variables": {},
      "objection_handlers": {},
      "escalation_rules": [],
      "transfer_rules": [],
      "transfer_phone_number": "+573001234567",
      "max_call_duration_seconds": 240,
      "response_delay_seconds": 0,
      "human_pacing_enabled": true,
      "enable_voicemail_detection": true,
      "disclose_ai": null,
      "expressive_mode": false,
      "tool_call_sound_enabled": false,
      "text_normalisation_type": "elevenlabs",
      "turn_timeout": 1.5,
      "turn_eagerness": "normal",
      "tts_speed": 0.96,
      "tts_stability": 0.5,
      "tts_similarity_boost": 0.75,
      "department": "Ventas",
      "skills": [
        "suscripciones",
        "facturación"
      ],
      "allowed_toolkits": null,
      "disabled_tools": {},
      "toolkit_access": {},
      "mcp_enabled": true,
      "tool_surface_status": "synced",
      "tool_surface_error": null,
      "tool_surface_updated_at": null,
      "paused_at": null,
      "created_at": "2026-09-01T15:04:05.000Z",
      "updated_at": "2026-09-10T18:22:41.000Z",
      "channels": [
        "voice",
        "whatsapp",
        "email",
        "webchat",
        "instagram",
        "messenger"
      ],
      "objective": "sales",
      "analysis": {
        "successEvaluation": [
          {
            "id": "venta_concretada",
            "description": "El cliente aceptó contratar un plan de suscripción durante la llamada."
          }
        ],
        "dataCollection": [
          {
            "id": "producto_vendido",
            "dataType": "string",
            "description": "Nombre del plan o producto vendido. Vacío si no hubo venta."
          }
        ]
      },
      "privacy": {
        "redactPiiAudio": true,
        "deleteAudioAfterProcessing": true,
        "retentionDays": 90
      },
      "mcpEnabled": true,
      "toolkitAccessEnforced": true,
      "total_calls": 0,
      "conversion_rate": 0,
      "calls_today": 0,
      "avg_sentiment": null
    }
  ]
}
```

**SDK Code**

```python Agents_getAgents_example
import requests

url = "https://api.jelliu.co/api/agents"

querystring = {"cursor":"2026-09-01T15:04:05.000Z","limit":"20","search":"Sofía"}

headers = {"Authorization": "Bearer <token>"}

response = requests.get(url, headers=headers, params=querystring)

print(response.json())
```

```javascript Agents_getAgents_example
const url = 'https://api.jelliu.co/api/agents?cursor=2026-09-01T15%3A04%3A05.000Z&limit=20&search=Sof%C3%ADa';
const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Agents_getAgents_example
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.jelliu.co/api/agents?cursor=2026-09-01T15%3A04%3A05.000Z&limit=20&search=Sof%C3%ADa"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Agents_getAgents_example
require 'uri'
require 'net/http'

url = URI("https://api.jelliu.co/api/agents?cursor=2026-09-01T15%3A04%3A05.000Z&limit=20&search=Sof%C3%ADa")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
```

```java Agents_getAgents_example
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://api.jelliu.co/api/agents?cursor=2026-09-01T15%3A04%3A05.000Z&limit=20&search=Sof%C3%ADa")
  .header("Authorization", "Bearer <token>")
  .asString();
```

```php Agents_getAgents_example
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.jelliu.co/api/agents?cursor=2026-09-01T15%3A04%3A05.000Z&limit=20&search=Sof%C3%ADa', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

echo $response->getBody();
```

```csharp Agents_getAgents_example
using RestSharp;

var client = new RestClient("https://api.jelliu.co/api/agents?cursor=2026-09-01T15%3A04%3A05.000Z&limit=20&search=Sof%C3%ADa");
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

```swift Agents_getAgents_example
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.jelliu.co/api/agents?cursor=2026-09-01T15%3A04%3A05.000Z&limit=20&search=Sof%C3%ADa")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```