Skills
Drafts, validation, publishing, runs, automation, integration bindings.
Raw Markdown for agents: skills.md. MCP: read_guide("skills").
Skills are AI processes owned by the tenant. A Skill has an immutable active version (what runs) and an optional draft (the working copy). skills.get returns both. skills.rename changes the name at once, without a draft; everything else (summary, instructions, inputs, outputs, scope, integrations, scripts, settings) goes through the draft.
Flow
skills.createopens a draft, empty, with content, or from a template found withskills.search. Each user holds one unsaved draft per kind; a call with different content fails withskill_draft_exists.skills.draft.savereplaces the whole working copy. Start fromskills.getand send every field that should remain, changed or not; send the currentdraft_revisionasexpected_revision. The response carriesissues, the same listskills.validatereturns.skills.validatechecks the draft (or the published version when no draft exists) exactly like a publish and returnsvalidplus every issue withpath,code,message,hintandblocking. Fix every blocking issue with anotherskills.draft.save, then validate again.skills.integration_bindings.listwithskill_idandspace_idshows the integration requirements and which of them the caller has bound; before the first publish it reads them from the draft (source: "draft").skills.savepublishes: it validates again, stores a new immutable version, makes it active and clears the draft. MCP clients may call it; in the in-app assistant it asks the user for approval. Publish only when the user asked for it, never as a follow-up to a rename or a draft save; otherwise report that the draft is still unpublished.skills.integration_bindings.setbinds each requirement to one of the caller's own active Space grants. It needs a published version.skills.automation.setstores the triggers and the run request of the Skill in a Space. Active triggers need a published version.- Webhook test: while the webhook is inactive,
skills.automation.webhook.listencaptures the next authenticated delivery within 10 minutes instead of running it;skills.automation.getthen shows it underwebhook.sample.payload, the item a script for this trigger receives (body is the provider object).
When changing a published Skill the version already exists, so bindings and
automation can be set before the next skills.save.
Other operations: skills.draft.discard drops the working copy of a Skill
that has an active version (a never-published Skill is deleted with
skills.delete instead). skills.restore creates a new active version from
an earlier one and drops the draft; it asks the user for approval.
skills.versions lists versions. skills.delete is irreversible and fails
while runs exist.
Running: skills.runs.start starts one run in a Space with inputs and
documents; runs cost credits. skills.runs.get and skills.runs.events
(cursor) show status, progress and results; skills.runs.cancel stops a run.
skills.models lists the AI models a Skill may use.
Rules
instructionsandcapabilitiesare required for a publish.capabilitiesis explicit: list every capability the Skill uses. Known capabilities: browser, email, folders, inbox, integrations, items-output, pages, persistent-output, records, reviewer, sheet-output, web-search.- Records: a Skill that reads or writes records lists
recordsincapabilitiesand the record type ids inscope.recordTypeIds(records.types.listreturns them). Scripts reach only record types in that scope. stepsno longer exists and is rejected; write the order as numbered lines ininstructions.- The definition model is strict: unknown fields fail on every level. Inputs
need
key(lowercase letters, digits,_),type(textordocument) andlabel; outputs needkey,type(doc,sheet,pageoritems) andnameTemplate; keys are unique. Integrations needkey,providerKeyandoperationsof{resource, operation}; scripts needscriptId, an explicitversionand analias.entry: {"kind": "script", "alias"}needs that alias underscriptsand exactly one output of typeitems. - Field bindings under
integrations[].operations[].fieldsarefixed(value) orchoice(values); they are checked against the provider's operation contract. - Integration requirements are logical: the definition never stores grants or connections. Each user binds them personally per Space; unbound requirements block runs and script entries.
- Folder and document ids come from
documents.folders_listanddocuments.list; never invent ids. skills.automation.set: a run request (runwith scope, inputs or an instruction) needs at least one trigger after the call. Omitwebhookto keep the current one;nullremoves it.run.scope.record_type_idsneedsrecordsin the Skill's capabilities.- A publish checks: name and summary present, the definition model, field
bindings, the pinned scripts (they belong to the Skill, the version exists,
the static check passed, slots and operations match the Skill's
integrations, record types lie in the scope, every
@script:<alias>in the instructions is pinned) and the settings the platform offers (agentScriptsEnabled,subagentsEnabled,securityReviewEnabledneeds the plan feature). An outdated pinned version, a missing dry run and record types outside a declared record scope are hints and do not block. - Importing a Skill from another tool: map what the draft supports (instructions, inputs, outputs, scope, integration requirements, settings); ids, bindings and fields of the other system do not transfer. List in the answer what was left out.
Automation
Up to three triggers per Skill and Space: a schedule (kind rule, cron or
once; workflows.schedule.preview shows the next run times), a platform
event and an inbound webhook. Each trigger has an entry: {"kind": "agent"}
starts the Skill agent with the stored run request; {"kind": "script", "alias", "parameters", "escalate"} runs one pinned script without a model
call and, with escalate true, starts the agent only when the script fails or
returns exceptions. A script entry needs the caller's own integration
bindings. A webhook (auth none, secret_header, hmac_signature, or
integration with a provider such as Shopify in mode subscription over a
grant, or manual) receives as soon as it is configured and executes only
while active. The webhook secret is never shown in chat; the user reads it
in the Skill's automation panel. The result of skills.automation.set is the
effective automation, the same shape as skills.automation.get.
Errors and fixes
validation_errorfromskills.save:detailsmaps each definition path (for exampleoutputs.0.nameTemplate,capabilities,scripts.1) to its message. Callskills.validatefor the full list with hints, fix the draft, validate again.script_target_out_of_scope: a pinned script reads or writes record types the Skill cannot reach. Blocking whencapabilitieslacksrecordsandscope.recordTypeIdsis empty: add both. A hint when the record types are only missing fromscope.recordTypeIds: add their ids.skill_draft_exists(409) fromskills.create: you already hold an unsaved draft. Read it withskills.get(details.skill_id)and continue it withskills.draft.save, or delete it withskills.delete.draft_revision_conflict(409): the draft changed elsewhere. Read it again withskills.getand retry with the currentdraft_revision.automation_run_scope_requires_trigger:runcarries a scope, inputs or an instruction, but schedule, event and webhook are all missing. Send a trigger in the same call, or sendrunwith empty inputs and scope to remove the automation.skill_not_saved(409): the operation needs a published version. Callskills.savefirst.webhook_mode_invalid(409): the webhook's auth mode does not fit the call, for example a signing secret for a webhook that is not a manually registered provider webhook, or a secret for authnone.integration_grant_missing(indetails.webhook): a subscription webhook names no active grant of this Space. Create or pick one withintegrations.grants.listorintegrations.grants.createand pass its id aswebhook.integration.grant_id.integration_binding_required: a script entry or run needs the caller's bindings. Bind them withskills.integration_bindings.set.
Example
A Skill that receives Shopify orders by webhook and writes them to records
through a script. The script orders was created with scripts.create,
tested with a dry run and is pinned in version 3.
{
"name": "Order sync",
"summary": "Writes every new Shopify order into the Orders records.",
"instructions": "1. Run @script:orders with the delivered orders.\n2. When the script reports exceptions, check each order and explain what is missing.",
"capabilities": ["integrations", "items-output", "records"],
"scope": {"recordTypeIds": ["<orders record type id>"]},
"integrations": [
{
"key": "shop",
"providerKey": "shopify",
"operations": [{"resource": "orders", "operation": "get"}]
}
],
"outputs": [
{"key": "orders", "type": "items", "nameTemplate": "Synced orders"}
],
"scripts": [
{"scriptId": "<script id>", "version": 3, "alias": "orders"}
],
"entry": {"kind": "script", "alias": "orders", "escalate": true}
}Send it as the draft of skills.draft.save, check it with
skills.validate, publish with skills.save (the same fields, with name,
summary, categories and tags as separate arguments and the rest as
definition), bind shop with skills.integration_bindings.set, then:
{
"space_id": "<space id>",
"skill_id": "<skill id>",
"schedule": null,
"event": null,
"webhook": {
"auth": "integration",
"integration": {
"provider_key": "shopify",
"mode": "subscription",
"grant_id": "<grant id>",
"events": ["orders/create"]
},
"active": false,
"entry": {"kind": "script", "alias": "orders", "escalate": true}
},
"run": {"inputs": {}, "scope": {"record_type_ids": ["<orders record type id>"]}}
}Capture a test delivery with skills.automation.webhook.listen, check
webhook.sample.payload in skills.automation.get, and let the user switch
the webhook to active.