Authoring templates
Build one OAgent. Deploy it to every customer with one command.
Why templates
An agency typically sells the same OAgent to many customers: an SDR for SaaS startups, an SEO manager for e-commerce, a support triage bot. Templates let you prototype one OAgent on your own account, snapshot it into a reusable definition, and deploy it to each customer with a single command. Parameters (founder name, domain, brand color) are passed at deploy time so each customer gets a personalized copy.
POST /api/templates, or “Save as template” on an agent card) is open to every account, and each one is private to the account that owns it. Forking another template is restricted to agency or @getoya.ai accounts. Deploying is open to all: an agency deploys into a customer, everyone else into their own workspace from /templates.Lifecycle
The full happy path:
# 1. Prototype the agent on your own account
oya account use --clear # operate as yourself, not a customer
# … build the agent in the web app or via CLI …
# 2. Snapshot the agent into a new template
oya template create \
--name "Acme SDR" \
--category sales \
--display-name "AI SDR for SaaS" \
--from-agent agt_abc123
# 3. (Optional) edit the template content in $EDITOR
oya template edit acme-sdr
# 4. Pick a customer and deploy
oya account use acct_8f3c…
oya template deploy acme-sdr \
--input founder_name="Alex" \
--input domain="acme.com" \
--name "Acme Sales Bot"
# 5. Wire gateways and deploy
oya agent gateway connect <agent_id> slack
oya agent deploy <agent_id>create --from-agent
The fastest way to author a template: point at an existing OAgent and Oya copies its soul (persona, mission, brand, behavior_rules, welcome_message), every skill attached to it (by skill_id), and every routine (name, prompt, schedule).
oya template create \
--name "Acme SDR" \
--from-agent agt_abc123 \
--include-kb # optional, embeds KB entries into the templateFlags
- --name: slugified into the template ID. Required only when neither --from-template nor --from-agent supplies one. Collisions get a numeric suffix.
- --display-name: pretty name shown in the gallery. Defaults to --name.
- --description, --category, --icon, --color: gallery metadata.
- --from-template <path|url|->: load a YAML template (path, http(s) URL, or - for stdin).
- --from-agent <id>: copy soul + skill_ids + routines from an existing OAgent.
- --include-kb: with --from-agent, also embed the OAgent's KB entries (large). Note: KB embedding is preserved in content but not yet auto-replayed on deploy. Customers upload KB after deploy for v1.
- --restrict-domain example.com: limit who can see the template (repeatable).
create --from-template
For full control over the schema, author a YAML file and load it directly. The shape matches the 12 built-in templates at backend/app/templates/catalog/<id>/TEMPLATE.yaml, the same files the Claude Code skill ships as references.
# From a file
oya template create --from-template ./acme-sdr.yaml
# From stdin
cat acme-sdr.yaml | oya template create --from-template -
# From a public URL (gist, GitHub raw, etc.)
oya template create --from-template https://gist.githubusercontent.com/you/.../acme-sdr.yamlThe canonical YAML schema:
id: acme-sdr
display_name: "Acme SDR"
tagline: "Sends 300 personalized cold emails per day"
description: |
Runs a real outbound motion every weekday for SaaS startups…
icon: target
category: sales
color: "#f59e0b"
soul:
name: "Acme SDR"
chat_model: "gemini/gemini-2.5-flash"
persona: |
You are an experienced Sales Development Representative…
behavior_rules:
- "Always personalize outreach with company-specific context."
- "Research the lead's company before drafting any message."
welcome_message: |
I'm your AI SDR. Tell me about your ICP and I'll start sourcing leads.
skills:
- web-search
- fetch-url
- brevo
- gmail-read
- memory
gateways_required:
- platform: apollo
label: "Apollo.io"
description: "Search leads and enrich contact data"
deploy_inputs:
- field: company_name
label: "Company Name"
type: text
required: true
placeholder: "Acme Corp"
knowledge_base:
- filename: "outreach-playbook.md"
content: |
# SDR Outreach Playbook
## Email Structure
…
routines:
- name: "Daily Lead Search"
schedule_cron: "0 9 * * 1-5"
schedule_human: "every weekday at 9am"
prompt: |
Populate today's raw candidate pool…Read ~/.claude/skills/oya/templates/ai-sdr.yaml, then mirror its shape. Every key you see in the example is supported; keys you don't see are not.fork & edit
Built-in templates (the curated gallery shipped with Oya) are read-only. To customize one, fork it into your account first.
# Fork a built-in
oya template fork ai-sdr --name "Acme SDR"
# → forked_from: ai-sdr
# → new id: acme-sdr
# Edit in $EDITOR (YAML round-trip)
oya template edit acme-sdr
# Print the current YAML (e.g. for piping into git)
oya template get acme-sdrdeploy
oya template deploy <template_id> creates a new OAgent owned by the target account, applies the template's soul, attaches every skill the template references, and installs every routine.
oya template deploy acme-sdr \
--account acct_8f3c… \
--name "Acme Sales Bot" \
--input founder_name="Alex" \
--input domain="acme.com"Deploy target resolution
The deploy target (which account owns the new OAgent) resolves in this order:
- 1. The --account flag, if passed.
- 2. The active target from `oya account use <id>`, if pinned.
- 3. Your own account.
deploy_inputs
--input KEY=VALUE values are persisted on the new OAgent's config.deploy_inputs map. Skill scripts and routines that reference oya_runtime.config() can read them at runtime. Full string interpolation in soul / skills / routines text is a v2 follow-up. Today, deploy_inputs are passed to scripts but not auto-substituted into soul copy.
Visibility restrictions
Set --restrict-domain on create (or edit it later) to limit a template to users from specific email domains. Useful when a template carries customer-specific copy or branded assets.
oya template create \
--name "Acme Internal" \
--from-agent agt_internal \
--restrict-domain acme.com \
--restrict-domain acme-agency.com