Skip to main content

Drafts & Publishing

Beta

Conversational Agents are in Beta and available on Professional, Scale and Enterprise.

Every agent has a live configuration (what contacts talk to) and at most one draft. Changes in the agent editor go to the draft. Contacts keep talking to the live configuration until you publish.

Base URL: https://api.sendseven.com/api/v1/automation

MethodPathScopeSuccess
GET/agents/{agent_id}/draftautomation:read200 draft
PUT/agents/{agent_id}/draftautomation:update200 draft
DELETE/agents/{agent_id}/draftautomation:update200 draft (now equal to live)
POST/agents/{agent_id}/publishautomation:update200 agent
POST/agents/{agent_id}/avatarautomation:update200 draft
GET/agents/insights/overview?days=30automation:read200 — see Insights overview
The regular API stays live

PATCH /agents/{agent_id}, PUT /agents/{agent_id}/skills and the knowledge source endpoints still change the live agent immediately, exactly as before. Use them for automation; use the draft endpoints when you want a review step. A pending draft never overwrites those live changes when it is published (see Merge rules).

Publish state​

Every agent response carries:

FieldNotes
publish_statepublished or draft. draft means the agent has never been published, so it does not answer contacts yet. Agents created before drafts existed are published.
has_unpublished_changestrue while a draft with changes exists.
draft_updated_atWhen the draft was last saved, or null.
published_atWhen the agent was last published, or null.

POST /agents publishes immediately by default. Send "publish": false to create a never-published agent that you finish and publish later:

{"name": "Shop Support", "instructions": "Answer questions about orders.", "publish": false}

is_active and the bot priority are not part of the draft: they always apply instantly.

The draft document​

GET /agents/{agent_id}/draft returns:

{
"agent_id": "…",
"bot_id": "…",
"document": {"name": "Shop Support", "instructions": "…", "skills": [], "knowledge_sources": [], "activation_rules": [], "…": "…"},
"live_document": {"…": "…"},
"publish_state": "published",
"has_unpublished_changes": true,
"draft_updated_at": "2026-10-06T09:00:00Z",
"draft_updated_by_name": "Alex Example",
"published_at": "2026-10-01T12:00:00Z",
"published_by_name": "Alex Example",
"changes": [{"section": "instructions", "fields": ["instructions"]}],
"validation": {"errors": [], "warnings": []},
"avatar_display_url": "https://…",
"widget_connections": [{"widget_id": "…", "name": "Website chat", "widget_type": "live_chat"}]
}
FieldNotes
documentThe full editable configuration: the draft when one exists, otherwise the live configuration. It accepts the same fields as Create an agent plus name, avatar_url, the greeting button fields, max_credits_per_conversation, skills, knowledge_sources and activation_rules (channels and schedule).
live_documentThe live configuration, only while there are changes (for a side-by-side view). Otherwise null.
changesChanged fields grouped by editor section: identity, instructions, skills, knowledge, capabilities, handoff, channels, settings.
validation.errorsWhat would block publishing right now (same codes as Publish blockers).
avatar_display_urlA short-lived URL to show the draft's avatar. Do not store it.
widget_connectionsActive widgets that use this agent directly. Informational.

Without a draft, GET returns the live configuration with has_unpublished_changes: false. It never creates a draft.

Save the draft​

PUT /agents/{agent_id}/draft with {"document": {…}}.

  • Send only what you change. Fields you leave out keep their draft (or live) value. Lists (skills, knowledge_sources, activation_rules) are replaced as a whole when sent. A skill link without sources keeps its existing per-skill source override.
  • New list items get a server id. Omit id (or send any unknown value) on a new knowledge source or activation rule. The response contains the id the server assigned; publishing creates the item with that id.
  • No change, no draft. If the saved draft ends up identical to the live configuration, the draft is removed and has_unpublished_changes is false. Sending back the document from GET unchanged is always a no-op.
  • Unknown fields are rejected (422). The same checks as the live endpoints apply (skills, tags, KB folders, live source URLs and limits). A blank name is allowed in a draft but blocks publishing.
  • avatar_url cannot be set to a new value here (422 invalid_avatar_url); use the avatar upload. Sending the current value or null (remove) is fine.
  • A draft is limited to 1 MB (422 draft_too_large).

DELETE /agents/{agent_id}/draft throws the draft away and returns the live configuration.

Test the draft​

POST /agents/{agent_id}/test accepts "config": "draft" to run the turn on the draft instead of the live configuration (default "live"). The response echoes which one ran in config. Without a draft the live configuration is used and config is live. See Test console.

Publish​

POST /agents/{agent_id}/publish makes the draft live in one step and returns the updated agent. If there is no draft, it only marks a never-published agent as published.

Merge rules​

The draft remembers the live configuration it started from. On save and on publish, SendSeven combines three versions: that starting point, your draft and the current live configuration.

  • A field you changed in the draft wins.
  • A field you did not change keeps its current live value, even if someone changed it through the API after the draft was started.
  • Settings objects (tools_config, handoff_config, language_settings, …) are combined key by key.
  • Lists are combined item by item (skills by skill_id, sources and rules by id): items you added or removed in the draft are added or removed; items added live in the meantime are kept.

Publish blockers​

If the draft cannot go live, nothing is changed and the response is:

{
"detail": {
"code": "publish_blocked",
"message": "The agent cannot be published yet",
"errors": [
{"section": "identity", "field": "ai_disclosure_message", "code": "ai_disclosure_missing"}
]
}
}
codeMeaning
name_missingThe agent has no name.
ai_disclosure_missingThe AI notice is set but has no text (only placeholders or spaces). Leave it empty to use the built-in notice.
live_source_invalidA live source is incomplete or its URL is not allowed.
skill_missingA linked skill was deleted or archived.
goal_invalidA linked skill's goal is invalid.
handoff_schedule_emptyHand-off hours are custom with the schedule switched on but no time ranges (section handoff). See Team availability.

Avatar​

POST /agents/{agent_id}/avatar — multipart/form-data with one file. The image goes into the draft; it is shown to contacts after you publish.

  • PNG, JPEG or WebP only, max 2 MB. The file content must match its type; SVG is not accepted.
  • Errors: 422 {"code": "invalid_avatar", "message": "…"}.

Returns the draft (document.avatar_url and avatar_display_url set).

Insights overview​

GET /agents/insights/overview?days=30 — workspace-wide numbers for all agents, or one agent with &agent_id=AGENT_ID. days is 7, 30 (default) or 90; anything else returns 422 invalid_days. Test-console turns are not counted.

{
"range_days": 30,
"goals": [
{"skill_id": "…", "skill_title": "Book a demo", "goal_label": "Collect name, company and preferred date", "started": 42, "completed": 30, "completion_pct": 71.4}
],
"skills_by_usage": [
{"skill_id": "…", "title": "Order returns", "turns": 310, "agents": 2}
],
"kb_gaps": {"pending_count": 5},
"truncated": false
}
FieldNotes
goalsPer skill with a goal: goal runs started and completed in the window. completion_pct is null when nothing started.
skills_by_usageReplies that used the skill and how many agents used it.
kb_gaps.pending_countKnowledge-gap FAQ drafts waiting for review.
truncatedtrue when the window had more than 20,000 replies; only the newest were counted.

Per-agent conversation, hand-off and credit totals are on /hub/stats and /agents/{agent_id}/analytics.