Skip to main content

Skill Goals

Beta

Skill goals are part of the Conversational Agents Beta.

A goal turns a skill into a guided task: the agent collects a defined set of answers, SendSeven validates each answer, saves the valid ones to the contact, and runs your completion actions. Typical uses: qualify a lead, take a callback request, register a return, collect feedback.

  • The goal is stored on the skill version. Changing it creates a new version, and pinned agents keep their old goal.
  • Validation is done by SendSeven, not by the model. The agent submits answers; only answers that pass your rules are accepted and saved. The model never writes contact fields directly.
  • Goals work on every surface: channels, Live Chat, email, Flows and the test console.

Add a goal to a skill​

Send goal when you create or update a skill (or in a skill import):

{
"slug": "qualify-lead",
"title": "Qualify a lead",
"description": "Use when a visitor asks about pricing, a demo or buying for a team.",
"default_mode": "model_selected",
"body": "Be friendly and brief. Ask one question at a time.",
"goal": {
"objective": "Find out whether the visitor is a good fit for a sales call.",
"completion_criteria": "We know the company size, the budget and the email address.",
"outputs": [
{"key": "company_size", "label": "Company size", "type": "select", "options": ["1-10", "11-50", "51-200", "200+"],
"bind": {"kind": "custom_field", "field_id": "CUSTOM_FIELD_ID"}},
{"key": "budget", "label": "Monthly budget (EUR)", "type": "number", "constraints": {"min": 0, "max": 1000000}},
{"key": "email", "bind": {"kind": "contact_standard", "field": "email"}},
{"key": "website", "type": "url", "required": false}
],
"on_max_attempts": "handoff",
"actions": {
"save_contact_fields": {"enabled": true, "overwrite_existing": false},
"assign": {"strategy": "round_robin_by_tag", "tag_id": "TAG_ID_SALES", "add_tag_ids": ["TAG_ID_LEAD"], "prefer_online": true},
"webhook": {"enabled": true},
"after": {"type": "none"}
}
}
}

Send "goal": null on PATCH to remove the goal; omit it to keep the current one.

Goal fields​

FieldNotes
objectiveRequired. What the agent should achieve. 1-2,000 characters.
completion_criteriaOptional. When the goal counts as done. Max 2,000.
outputsUp to 20 answers to collect. Can be empty for an objective-only goal.
on_max_attemptsWhat happens when an answer is still invalid after max_attempts: handoff (default) hands over to a human, skip leaves it empty and moves on.
actionsCompletion actions.

Outputs​

FieldNotes
keyRequired. ^[A-Za-z][A-Za-z0-9_]{0,63}$. Unique (case-insensitive). The key in webhooks and Flow variables.
labelMax 100.
descriptionMax 500. Help for the agent, for example what to ask.
requiredDefault true.
typetext, number, integer, boolean, date, datetime, email, phone, url, select, multiselect. Bound outputs take the type of their field; an unbound output without a type is text.
optionsFor select and multiselect. Up to 100 non-empty strings, max 255 characters each.
bindWhere the answer is saved. See Binding.
constraintsExtra validation rules. See Constraints.
max_attempts1..5, default 3. Invalid answers allowed before on_max_attempts applies.

Binding to contact fields​

bindSaves to
{"kind": "contact_standard", "field": "…"}A standard contact field: first_name, last_name, name, language (text), birthday (date), email, phone
{"kind": "custom_field", "field_id": "…"}An active custom field in your workspace
  • A bound output uses the field's type. Declaring a different type returns type_mismatch.
  • One field can be bound to only one output.
  • A bound select/multiselect output may restrict options to a subset of the field's options.
  • Location and collection custom fields cannot be bound.
  • The custom field's own validation rules always apply.

Constraints​

KeyTypes
min, maxnumber, integer
min_length, max_length (0..10000)text, email, phone, url
pattern (1-200 characters, a safe regular expression)text, email, phone, url
date_min, date_max (ISO date or "today")date, datetime

boolean, select and multiselect take no constraints. For a bound output, constraints may only tighten the field's own rules: a wider range, a longer maximum or a different pattern returns constraint_loosens.

How the agent works with a goal​

  1. The goal becomes active when its skill is loaded (by the classifier, by load_skill, as a Flow skill, or as an always-on skill).
  2. The agent asks for the answers and submits them with submit_goal_fields. Each answer is normalised and validated; invalid answers come back with an error code (for example too_small or invalid_email) so the agent can ask again.
  3. The agent finishes with complete_goal. This succeeds only when every required output was accepted or skipped. A goal without outputs can be completed after at least 1 contact message following activation, or at least 2 when the goal has completion actions.
  4. The completing turn has the outcome goal_completed (task_completed inside a Flow), and the completion actions run.

