Creating Skills
Build and deploy your first skill
This guide walks you through creating, testing, and deploying a skill.
Step 1: Plan Your Skill
Define the Skill Purpose
Ask yourself:
- What problem does this skill solve?
- What inputs will it need?
- What output should it return?
- Is it informational, action-based, or interactive?
Example: Order Status Skill
Purpose: Check the status of a customer order
Inputs:
- Order ID (required)
- Customer ID (optional)
Output:
- Status message
- Current status
- Estimated delivery date
Type: Informational
Step 2: Define the Schema
Create the Skill Definition
const orderStatusSkill = {
slug: "check_order_status",
name: "Check Order Status",
description: "Look up the current status of a customer's order",
prompt: `Use this skill when a customer asks about their order status.
Takes an order ID and returns the current status (pending,
processing, shipped, delivered, or cancelled) along with
estimated delivery date.`,
parameters: {
type: "object",
properties: {
orderId: {
type: "string",
description: "The unique order identifier"
},
customerId: {
type: "string",
description: "Customer ID for verification (optional)"
}
},
required: ["orderId"]
},
response: {
type: "object",
properties: {
message: {
type: "string",
description: "Human-readable status message"
},
orderId: {
type: "string"
},
status: {
type: "string",
enum: ["pending", "processing", "shipped", "delivered", "cancelled"]
},
estimatedDelivery: {
type: "string",
description: "ISO 8601 date string"
},
trackingUrl: {
type: "string",
description: "URL to track the shipment"
}
},
required: ["message", "status"]
},
buttons: [
{
label: "Track Package",
payload: "TRACK_PACKAGE"
},
{
label: "Request Return",
payload: "REQUEST_RETURN"
}
]
}Step 3: Write the Handler Function
Basic Handler Structure
async function handler(input) {
const { config, parameters, context } = input
try {
// Extract parameters
const { orderId, customerId } = parameters
// Validate input
if (!orderId) {
return {
message: "Error: Order ID is required",
status: "error"
}
}
// Logic to fetch order status
// (This is where you'd call your API, database, etc.)
const orderStatus = await getOrderStatus(orderId)
// Return result
return {
message: `Your order ${orderId} is ${orderStatus.status}`,
orderId: orderId,
status: orderStatus.status,
estimatedDelivery: orderStatus.estimatedDelivery,
trackingUrl: orderStatus.trackingUrl
}
} catch (error) {
return {
message: `Error checking order status: ${error.message}`,
status: "error"
}
}
}Using Configuration
If your skill needs API credentials or configuration:
async function handler(input) {
const { config, parameters } = input
// Access configuration values
const apiKey = config.apiKey
const apiEndpoint = config.apiEndpoint
const timeout = config.timeout || 5000
try {
// Use config to call external service
const response = await fetch(`${apiEndpoint}/orders/${parameters.orderId}`, {
headers: {
'Authorization': `Bearer ${apiKey}`
},
timeout: timeout
})
const data = await response.json()
return {
message: `Order status: ${data.status}`,
...data
}
} catch (error) {
return {
message: `Failed to check order: ${error.message}`,
status: "error"
}
}
}Step 4: Define Configuration (If Needed)
If your skill needs configuration values:
const configDefinitions = [
{
key: "apiEndpoint",
label: "API Endpoint",
type: "STRING",
description: "Your API endpoint URL",
required: true,
order: 1
},
{
key: "apiKey",
label: "API Key",
type: "SECRET",
description: "Your API authentication key",
required: true,
isSensitive: true,
order: 2
},
{
key: "timeout",
label: "Request Timeout (ms)",
type: "NUMBER",
description: "Request timeout in milliseconds",
required: false,
defaultValue: "5000",
order: 3
}
]Step 5: Create the SkillSet
Combine all skills into a SkillSet:
const skillSet = {
name: "Order Management",
slug: "order_management",
tagline: "Order lookup and management skills",
description: "Skills for checking order status, processing returns, and managing shipments",
visibility: "PRIVATE",
skills: [
orderStatusSkill,
// ... other skills
],
configDefinitions: [
{
key: "apiEndpoint",
label: "API Endpoint",
type: "STRING",
required: true,
order: 1
},
{
key: "apiKey",
label: "API Key",
type: "SECRET",
required: true,
isSensitive: true,
order: 2
}
],
requiredIntegrationProviders: ["YOUR_API"] // If applicable
}Step 6: Test Your Skill
Testing Locally
Before deployment, test your skill logic:
// Test data
const testInput = {
config: {
apiEndpoint: "https://api.example.com",
apiKey: "test-key"
},
parameters: {
orderId: "ORD-12345"
},
context: {
conversationId: "conv_123",
organizationId: "org_123"
}
}
// Run handler
handler(testInput).then(result => {
console.log("Result:", result)
})Test Cases to Cover
- Valid input with all parameters
- Valid input with only required parameters
- Invalid/missing required parameters
- Edge cases (empty strings, null values, etc.)
- Error scenarios (API down, timeout, etc.)
- Boundary conditions
Step 7: Deploy the SkillSet
Via Dashboard
- Go to AI Settings > Skills
- Click Create New SkillSet
- Fill in basic information
- Add skills one by one
- Configure values if needed
- Deploy to make live
Via API
const response = await fetch('/api/skillsets', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
},
body: JSON.stringify(skillSet)
})
const created = await response.json()
console.log('SkillSet created:', created.id)Step 8: Connect to Your AI Persona
Once deployed, connect the SkillSet to your AI persona:
- Go to AI Settings > Personas
- Select the persona
- Go to Skills tab
- Enable the SkillSet
- Configure values (API key, endpoint, etc.)
- Test in conversation
Complete Example: Payment Processing Skill
// Skill definition
const processPaymentSkill = {
slug: "process_payment",
name: "Process Payment",
description: "Process a payment for a customer",
prompt: `Process a customer payment. Accepts amount and payment method.
Returns payment confirmation or error.`,
parameters: {
type: "object",
properties: {
amount: {
type: "number",
description: "Payment amount in dollars"
},
paymentMethod: {
type: "string",
enum: ["card", "bank", "crypto"],
description: "Payment method"
},
customerId: {
type: "string",
description: "Customer ID"
}
},
required: ["amount", "paymentMethod"]
},
response: {
type: "object",
properties: {
message: { type: "string" },
transactionId: { type: "string" },
status: { type: "string" },
amount: { type: "number" }
}
}
}
// Handler implementation
async function handler(input) {
const { config, parameters } = input
const { amount, paymentMethod, customerId } = parameters
try {
// Validate amount
if (amount <= 0) {
return {
message: "Error: Invalid amount",
status: "failed"
}
}
// Call payment processor
const response = await fetch(`${config.processorUrl}/charge`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${config.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
amount,
method: paymentMethod,
customerId
})
})
const result = await response.json()
if (!response.ok) {
return {
message: `Payment failed: ${result.error}`,
status: "failed"
}
}
return {
message: `Payment of $${amount} processed successfully`,
transactionId: result.transactionId,
status: "success",
amount
}
} catch (error) {
console.error('Payment error:', error)
return {
message: `System error: ${error.message}`,
status: "error"
}
}
}Troubleshooting
Handler Not Found
Error: "handler function not found"
Solution: Ensure your skill code includes:
async function handler(input) {
// ...
}Configuration Not Available
Error: config.apiKey is undefined
Solution:
- Check configuration is defined in SkillSet
- Ensure values are set in persona settings
- Verify config key matches the code
Skill Not Appearing in AI
Problem: Skill doesn't show up in conversation
Solutions:
- Confirm SkillSet is deployed
- Check persona has the SkillSet enabled
- Verify prompt is clear enough for AI to understand
- Test with explicit requests ("use the check order status skill")
Timeouts
Problem: Skill execution timing out
Solutions:
- Reduce external API call timeout
- Optimize database queries
- Use caching for common requests
- Increase Cloudflare worker timeout
Next Steps
- Skill Best Practices
- Monitoring Skills (coming soon)
- Advanced Patterns (coming soon)