Graine AI

Clone an agent

Copy an agent — including its prompts — into a new DRAFT agent.

Copy an agent — including its prompts — into a new DRAFT agent.

The copy gets a new agent_id and its own prompt record. If you do not supply a display_name, the copy is named "<source name> - variant".

Three things that surprise people, all deliberate:

  • The clone comes back at version 2, not 1. Its conversation controls can only be applied by a second write, which increments the version.
  • status, inbound source configuration and follow-up settings are not copied. The copy always starts as a DRAFT.
  • cloned_from is added by this API from the request path; the underlying agent record has no such field.

An oversized prompt (over 380,000 UTF-8 bytes) makes the source agent unclonable and this returns 422.

Rate limit: 1000 requests per minute per organization (bucket default). Exceeding it returns 429 with Retry-After; the X-RateLimit-* response headers report your remaining allowance on every call.

POST
/v2/agents/{agent_id}/clone
/v2/agents/6c6f4cc3-f56c-479d-8422-7f76694daa29/clone

The Authorization access token

Authorization

Authorization
Required
Bearer <token>

Your Graine API key. Create one in the dashboard under Developers, or via POST /v2/api-keys. Send it as Authorization: Bearer <key>.

In: header

Request Body

application/jsonOptional

display_nameDisplay Name | null

Name for the copy. Defaults to ' - variant'.

Minimum length: 1Maximum length: 100

summarySummary | null

Note for the copy.

Maximum length: 2000

Path Parameters

agent_id
Required
Agent Id

Agent to copy.

Query Parameters

organization_idOrganization Id

Must match the organisation the API key belongs to.

Response Body

201

The new agent, including the id it was copied from.

agent_id
Required
Agent Id

Stable identifier for this agent. Use it everywhere else in the API.

org_idOrg Id | null

The organisation this agent belongs to. Always the organisation your API key belongs to.

display_nameDisplay Name | null

The agent's name, as it appears in the dashboard.

summarySummary | null

A short note about what this agent is for. Yours to write; never shown to a caller.

greeting_messageGreeting Message | null

The first line the agent speaks. May contain {variables}.

statusStatus | null

DRAFT | ACTIVE | INACTIVE | ARCHIVED. An agent must be ACTIVE to place calls, and INACTIVE to be edited or archived.

categoryCategory | null

What kind of work this agent does. Yours to choose; it groups agents in the dashboard and changes nothing at call time.

versionVersion

Incremented on every successful update. Read it back after a write to confirm the change landed.

Default: 1

timestampsTimestamps

When this agent was created and last changed.

labelsLabels

Free-form labels for your own filtering.

promptsPrompts

The agent's instructions. Empty on a list row — prompts are not fetched while listing.

variablesVariables

Placeholders the greeting and prompts may use.

analyticsAnalytics

What is extracted from every call, beyond the summary.

integrationsIntegrations

Where this agent is wired up: a status callback under webhook, browser calling under webcall, connected tools under mcp. Only what is configured appears.

knowledge_baseKnowledge Base

The knowledge base this agent answers from. Null when none is attached, and null on a list row — it is not fetched while listing.

conversation_configConversation Config | null

A flat view of the ten most-changed controls from the policy buckets below — the same values, not a second set. Change either.

configurationConfiguration

How this agent runs a call.

settingsSettings

Agent-wide switches.

follow_up_configFollow Up Config

What happens after a call, when a follow-up is warranted.

multilingualMultilingual | null

Per-language voices and prompts, when the agent switches language mid-call. Null when it speaks one language.

calling_guardCalling Guard | null

The hours this agent may ring people, or null when it may call at any time. Applies to POST /v2/calls only — a call outside the window is rescheduled for the next opening rather than refused.

dropped_languagesDropped Languages | null

Returned after an update that sent a language which could not be saved — usually a missing voice. Those languages were not stored.

cloned_from
Required
Cloned From

The agent this copy was made from, echoed from the request path.

400

1000 — malformed or contradictory input. 1002 — a required parameter is missing.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

401

1100 — missing, unknown or inactive API key. 1101 — a browser session token was presented instead of an API key.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

403

1102 — the key is valid but lacks the scope this endpoint requires, or names another organization. GET /v2/scopes reports what a key holds.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

404

1200 — no such resource in your organization. A resource belonging to another organization answers 404, never 403.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

422

1001 — the body or query failed validation; the message names the first offending field.

error
Required
integer

Stable integer code from the error table. Branch on this, not on the message.

message
Required
string

One human-readable sentence. Wording may change; the code will not.

429

1300 — the per-organization request rate or concurrent-call limit was reached. Carries Retry-After.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

503

1501 — a service this endpoint depends on is unreachable or returned a 5xx.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

504

1502 — a service this endpoint depends on did not answer in time.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

curl -X POST "https://api.graine.ai/v2/agents/6c6f4cc3-f56c-479d-8422-7f76694daa29/clone?organization_id=string" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "Support Bot (experiment)",
    "summary": "Copy of the activation agent for the Pune pilot."
  }'

The new agent, including the id it was copied from.

