Update an agent
Accepts PUT or PATCH (identical partial-merge semantics). Partial-patches an agent — only the fields you send change. autonomy_type, entity_type, and trigger_type are immutable — trigger_type may be sent but must match the agent’s current value, and a differing value returns 400. Send updated ticket_conditions / time_entry_conditions to change the entity filter; the server rebuilds the FIND_ENTITIES action. actions is the pipeline AFTER FIND_ENTITIES (CHAINED_ACTIONS only) — patching actions on an AGENTIC agent or conditions on a CONVERSATIONAL agent returns 400. An actions patch MERGES per-action: each entry targets one existing action by id (or unique action_type) and its input_as_dict is merged key-by-key, so changing one setting preserves the rest; unreferenced actions are kept and unmatched entries are appended. There is no wholesale-replace mode — a PUT can never drop or reorder an action (do structural pipeline edits in the dashboard). On a TRIAGE_TICKET / TRIAGE_TIME_ENTRY action, fields_to_triage is a list and so replaces wholesale under the normal merge — send fields_to_triage_edits in that action’s input_as_dict to change one field keyed by field_name: a matching entry merges key-by-key (every other triage field stays byte-identical), an unmatched one is appended (field_name is the only setting it must carry), and an entry carrying remove: true drops its target instead of editing it — a removal names its target and nothing else, must match a configured field (a no-match returns 400 rather than silently doing nothing), and cannot remove the last remaining field. A PSA-unsupported field_name returns 400, and sending both fields_to_triage and fields_to_triage_edits in one action returns 400. Use the field_name spelling GET /agents/<id> returns (the PSA name, e.g. contact, customFields); the enum member name (CONTACT, CUSTOM_FIELD) is also accepted. A custom field is keyed by field_name (customFields) plus custom_field_name, since all custom fields share one field_name — send both to target the right one. A toolbox patch also MERGES per tool: each entry under tool_settings_by_type is merged key-by-key onto that tool’s current settings (lists like allowed_users replace; null for a tool key disables it), so changing one tool’s setting no longer requires resending the whole toolbox. agent_settings, integrations, schedule, and callback_settings MERGE the same way: send only the fields you’re changing — e.g. {"agent_settings": {"custom_instructions": "…"}} edits only the instructions and leaves test_mode/model/til_channel intact, sending one integration’s permissions leaves the others connected, and {"schedule": {"timezone": "…"}} leaves the run cadence and business-hours windows intact. To edit a long custom_instructions runbook without resending the whole blob, send agent_settings_edits.custom_instructions with either edits (a list of exact old_string/new_string search-replaces — each must match exactly once, or pass replace_all; zero or multiple matches reject the request and write nothing) or append (text added with a newline separator). An optional base_hash (sha256:… of the instructions you read) gives optimistic concurrency: a stale hash returns 409 agent_stale_write instead of clobbering a concurrent dashboard edit. This form is mutually exclusive with a full agent_settings.custom_instructions in the same request (400), and applies only to AGENTIC / CONVERSATIONAL agents (CHAINED_ACTIONS has no agent_settings layer → 400). Refuses Neo-managed agents (403 agent_managed_by_neo). Editing actions, agent_settings, toolbox, integrations, or the conditions is a destructive change (triggers a TIL approval when an agent makes the call via NEO_API_REQUEST). 409 agent_update_conflict on a concurrent edit.
Authorizations
Authorization: Bearer <token> where <token> is either a neo_sk_<env>_<secret> API key (service account) or a Microsoft Entra ID access token (dashboard user).
Path Parameters
Body
PUT /public-api/agents/<id> body — a partial patch. Only the fields you send are
changed; everything else keeps its current value. autonomy_type, entity_type, and
trigger_type are immutable: autonomy_type/entity_type aren't fields here at
all, and trigger_type may be sent but must match the agent's current value — a differing
value returns 400. Editing actions, agent_settings, toolbox, integrations, or the
conditions is a destructive change and triggers a TIL approval when an agent makes the
call via NEO_API_REQUEST.
The intent-shape applies on update too: send updated ticket_conditions /
time_entry_conditions to change the entity filter; the server rebuilds the
FIND_ENTITIES action. actions is the pipeline AFTER FIND_ENTITIES (CHAINED_ACTIONS
only) — do not include FIND_ENTITIES yourself.
actions MERGES (ENG-5712): each entry targets one existing action by id (preferred —
read it from GET /agents/<id>) or, when id is omitted, by action_type if exactly one
existing action has that type. The targeted action keeps its other config — its
input_as_dict is merged key-by-key with what you send (nested objects merged, an explicit
null deletes a key, scalars/lists replace), so changing one dispatch setting no longer
wipes the technician pool, instructions, or calendar settings you didn't resend. Entries
that match nothing are appended as new actions; existing actions you don't mention are left
untouched. There is deliberately no wholesale-replace mode: a PUT can never drop or reorder
an action — do structural pipeline edits (remove/reorder) in the dashboard.
On a TRIAGE_TICKET / TRIAGE_TIME_ENTRY action, fields_to_triage is a list and so replaces
wholesale under that per-action merge; to change one triage field without resending the others
send fields_to_triage_edits in the action's input_as_dict. Each entry is keyed by
field_name: a match merges key-by-key (every other triage field stays byte-identical), an
unmatched field_name is appended (field_name is the only setting it must carry), an entry
carrying remove: true drops its target, a PSA-unsupported field_name is rejected 400, and
sending both fields_to_triage and fields_to_triage_edits in one action is a 400. A removal
names its target and nothing else, must match a configured field, and cannot empty the list.
toolbox, agent_settings, integrations, schedule, and callback_settings all MERGE the
same way (RFC 7396) — send only what you're changing, and everything you omit keeps its current
value:
toolboxmerges per tool undertool_settings_by_type(nested objects merge; scalars and lists — e.g.allowed_users— replace wholesale; anulltool key disables that tool).agent_settingsmerges per field, so{"agent_settings": {"custom_instructions": "…"}}changes only the instructions and leavestest_mode,model,reasoning_effort,til_channel, etc. untouched (a partial send used to silently reset them to defaults, e.g. flipping an agent out of test mode).integrationsmerges per integration, so sending one integration's permissions leaves every other connected integration untouched (a partial send used to disable them all).schedulemerges per field, so{"schedule": {"timezone": "…"}}changes only the timezone and leaves the run cadence / business-hours windows / weekend-skip intact.callback_settingsmerges per field (target_changesis still required). Nested objects merge key-by-key, scalars/lists replace wholesale, and an explicitnulldeletes that key. An explicit top-level"toolbox": nullstill clears the whole toolbox.
Surgical, config-preserving edits to agent_settings string fields. Today only
custom_instructions is editable this way. Use this instead of sending the whole
agent_settings.custom_instructions string when you want to change one span of a long runbook
without a full read-modify-write of the blob. Mutually exclusive with a full
agent_settings.custom_instructions in the same request.
ENABLED, DISABLED, DELETED Trigger/entity filter for TICKET agents — which tickets this agent fires on. Send it at the top level to set or change the filter; the server (re)builds the FIND_ENTITIES action from it (do not put the filter in actions). Use time_entry_conditions instead for TIME_ENTRY agents. Shape: conditions holds a combinator (and/or) and a rules list of leaves (field/operator/value) and/or nested groups (rules + combinator). A rule's position in the tree changes the logic — a company exclusion belongs as a top-level rule under the root and, not nested inside an or, or it only excludes when the other branches also miss.
Trigger/entity filter for TIME_ENTRY agents — the analogue of ticket_conditions. Ignored for TICKET agents.
SCHEDULED, TRIGGERED, NONE 