More rules:

  • One active goal at a time. Loading another skill with a goal pauses the current one. Up to 3 paused goals are kept and resume when their skill is loaded again.
  • The active goal's skill stays loaded on the following turns, unless the conversation moves to another topic.
  • Goal tools are free and get 2 extra tool calls on top of max_tool_calls.
  • Goal progress is kept per conversation. In the test console it travels in the state you send back (key _agent_goal).

complete_goal errors the agent may receive: no_active_goal, goal_unavailable, required_fields_missing (with remaining_required), too_early.

Completion actions​

Actions run once per goal activation, in live conversations only, in this order:

ActionFieldsEffect
save_contact_fieldsenabled (default true), overwrite_existing (default false)Saves the accepted answers to their bound fields. With overwrite_existing: false, a field that already has a value is left alone.
assignstrategy, tag_id, add_tag_ids (max 10), prefer_online (default true)strategy: none (default), round_robin_all, least_busy_all, round_robin_by_tag, least_busy_by_tag (tag_id required). Assigns an unassigned conversation; an assigned one is never reassigned. add_tag_ids are added to the conversation.
webhookenabled (default true)Sends agent.goal_completed.
aftertype: none (default), handoff or start_flow; flow_id; variable_prefix (default goal, ^[A-Za-z][A-Za-z0-9_]{0,31}$)handoff hands the conversation to a human (needs escalation on; with a *_by_tag strategy the assign.tag_id is the hand-off tag). start_flow starts a published, manually startable Flow for the conversation with the answers in {{vars.<variable_prefix>.<key>}}.

Inside a Flow, actions run too, but after is skipped: the Flow continues on the Run AI Assistant node's done branch instead. See Agents in Flows.

In the test console nothing is executed. The engine_trace contains a goal_actions_dry_run step whose would_execute lists the fields that would be saved, the assignment, the webhook and the after action.

agent.goal_completed webhook​

Subscribe your webhook endpoint to agent.goal_completed (see Webhook setup). The event uses the standard envelope:

{
"id": "evt_4f9c2a7b1d3e5f60",
"type": "agent.goal_completed",
"created_at": "2026-10-05T10:00:01Z",
"tenant_id": "…",
"event_id": "6f1c1d1e-2b3a-4c5d-8e9f-0a1b2c3d4e5f",
"data": {
"agent": {"id": "…", "name": "Sales Assistant"},
"skill": {"id": "…", "slug": "qualify-lead", "version": 3},
"conversation_id": "…",
"contact_id": "…",
"channel_type": "whatsapp",
"outputs": {"company_size": "11-50", "budget": 5000, "email": "[email protected]", "website": null},
"saved_fields": ["company_size", "email"],
"completed_at": "2026-10-05T10:00:00Z",
"flow_run_id": null
}
}
  • event_id identifies the goal activation and is the same on every delivery attempt. Use it to de-duplicate.
  • outputs has at most 20 keys. Skipped optional answers are null. Text is cut at 4,000 characters and lists at 50 items.
  • Full reference: Webhook events — agent.goal_completed.

Statistics​

GET /automation/skills/{skill_id}/goal-stats?days=30 returns started, completed and handed-off goals, the completion rate and a per-version breakdown. See Skills API — Goal statistics.

Errors​

An invalid goal is rejected with 422:

{
"detail": {
"code": "invalid_goal",
"message": "The skill goal is invalid",
"errors": [
{"path": "outputs[1].constraints.max", "code": "constraint_loosens", "message": "…"}
]
}
}

Up to 50 errors are returned, each with the path of the problem.

codeMeaning
invalid, invalid_type, invalid_valueMalformed goal, wrong type or value
options_required, duplicate_option, options_not_applicable, options_not_subsetProblems with options
constraint_not_applicable, min_greater_than_max, constraint_loosensProblems with constraints
unknown_standard_field, type_mismatch, duplicate_bind, unsupported_field_typeProblems with bind
custom_field_not_found, custom_field_inactiveThe bound custom field does not exist or is inactive
tag_not_foundassign.tag_id or an add_tag_ids entry is not in your workspace
flow_not_found, flow_not_published, flow_not_manually_startableProblems with after.flow_id

Answer validation codes the agent can receive: required, not_in_options, not_a_number, not_an_integer, too_small, too_large, too_short, too_long, pattern_mismatch, invalid_email, invalid_phone, invalid_url, invalid_date, date_too_early, date_too_late, invalid_boolean, invalid_language, unsupported.