{
  "agent_id": "6c6f4cc3-f56c-479d-8422-7f76694daa29",
  "org_id": "string",
  "display_name": "Priya Plumber Activation Agent",
  "summary": "Activates plumbers who signed up but never scanned a code.",
  "greeting_message": "Hello! Am I speaking with {callee_name}?",
  "status": "DRAFT",
  "category": "other",
  "version": 1,
  "timestamps": {
    "created_at": "2026-08-20T13:06:01.724Z",
    "updated_at": "2026-08-20T13:27:49.073Z"
  },
  "labels": [
    "activation",
    "hindi"
  ],
  "prompts": {
    "conversation": {
      "system": "You are Priya, a warm caller from ..."
    },
    "summarization": {
      "system": "You are Priya, a warm caller from ..."
    },
    "extraction": {
      "system": "You are Priya, a warm caller from ..."
    }
  },
  "variables": {
    "custom": [
      "callee_name",
      "mobile_number"
    ],
    "placeholders": [
      "callee_name",
      "mobile_number"
    ],
    "_original_format": "string"
  },
  "analytics": {
    "custom": [
      {
        "name": "Lead qualification",
        "prompt": "Read the transcript and fill in each field.",
        "required_keys": [
          "intent",
          "callback_time"
        ]
      }
    ]
  },
  "integrations": {
    "webhook": {
      "url": "https://example.com/hooks/graine"
    },
    "webcall": {
      "enabled": true
    }
  },
  "knowledge_base": "product-handbook",
  "conversation_config": {
    "finale_message": "Thank you for your time.",
    "presence_probe_message": "Are you still on the call?",
    "probe_interval_seconds": 7,
    "probe_window_seconds": 30,
    "probe_min_words": 7,
    "block_unlisted_callers": false
  },
  "configuration": {
    "runtime": {
      "call_transport": {
        "provider": "default",
        "audio_format": "wav"
      },
      "speech_recognition": {
        "provider": "deepgram",
        "model": "nova-3",
        "language": "hi",
        "stream": true,
        "endpointing": 190,
        "encoding": "linear16",
        "sampling_rate": 16000,
        "task": "transcribe",
        "keywords": "haan,ji,bolo,pearl,sparsh"
      },
      "voice_synthesis": {
        "provider": "cartesia",
        "voice_id": "148a02f1-d396-4615-8552-52483eaf9d9a",
        "voice_name": "Laxmi",
        "speed": 1,
        "speaking_rate": 1,
        "audio_format": "wav",
        "buffer_size": 500,
        "stream": true,
        "caching": true,
        "model": "sonic-3.5"
      },
      "language_model": {
        "provider": "azure",
        "model": "azure/gpt-4o-mini-2",
        "agent_type": "simple_llm_agent",
        "config": {
          "max_tokens": 300,
          "temperature": 0.25
        },
        "knowledge_base_config": {
          "enabled": true,
          "collections": [
            "internal_organization-live-EXAMPLE__product-handbook"
          ],
          "similarity_top_k": 5,
          "max_results": 3,
          "reranker_enabled": true
        }
      }
    },
    "session_policy": {
      "session_ttl": 300,
      "close_after_reply": true,
      "finale_interruptible": false,
      "completion_probe": true,
      "probe_interval_seconds": 7,
      "auto_retry_plan": true,
      "trusted_callers": [
        "+917971442184"
      ],
      "silence_close_after": 12,
      "block_unlisted_callers": false,
      "entry_quota": -1,
      "probe_window_seconds": 30,
      "probe_min_words": 7,
      "probe_start_delay_seconds": 2.5
    },
    "stream_policy": {
      "bargein_word_threshold": 3,
      "force_chunking": true,
      "rapid_path_mode": true,
      "transcript_fidelity_mode": false,
      "emit_step_ms": 200,
      "bargein_backoff_ms": 0,
      "interrupt_on_speech_start": true
    },
    "engagement_policy": {
      "noise_guard_level": 2,
      "scene_bed_gain": -11,
      "listener_cues_enabled": false,
      "presence_probe_enabled": true,
      "keypad_signal_gate": false,
      "presence_probe_after_seconds": 6,
      "listener_cue_gap_seconds": 5,
      "locale_probe_turns": 2,
      "scene_bed_enabled": true,
      "scene_profile_id": "https://ambientsoundgraineai.s3.ap-south-1.amazonaws.com/office-ambience.mp3",
      "cadence_padding": false,
      "listener_cue_start_delay_seconds": 5,
      "presence_probe_message": "Are you still on the line?"
    },
    "message_policy": {
      "completion_probe_prompt": "You are judging whether a conversation is finished. It is finished when the customer's question has been answered and neither side has anything left to add.",
      "finale_message": "Thank you for your time."
    },
    "compliance_policy": {
      "pii_guard_enabled": false
    },
    "post_processing": [
      {
        "type": "summary",
        "enabled": true
      }
    ],
    "external_functions": [
      {
        "name": "check_order_status",
        "description": "Look up an order by its id.",
        "parameters": {
          "type": "object",
          "properties": {
            "order_id": {
              "type": "string"
            }
          }
        }
      }
    ]
  },
  "settings": {
    "agent_memory": false
  },
  "follow_up_config": {
    "follow_up_agent_id": "b2e9c7a4-5d31-4f68-9c02-7a1b3e5d9f84",
    "follow_up_prompt": "Read the finished call. If the customer asked to be called back, greet them by name and confirm the slot they asked for.",
    "follow_up_extra_logic": {
      "skip_if_ordered": true
    }
  },
  "multilingual": {
    "enabled": true,
    "active_language": "hi",
    "languages": {
      "hi": {
        "agent_name": "Priya"
      },
      "en": {
        "agent_name": "Priya"
      }
    }
  },
  "calling_guard": {
    "enabled": true,
    "timezone": "Asia/Kolkata",
    "start": "09:00",
    "end": "21:00",
    "days": [
      0,
      1,
      2,
      3,
      4
    ]
  },
  "dropped_languages": [
    "ta"
  ],
  "cloned_from": "6c6f4cc3-f56c-479d-8422-7f76694daa29"
}