Documentation
Configuration
The manifest is the contract between your product and Duct. It tells the shell what your product can do, what requires auth, what is safe for agents, and where users can be navigated.
Minimal Manifest
import { defineDuctConfig } from '@duct-sdk/sdk';
Top-Level Sections
Agent page context (pageResolver.actionId)
When a deeplink declarespageResolver, agent page-context resolution requires pageResolver.actionId referencing a manifest action. Raw endpoint-only resolvers are skipped for agents (fail closed). Human shell chat may still use endpoint resolvers where configured.Widget appearance
Theme, size preset, welcome text, placeholder, and accent color can be set in duct.config.ts → widget or saved from Dashboard → Playground → Customize. Whichever you update most recently wins on the live shell. Agent-loop limits (maxToolIterations, maxPlanSteps, compactionTurns) still follow your manifest push when provided.
Playground vs embedded shell
Dashboard playground Human mode keeps its own conversation and session on your browser, separate from the shell embedded on your product — even when both use the same shell ID. Playground turns appear in Audit → Conversations with a Playground caller badge. Use New chat in either surface to start fresh.Product fields
Auth fields
The auth block controls how the embed requests delegated user tokens and where unauthenticated guests are sent when they ask for protected data.
Action security fields
Every action declares agentAccessible, sideEffects, sensitivityLevel, and requiresAuth. They are enforced before Duct calls your API — for human chat, Agent API, and cross-shell callers.
Choosing values & troubleshooting
Field reference, caller-by-caller enforcement, scenario checklist, and error codes: Action Permissions.requiresAuth and programmatic callers
Whether the action needs an authenticated end-user on your API. For programmatic callers, Duct blocks the call unless user_consent_token is present. Company backends typically pass duct_delegated minted server-side; partner agents use uct_ or company OAuth delegation. Your API must accept token types each caller can actually obtain — not only session JWTs from your login form.
requiresAuth and programmatic callers
Human shell chat can work while partner agents get403 if your middleware only accepts duct_delegated from a logged-in browser session. Internal backends on your servers can mint duct_delegated after login — partner agents need uct_ or OAuth. See partner agents and user identity.Action chaining (dependsOn)
Declare hard ordering between actions with dependsOn on the consumer action. Each entry names a prerequisite action and optional paramMapping from the dependency's response fields into params for the dependent step.
{The human shell expands the DAG when the router picks a dependent action. POST /v1/shells/:id/message does the same — prerequisites run in order inside one orchestration turn. Use POST /invoke when your agent already has all params.
Field priority between steps
When forwarding output from step A into step B: (1)paramMappings on action A, else (2) dependsOn.paramMapping on action B, else (3) auto-extract scalar *_id fields. Human shell and Agent API use the same order.Manifest Versions & Side-Effect Changes
Every npx @duct-sdk/sdk push (and every rollback or promote) increments manifest_version. After activation, enforcement uses the current live manifest — cache invalidation runs on push, and versioned cache keys mitigate concurrent stale refills — so updated permissions apply promptly under normal conditions. Permission and consent checks always evaluate the current version, not the version in effect when a chat session started.
Adding sideEffects to an existing action
Fresh consent is required on the next invocation. If an action previously hadsideEffects: false and you push a manifest where it is true, the runtime treats it as a side-effect action from that point forward. Earlier read-only executions in the same conversation do not carry over as approval for the mutation.Human chat (ConfirmCard)
The next time the shell invokes that action, the user sees a ConfirmCard and must approve before anything reaches your API. Each side-effect call requires its own confirmation — there is no blanket “user already consented in this session” bypass for mutations.
While a confirmation card, in-chat form, or multiple-choice clarification is showing, the message input is disabled until the user responds on that card — so accidental typing cannot interrupt an approval or disambiguation step.
Agent API
Side-effect actions return 202 confirmation_required with a short-lived execution_token on every call, even when a valid X-Duct-User-Consent header is present. A user consent token authorizes the agent to request execution; it does not auto-execute irreversible work. If the action newly requires side-effect handling, agents without a consent token receive 403 until the user approves through your consent flow.
Confirmation approval mode
Set DUCT_APPROVAL_MODE on the platform stack. Default operator_session requires a signed-in dashboard operator to approve side-effect confirmations. Set link_holder to allow independent agents to forward approval_url to a human browser — this proves channel possession, not verified human identity (optional CAPTCHA via DUCT_APPROVAL_CAPTCHA_*). Legacy DUCT_STRICT_OPERATOR_APPROVAL=true maps to operator_session.
User consent after a manifest change
Consent is bound to what the user approved — not just the action name. If you change an action's method, path, sideEffects, or sensitivityLevel and push, existing user_consent_token values no longer cover that action. The next invoke returns 409 until the user re-approves through your consent flow. Tokens that still match the current definition remain valid until expiry (24 hours) or explicit revocation.
Side-effect confirmation is separate: even with a valid consent token, agents still receive 202 confirmation_required on each mutating call.
Agent tokens after a push
Agent access profiles exchange for short-lived tokens tied to the manifest version at exchange time. After push, rollback, or promote, invoke may return 409 with MANIFEST_STALE until the agent re-exchanges at /v1/agent-access-token. See agent access after a push.
Session notice
Chat sessions started before a push may show a soft “shell configuration updated” banner. Enforcement still uses the latest manifest; the banner is a UX hint that action schemas may have changed mid-conversation.
Config files are TypeScript today
The CLI config file is TypeScript so it can be type-checked and autocompleted. That does not require your app backend to be TypeScript. Python, Ruby, Go, Java, and other stacks can still expose HTTP endpoints and use the REST API.What Is Not In duct.config.ts
The manifest controls the shell's capabilities. A few settings live outside it and are managed elsewhere: