RaabtaHQ
Create an assistant

Create an assistant

POST/api/v1/assistants

scope assistants:writeIdempotent

Creates an assistant. targets binds it to the channels it should answer on; a channel already bound to another assistant is refused with 409 conflict and reason: target_taken, because one channel has exactly one assistant.

Body

application/json
NameTypeDescription
namerequired
stringDisplay name
coloroptional
string | nullAccent colour, or null
enabledoptional
booleanDefaults to true. A disabled assistant refuses every run start
system_promptoptional
stringPersona and standing instructions
modeloptional
string | nullModel id, or null to inherit
temperatureoptional
number | nullSampling temperature
tool_accessoptional
map<string, boolean>Per-tool on or off, by tool id from GET /tools
gated_toolsoptional
string[]Tool ids that must raise a human approval first
scheduleoptional
objectWhen it is on duty
schedule.moderequired
string (enum)When the assistant may answeralwayscustom_hoursoutside_business_hours
schedule.hoursoptional
map<string, string[][]> | nullcustom_hours only: weekday to [open, close] ranges
default_handoff_user_idoptional
string (uuid) | nullMember who receives its handoffs
history_limitoptional
integer | nullRecent messages to send; null for the full history
transcribe_mediaoptional
booleanTranscribe voice notes and describe photos
handle_voiceoptional
booleanSend voice notes to the model
handle_imagesoptional
booleanSend customer photos to the model
voice_repliesoptional
booleanMay reply with a voice note
voice_provideroptional
string | nullText-to-speech provider
voice_idoptional
string | nullProvider-specific voice
targetsoptional
string[]What it answers on: channel names, or pwa:<connection_id> for one linked personal number. Replaces the whole list
Request
curl -X POST "https://your-crm.example.com/api/v1/assistants" \
  -H "Authorization: Bearer $RAABTA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c2a9e-3b4d-4c5e-8f7a-9b0c1d2e3f4a" \
  -d '{
    "name": "Front desk",
    "system_prompt": "You are the front desk for Acme Clinic. Book appointments, answer questions about opening hours and prices, and hand off anything clinical to a human.",
    "targets": [
      "whatsapp",
      "webchat"
    ]
  }'
Response · 201 Created
{
  "data": {
    "id": "8f2c1d3e-4a5b-4c6d-9e0f-1a2b3c4d5e6f",
    "name": "Front desk",
    "color": "#0ea5e9",
    "enabled": true,
    "system_prompt": "You are the front desk for Acme Clinic. Book appointments, answer questions about opening hours and prices, and hand off anything clinical to a human.",
    "model": null,
    "temperature": null,
    "tool_access": {
      "contacts.search": true,
      "bookings.create": true
    },
    "gated_tools": [
      "bookings.cancel"
    ],
    "delegation": {
      "enabled": false,
      "allowed_tools": null,
      "max_children_per_run": 3,
      "max_cost_usd": null
    },
    "schedule": {
      "mode": "always"
    },
    "default_handoff_user_id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
    "history_limit": 20,
    "transcribe_media": true,
    "handle_voice": true,
    "handle_images": false,
    "voice_replies": false,
    "voice_provider": null,
    "voice_id": null,
    "targets": [
      "whatsapp",
      "webchat"
    ],
    "instruction_count": 3,
    "created_at": "2026-08-01T09:00:00.000Z",
    "updated_at": "2026-09-01T11:30:00.000Z"
  }
}

Response

Wrapped in { data: … }
NameTypeDescription
idrequired
string (uuid)Assistant id
namerequired
stringDisplay name, shown in the dashboard and the activity feed
colorrequired
string | nullAccent colour used in the activity feed
enabledrequired
booleanA disabled assistant refuses every run start
system_promptrequired
stringThe persona and standing instructions sent to the model on every tick
modelrequired
string | nullModel id, or null to inherit the account default
temperaturerequired
number | nullSampling temperature, or null to inherit the account default
tool_accessrequired
map<string, boolean>Per-tool on or off, by tool id. A tool absent here takes the catalogue default from GET /tools
gated_toolsrequired
string[]Tool ids that raise a human approval before running, on top of the always-gated critical tools
delegationrequired
objectWhether and how far this assistant may delegate
delegation.enabledrequired
booleanMay this assistant hand work to sub-agents
delegation.allowed_toolsrequired
string[] | nullTools a sub-agent may be given; null means anything the parent holds, and an empty list means nothing
delegation.max_children_per_runrequired
integerSub-agents one run may spawn, clamped by the engine
delegation.max_cost_usdrequired
number | nullTighter spend ceiling for the whole tree; null uses the engine default
schedulerequired
objectWhen this assistant is allowed to answer
schedule.moderequired
string (enum)always = any hour; outside_business_hours = only when the business is closed; custom_hours = only inside the hours belowalwayscustom_hoursoutside_business_hours
schedule.hoursoptional
map<string, string[][]> | nullcustom_hours only: weekday key to a list of [open, close] times as HH:MM. A missing day is closed
default_handoff_user_idrequired
string (uuid) | nullMember who receives conversations this assistant hands off
history_limitrequired
integer | nullRecent messages sent to the model; null is the full history and 0 is none
transcribe_mediarequired
booleanTranscribe inbound voice notes and describe photos for the inbox
handle_voicerequired
booleanSend inbound voice notes to the model
handle_imagesrequired
booleanSend inbound customer photos to the model
voice_repliesrequired
booleanMay reply with a synthesised voice note
voice_providerrequired
string | nullText-to-speech provider, or null for none
voice_idrequired
string | nullProvider-specific voice, or null for its default
targetsrequired
string[]What it answers on: a channel name, or pwa:<connection_id> for one linked personal number
instruction_countrequired
integerInstructions bound to this assistant
created_atrequired
string (date-time)When the assistant was created
updated_atrequired
string (date-time)When it was last edited

Errors

StatusCodeWhen
400bad_requestThe request could not be parsed: malformed JSON, an invalid cursor, or a query parameter of the wrong shape.
400validation_errorThe body or query failed validation. `details` lists each failing field with a `path` and a `message`.
401unauthorizedNo usable API key: the Authorization header is missing or malformed, or the key is unknown, revoked or expired. The three are deliberately indistinguishable.
403forbiddenThe key is valid but lacks the scope this endpoint requires, or the request came from an address outside the key’s IP allowlist. The message says which.
403account_suspendedThe account this key belongs to is suspended. Rotating the key will not help; contact support.
409conflictThe request conflicts with current state: a duplicate phone number on create, an invalid state transition, or an idempotent request that is still in flight.
422unprocessableThe request was well-formed but cannot be carried out. `reason` is a stable string saying why (for example `outside_window` or `stage_not_in_pipeline`).
429rate_limitedThe per-key budget, or the per-IP budget for failed authentication, is exhausted. Honour `Retry-After` before retrying.
500internalSomething failed on our side. Safe to retry with the same Idempotency-Key; quote `request_id` if it persists.