ai-actions-skill
AI Actions Skill for Freshworks Platform 3.0
You are an AI Actions specialist for Freshworks Platform 3.0.
Core Rules
- NEVER assume API endpoints - ALWAYS check the third-party API documentation before constructing request templates
- Request schemas MUST be flat - NO nested objects, NO arrays - ZERO TOLERANCE
- Response schemas CAN be nested - Include only essential fields
- Function names MUST match exactly - Case-sensitive between actions.json and server.js
- Construct nested structures in server.js - NOT in request schemas
- Use request templates - For all external API calls
- Validate before finalizing - Run
fdk validateand test with FDK test server
App Architecture
ai-actions-app/
├── actions.json # Action schemas (flat request, nested response)
├── server/
│ ├── server.js # SMI functions (flat→nested transformation)
│ └── test_data/
│ └── actionName.json # Test payloads
├── config/
│ ├── requests.json # External API templates
│ └── iparams.json # Installation parameters
└── manifest.json # Platform 3.0 manifest (declares requests)
What are AI Actions?
Concept: A declared function catalog exposed via actions.json where each action has:
- A unique function name
- Input JSON Schema (request parameters)
- Response JSON Schema (output structure)
- A backend callback (SMI function) with the SAME name that executes the action
Purpose: Makes tool/function calling predictable for UIs, assistants, and automations that can:
- Introspect available actions
- Validate inputs against schemas
- Handle typed responses
Invocation: Actions are triggered by:
- Platform events (e.g., onTicketCreate)
- Workflow automator
- Frontend app components
- AI assistants
Supported Products:
- ✅ Freshdesk
- ✅ Freshchat
- ✅ Freshworks CRM
- ✅ Freshcaller
File Structure
AI Actions require these files:
app-root/
├── actions.json # Action definitions (schemas, parameters, responses)
├── server/
│ ├── server.js # SMI function implementations
│ └── test_data/
│ └── actionName.json # Test payload for FDK testing
├── config/
│ ├── requests.json # Request templates for external APIs
│ └── iparams.json # Installation parameters
└── manifest.json # Platform 3.0 manifest (from freshworks_app_dev_skill)
CRITICAL: The function name in actions.json MUST match the function name in server.js (case-sensitive).
Skeleton Template: See ai-actions-skeleton/ folder in this skill for generic templates.
For complete app structure: Use freshworks-app-dev-skill for manifest.json, .fdk/, and other app files.
Quick Reference
actions.json Template
{
"functionName": {
"display_name": "Human Readable Name",
"description": "What this action does",
"parameters": {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"field1": { "type": "string", "description": "Field description" },
"field2": { "type": "number", "description": "Field description" }
},
"required": ["field1"]
},
"response": {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"id": { "type": "string", "description": "Resource ID" },
"data": {
"type": "object",
"properties": {
"nested": { "type": "string" }
}
}
},
"required": ["id"]
}
}
}
server.js Template
exports = {
functionName: async function(args) {
const { field1, field2 } = args;
try {
const response = await $request.invokeTemplate('requestTemplate', {
context: {},
body: JSON.stringify({ field1, field2 })
});
return renderData(null, {
data: {
response: JSON.parse(response.response)
}
});
} catch (error) {
return renderData({
status: error.status || 500,
message: error.message || 'Action failed'
});
}
}
};
Critical Constraints
Request Schema Rules (ZERO TOLERANCE)
❌ NEVER ALLOWED:
- Nested objects in request schema
- Arrays in request schema
- Object type in request schema
- Array type in request schema
✅ ALWAYS ALLOWED:
- Flat structure only (single level)
- Supported types:
string,number,boolean,integer
❌ WRONG - Nested Object in Request Schema
{
"createContact": {
"parameters": {
"type": "object",
"properties": {
"contact": {
"type": "object", // ❌ INVALID - nested object
"properties": {
"name": { "type": "string" },
"email": { "type": "string" }
}
}
}
}
}
}
❌ WRONG - Array in Request Schema
{
"bulkCreateContacts": {
"parameters": {
"type": "object",
"properties": {
"contacts": {
"type": "array", // ❌ INVALID - array not allowed
"items": {
"type": "object"
}
}
}
}
}
}
✅ CORRECT - Flat Request Schema
{
"createContact": {
"display_name": "Create Contact",
"description": "Creates a new contact in the system",
"parameters": {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Contact name"
},
"email": {
"type": "string",
"description": "Contact email address"
},
"phone": {
"type": "string",
"description": "Contact phone number"
}
},
"required": ["name", "email"]
}
}
}
Handling Nested Objects/Arrays
Problem: API requires nested object or array, but request schema must be flat.
Solution: Construct nested structures in server.js SMI function.
Example: API Requires Nested Object
API Requirement:
POST /api/contacts
{
"contact": {
"name": "John Doe",
"email": "john@example.com"
}
}
Step 1: Flat Request Schema in actions.json
{
"createContact": {
"parameters": {
"type": "object",
"properties": {
"name": { "type": "string" },
"email": { "type": "string" }
},
"required": ["name", "email"]
}
}
}
Step 2: Construct Nested Object in server.js
exports = {
createContact: async function(args) {
const { name, email } = args;
// Construct nested object for API
const requestBody = {
contact: {
name: name,
email: email
}
};
try {
const response = await $request.invokeTemplate('createContactRequest', {
context: {},
body: JSON.stringify(requestBody)
});
return renderData(null, {
data: {
response: response.response
}
});
} catch (error) {
return renderData({ status: 500, message: error.message });
}
}
};
Response Schema Rules
Different from Request: Response schemas CAN be nested and CAN contain arrays.
Best Practices:
-
Include Only Essential Fields
- ✅ Include only fields that will be used by other actions or workflows
- ❌ DO NOT generate schemas for the entire API response
-
Nested Objects ARE Allowed
- ✅ Response schemas CAN support nested objects
- ✅ Response schemas CAN support multiple layers
-
Field Optionality
- ✅ Make response fields optional (nullable) by default
- ✅ Only mark IDs as required (they must always be present)
-
Arrays in Responses
- ✅ Arrays ARE allowed in response schemas
- ✅ Use arrays for list responses
✅ CORRECT - Nested Response Schema
{
"getContact": {
"response": {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Contact ID"
},
"contact": {
"type": "object",
"description": "Contact details",
"properties": {
"name": { "type": "string" },
"email": { "type": "string" },
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" }
}
}
}
}
},
"required": ["id"]
}
}
}
Function Naming Rules
CRITICAL: Function names must follow strict naming conventions.
✅ Valid Function Names:
- Case-sensitive:
createContact≠CreateContact - Alphanumeric:
[a-z],[A-Z],[0-9] - Underscores allowed:
create_contact,get_ticket_status - Length: 2 to 40 characters
- Must NOT start with number: ❌
1createContact - Must NOT contain spaces: ❌
create contact
✅ CORRECT Examples:
createContact
getTicketStatus
update_user_profile
fetchData123
syncContacts_v2
❌ WRONG Examples:
1createContact // ❌ Starts with number
create contact // ❌ Contains space
create-contact // ❌ Contains hyphen (use underscore)
c // ❌ Too short (< 2 chars)
SMI Function Implementation
Complete Template
exports = {
functionName: async function(args) {
// Extract parameters from args
const { param1, param2 } = args;
// Construct request body (if API requires nested structure)
const requestBody = {
// Transform flat parameters to API-required structure
nested: {
param1: param1,
param2: param2
}
};
try {
// Call external API using request template
const response = await $request.invokeTemplate('apiRequestTemplate', {
context: {},
body: JSON.stringify(requestBody)
});
// Parse response if needed
const parsedResponse = JSON.parse(response.response);
// Return success response
return renderData(null, {
data: {
response: parsedResponse,
response_variables: {
variable1: parsedResponse.id,
variable2: parsedResponse.status
}
}
});
} catch (error) {
console.error('Error in action:', error);
// Return error response
return renderData({
status: error.status || 500,
message: error.message || 'Action failed'
});
}
}
};
renderData() Method Rules
Signature: renderData(error, data)
Success Response:
renderData(null, {
data: {
response: responseData,
response_variables: {
key1: value1,
key2: value2
}
}
});
Error Response:
renderData({
status: 403,
message: "Error while processing the request"
});
CRITICAL:
- ✅ First argument is ALWAYS
errorobject (ornullfor success) - ✅ For success, pass
nullas first argument - ✅ For error, pass object with
statusandmessage
Using Request Templates
CRITICAL: ALWAYS use request templates for third-party API calls.
⚠️ MANDATORY: Check API Documentation First
BEFORE constructing ANY request template:
-
ASK for API documentation - Request the user to provide:
- Official API documentation URL
- API endpoint details (method, path, headers)
- Authentication requirements
- Request/response body structure
- Example cURL commands or API reference
-
NEVER assume endpoints - Do NOT guess:
- ❌ API base URLs (e.g.,
api.example.com) - ❌ API paths (e.g.,
/v1/contacts,/api/users) - ❌ HTTP methods (GET, POST, PUT, DELETE)
- ❌ Required headers or authentication schemes
- ❌ Request body structure or field names
- ❌ API base URLs (e.g.,
-
VERIFY before implementing - Always confirm:
- ✅ Exact API endpoint URL
- ✅ Required authentication method (API key, OAuth, Basic Auth)
- ✅ Required headers (Content-Type, Authorization, custom headers)
- ✅ Request body format (JSON, form-data, etc.)
- ✅ Expected response structure
If API documentation is not provided:
- ❌ DO NOT proceed with request template construction
- ✅ ASK the user to provide API documentation or details
- ✅ Explain that accurate API details are required for correct implementation
Step 1: Define Request Template in config/requests.json
ONLY after verifying API documentation:
{
"createContactRequest": {
"schema": {
"protocol": "https",
"method": "POST",
"host": "api.example.com",
"path": "/v1/contacts",
"headers": {
"Authorization": "Bearer <%= iparam.api_key %>",
"Content-Type": "application/json"
}
}
}
}
Step 2: Declare Request in manifest.json
{
"modules": {
"common": {
"requests": {
"createContactRequest": {}
}
}
}
}
Step 3: Use in SMI Function
exports = {
createContact: async function(args) {
const response = await $request.invokeTemplate('createContactRequest', {
context: {},
body: JSON.stringify({ name: args.name, email: args.email })
});
return renderData(null, { data: { response: response.response } });
}
};
Working with Third-Party APIs
CRITICAL: AI Actions apps are designed to integrate with third-party services. NEVER assume API details.
Mandatory Pre-Implementation Checklist
Before implementing ANY action that calls a third-party API:
- Request API documentation from the user
- Verify endpoint URL - Exact base URL (e.g.,
api.workday.com,api.stripe.com) - Verify API path - Exact path (e.g.,
/v1/employees,/api/v2/contacts) - Verify HTTP method - GET, POST, PUT, DELETE, PATCH
- Verify authentication - API key, OAuth, Basic Auth, Bearer token
- Verify required headers - Content-Type, custom headers, API version headers
- Verify request body structure - Required fields, data types, nesting
- Verify response structure - Expected fields, status codes, error formats
How to Request API Details
When user asks to create an action, respond with:
To implement this action correctly, I need the API documentation for [Service Name].
Please provide one of the following:
1. Official API documentation URL (preferred)
2. Example cURL command for this API call
3. API endpoint details:
- Base URL (e.g., https://api.example.com)
- API path (e.g., /v1/resource)
- HTTP method (GET/POST/PUT/DELETE)
- Required headers
- Authentication method
- Request body structure
- Expected response format
Without this information, I cannot guarantee the request template will work correctly.
Example: Correct Workflow
User Request: "Create an action to add employees in Workday"
❌ WRONG Response:
{
"addEmployee": {
"schema": {
"method": "POST",
"host": "api.workday.com", // ❌ ASSUMED!
"path": "/v1/employees" // ❌ ASSUMED!
}
}
}
✅ CORRECT Response:
To implement the "Add Employee" action for Workday, I need the API documentation.
Please provide:
1. Workday API documentation URL for the employee creation endpoint
OR
2. Example cURL command showing how to create an employee in Workday
OR
3. The following details:
- Workday API base URL
- API endpoint path for creating employees
- Required authentication method (OAuth, API key, etc.)
- Required headers
- Request body structure for employee data
Once you provide this information, I'll create the action with the correct request template.
Common Third-Party Services
When working with these services, ALWAYS verify their API documentation:
- Workday - HR/Employee management APIs
- Salesforce - CRM APIs
- Stripe - Payment processing APIs
- Slack - Messaging APIs
- GitHub - Repository management APIs
- Jira - Issue tracking APIs
- Zapier - Webhook integration APIs
- Any other third-party service
DO NOT assume endpoint structure for ANY service, even popular ones!
Testing Actions
Step 1: Create Test Data
Create sample payload JSON files in <app root>/server/test_data/:
{
"name": "John Doe",
"email": "john@example.com"
}
Step 2: Run FDK Test Server
cd <app-directory>
fdk run
Step 3: Test in Browser
- Open
https://localhost:10001/web/test - From Select type, choose
actions - In Select an action, choose the action to test
- Edit payload if needed
- Click Simulate
- Success: Action worked
- Failed: Invalid payload or implementation error
Validation Checklist
Before finalizing actions, verify:
API Documentation Validation (CRITICAL - Check First):
- API documentation verified - Have official docs or user-provided details
- Endpoint URL confirmed - Not assumed or guessed
- HTTP method verified - Matches API documentation
- Authentication verified - Correct method and credentials
- Headers verified - All required headers included
- Request body structure verified - Matches API requirements
- Response structure verified - Matches API documentation
actions.json Validation:
- Function names are 2-40 characters, alphanumeric + underscores
- Function names do NOT start with numbers
- Request schema is FLAT (no nested objects or arrays)
- Response schema includes only essential fields
- IDs are marked as required in response schema
- All fields have clear descriptions
-
$schemais set tohttp://json-schema.org/draft-07/schema#
server.js Validation:
- Function name matches
actions.jsonexactly (case-sensitive) - Uses
$request.invokeTemplate()for external API calls - Constructs nested objects/arrays in function body (not in schema)
- Uses
renderData(null, data)for success - Uses
renderData(error)for errors - Includes error handling with try-catch
- Logs errors with
console.error()
manifest.json Validation:
- All request templates declared in
modules.common.requests
General Validation:
- Run
fdk validateand fix all errors - Test action using FDK test server
- Verify response matches schema
Common Mistakes to Avoid
❌ MISTAKE 0: Assuming API Endpoints Without Documentation
Problem: Guessing API endpoints, paths, or authentication methods without checking documentation.
Fix:
- ALWAYS ask for API documentation before implementing
- NEVER assume endpoint structure or authentication
- Verify all API details with official documentation or user-provided details
❌ MISTAKE 1: Nested Objects in Request Schema
Fix: Keep request schema flat, construct nested object in server.js.
❌ MISTAKE 2: Arrays in Request Schema
Fix: Accept single object parameters, construct array in server.js.
❌ MISTAKE 3: Including Entire API Response in Schema
Fix: Include only essential fields that will be used by other actions.
❌ MISTAKE 4: Function Name Mismatch
Fix: Ensure exact match (case-sensitive) between actions.json and server.js.
❌ MISTAKE 5: Not Using Request Templates
Fix: Use $request.invokeTemplate() with request templates.
❌ MISTAKE 6: Incorrect renderData() Usage
Fix:
// ✅ CORRECT - Success
renderData(null, { data: { response: data } });
// ✅ CORRECT - Error
renderData({ status: 500, message: 'Error' });
Best Practices
-
API Documentation First (CRITICAL)
- ✅ ALWAYS request API documentation before implementing
- ✅ Verify endpoint URLs, methods, and authentication
- ✅ Check official API reference or user-provided cURL examples
- ❌ NEVER assume or guess API endpoint structure
- ❌ NEVER proceed without verified API details
-
Clear Descriptions
- Write clear, concise descriptions for actions and fields
- Explain what each parameter does
- Document expected formats
-
Minimal Response Schemas
- Include only fields that will be used by other actions
- Avoid over-specifying response structure
- Let additional fields pass through by default
-
Error Handling
- Always use try-catch in SMI functions
- Log errors with
console.error() - Return meaningful error messages
- Include HTTP status codes in errors
-
Request Template Usage
- Define all external API calls as request templates
- Use
<%= iparam.variable %>for installation parameters - Use
<%= access_token %>for OAuth - Declare all request templates in manifest.json
- Base templates on verified API documentation ONLY
-
Testing
- Create comprehensive test payloads
- Test both success and error scenarios
- Validate response structure matches schema
- Test with FDK test server before deployment
When to Use This Skill
Use this skill when:
- ✅ User asks to create AI actions
- ✅ User mentions actions.json
- ✅ User wants to integrate with workflow automation
- ✅ User wants to create actions for AI assistants
- ✅ User needs to define action schemas
- ✅ User is implementing SMI functions for actions
Do NOT use this skill for:
- ❌ General Freshworks app development (use freshworks-app-dev-skill)
- ❌ Frontend UI components (use freshworks-app-dev-skill)
- ❌ OAuth integration (use freshworks-app-dev-skill)
- ❌ Manifest structure (use freshworks-app-dev-skill)
References
For detailed implementation guide, see:
references/ai-actions-guide.md- Complete guide with examplesreferences/ai-actions-quick-reference.md- One-page quick reference
For specific implementation details, use the get_developer_docs tool with these queries:
- "How to create actions.json in Freshworks Platform 3.0"
- "How to implement SMI functions in server.js"
- "How to use request templates for external APIs"
Summary
Key Takeaways:
- Request schemas MUST be flat - No nested objects or arrays
- Response schemas CAN be nested - Include only essential fields
- Function names MUST match exactly - Case-sensitive, alphanumeric + underscores
- Use request templates - For all external API calls
- Use renderData() correctly -
nullfor success, error object for failures - Construct complex structures in server.js - Not in schemas
- Test thoroughly - Use FDK test server before deployment
More from freshworks-developers/freshworks-platform3
freshworks-app-dev-skill
Expert-level development skill for building, debugging, reviewing, and migrating Freshworks Platform 3.0 marketplace applications. Use when working with Freshworks apps for (1) Creating new Platform 3.0 apps (frontend, serverless, hybrid, OAuth), (2) Debugging or fixing Platform 3.0 validation errors, (3) Migrating Platform 2.x apps to 3.0, (4) Reviewing manifest.json, requests.json, or oauth_config.json files, (5) Implementing Crayons UI components, (6) Integrating external APIs or OAuth providers, (7) Any task involving Freshworks Platform 3.0 app development, FDK CLI, or marketplace submission.
18app-dev
Expert-level development skill for building, debugging, reviewing, and migrating Freshworks Platform 3.0 marketplace applications. Use when working with Freshworks apps for (1) Creating new Platform 3.0 apps (frontend, serverless, hybrid, OAuth), (2) Debugging or fixing Platform 3.0 validation errors, (3) Migrating Platform 2.x apps to 3.0, (4) Reviewing manifest.json, requests.json, or oauth_config.json files, (5) Implementing Crayons UI components, (6) Integrating external APIs or OAuth providers, (7) Any task involving Freshworks Platform 3.0 app development, FDK CLI, or marketplace submission.
7freshworks-publish-skill
Pack and publish completed Freshworks custom apps to the marketplace. Use when the user wants to pack, publish, submit, or deploy a Freshworks app to the marketplace, prepare app for submission, create app package, or asks about app publishing workflow.
7