Skip to main content
PUT

Authorizations

Authorization
string
header
required

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

agent_id
string
required

Body

application/json

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.

Because an id-less entry matches by action_type, it can only ever ADD an action of a type the agent does not already have. Send an id-less entry for a type that already has exactly one action and you EDIT that action (200), rather than adding a second one; two or more of that type return 400. There is therefore no supported way to add a second action of an existing type through this endpoint — add it 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.

A custom field is keyed by field_name AND custom_field_name, so an edit to one must send both — custom_field_name alone says which custom field you mean. An edit that omits it keys to a different slot, matches nothing, and is appended as a new entry; on an enabled agent that entry is rejected 400 (custom_triage_field_without_a_name), because a custom field with no name triages nothing.

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:

  • toolbox merges per tool under tool_settings_by_type (nested objects merge; scalars and lists — e.g. allowed_users — replace wholesale; a null tool key disables that tool).
  • agent_settings merges per field, so {"agent_settings": {"custom_instructions": "…"}} changes only the instructions and leaves test_mode, til_channel, etc. untouched (a partial send used to silently reset them to defaults, e.g. flipping an agent out of test mode).
  • integrations merges per integration, so sending one integration's permissions leaves every other connected integration untouched (a partial send used to disable them all).
  • schedule merges per field, so {"schedule": {"timezone": "…"}} changes only the timezone and leaves the run cadence / business-hours windows / weekend-skip intact.
  • callback_settings merges per field (target_changes is still required). Nested objects merge key-by-key, scalars/lists replace wholesale, and an explicit null deletes that key. An explicit top-level "toolbox": null still clears the whole toolbox.
actions
Actions · object[] | null
agent_settings
Agent Settings · object | null
agent_settings_edits
AgentSettingsEdits · object | null

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.

callback_settings
Callback Settings · object | null
confirm_estimated_cost
boolean
default:false

Set to true to save a scheduled Agent whose estimated cost needs confirmation (see GET /agents/{id}/cost-estimate). Without it, a save that leaves a scheduled Agent ENABLED at an estimate of 25% or more of the monthly credits returns 409 estimated_cost_confirmation_required, with the estimate in error.details.estimate, and changes nothing, when it turns the agent on (a create defaults to ENABLED) or makes an enabled one run more often. Send it only after the person who pays has seen the estimate and agreed. Ignored everywhere else.

description
string | null
group_name
string | null
group_order
integer | null
integrations
Integrations · object | null
manual_minutes_per_run_override
number | null

Minutes of technician time one successful run saves, as your team measured it. A triggered CHAINED_ACTIONS workflow only; a whole number from 1 to 1440. null restores the default, the sum of each action's fixed minutes (manual_minutes_parts). On an update, omit the key to keep the stored value.

name
string | null
opportunity_conditions
PsaConditions · object | null

Trigger/entity filter for OPPORTUNITY agents (ConnectWise only) — which opportunities this agent fires on. Same shape as ticket_conditions; GET /public-api/schemas/opportunity lists the fields. An OPPORTUNITY agent filters with this field alone.

process_entities_sequentially
boolean | null
process_sub_entities_only_once
boolean | null
schedule
Schedule · object | null
show_in_psa_pod
boolean | null
state
enum<string> | null
Available options:
ENABLED,
DISABLED,
DELETED
ticket_conditions
PsaTicketConditions · object | null

Trigger/entity filter — 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). A TIME_ENTRY agent carries this field too — there it selects the tickets whose time entries are searched, and time_entry_conditions then selects the time entries on them. 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. A leaf's value is a single value; only the in and notIn operators take a list. A list under any other operator returns 400.

time_entry_conditions
PsaConditions · object | null

Trigger/entity filter for TIME_ENTRY agents — which time entries this agent fires on, on the tickets ticket_conditions selected. Same shape as ticket_conditions. Ignored for TICKET agents.

toolbox
Toolbox · object | null
trigger_type
enum<string> | null
Available options:
SCHEDULED,
TRIGGERED,
NONE

Response

Success.

data
Agent · object
required

Lean summary of a workflow row — the per-item shape GET /public-api/agents (list) returns. The single-resource GET /public-api/agents/<id> returns the richer AgentDetail; historical snapshots come back as AgentVersion.

meta
object
required