Getting Started with Actions
This guide explains what Actions are in Ductape and how to work with them effectively.
What is an Action?
An Action is an individual API endpoint that performs a specific task—like sending an email, charging a payment, or creating a user. When you import an App into Ductape, each endpoint in that App becomes an Action you can call.
Think of Actions as the building blocks of your integrations:
- Apps are collections of endpoints (like the Stripe API)
- Actions are individual endpoints (like "Create Charge" or "Refund Payment")
- Products orchestrate multiple Actions together into features
Prerequisites
Before working with Actions, make sure you have:
- A Ductape account and workspace
- An App created with imported endpoints
- The Ductape SDK installed in your project
Step 1: Install the SDK
Install the Ductape SDK in your project:
- TypeScript
- Java
- Go
- .NET
npm install @ductape/sdk@0.1.8
<dependency>
<groupId>app.ductape</groupId>
<artifactId>sdk</artifactId>
<version>0.1.8</version>
</dependency>
go get github.com/ductape/ductape/sdk/go@v0.1.8
dotnet add package Ductape.Sdk --version 0.1.8
Step 2: Initialize the SDK
Set up the Ductape SDK with your credentials:
- TypeScript
- Java
- Go
- .NET
import Ductape from '@ductape/sdk';
const ductape = new Ductape({
accessKey: 'your-access-key',
});
import app.ductape.sdk.Ductape;
import app.ductape.sdk.core.EnvType;
import app.ductape.sdk.core.RequestContext;
RequestContext auth = new RequestContext(null, null, null, null, 'your-access-key');
Ductape ductape = new Ductape(EnvType.PRODUCTION, auth);
import (
"context"
"github.com/ductape/ductape/sdk/go/core"
ductapesdk "github.com/ductape/ductape/sdk/go/ductape"
)
auth := core.NewRequestContext("", "", "", "", 'your-access-key')
client, err := ductapesdk.New(core.EnvProduction, auth)
if err != nil {
return err
}
using Ductape.Sdk;
using Ductape.Sdk.Core;
var auth = new RequestContext(null, null, null, null, 'your-access-key', null);
var ductape = new Ductape(EnvType.Production, auth);
Step 3: Import Actions
Actions are imported from API documentation like Postman collections or OpenAPI specs. You can import them into an existing App or create a new App during import.
Import into an Existing App
- TypeScript
- Java
- Go
- .NET
import { ImportDocTypes } from '@ductape/sdk/types';
import fs from 'fs';
// Read your Postman collection
const file = fs.readFileSync('./api.postman_collection.json');
// Import into an existing app
await ductape.api.import({
file,
type: ImportDocTypes.postmanV21,
app: 'my-app',
});
console.log('Actions imported successfully');
import fs from 'fs';
// Read your Postman collection
Map<String, Object> file = fs.readFileSync('./api.postman_collection.json');
// Import into an existing app
ductape.api().import(Map<String, Object>.of(
file,
type: ImportDocTypes.postmanV21,
"app", "my-app"
));
System.out.println('Actions imported successfully');
import fs from 'fs';
// Read your Postman collection
file := fs.readFileSync('./api.postman_collection.json');
// Import into an existing app
client.api.import({
file,
type: ImportDocTypes.postmanV21,
"app": "my-app",
});
fmt.Println('Actions imported successfully');
import fs from 'fs';
// Read your Postman collection
var file = fs.readFileSync('./api.postman_collection.json');
// Import into an existing app
await ductape.api.import({
file,
type: ImportDocTypes.postmanV21,
["app"] = "my-app",
});
Console.WriteLine('Actions imported successfully');
Create a New App and Import
Omit the app to create a new App from the collection:
- TypeScript
- Java
- Go
- .NET
await ductape.api.import({
file,
type: ImportDocTypes.postmanV21,
// App will be created from collection metadata
});
ductape.api().import(Map<String, Object>.of(
file,
type: ImportDocTypes.postmanV21,
// App will be created from collection metadata
));
client.api.import({
file,
type: ImportDocTypes.postmanV21,
// App will be created from collection metadata
});
await ductape.api.import({
file,
type: ImportDocTypes.postmanV21,
// App will be created from collection metadata
});
Supported Import Formats
| Format | Status | Description |
|---|---|---|
| Postman V2.1 | Available | Modern Postman collection format |
| Postman V2.0 | Coming Soon | Legacy Postman format |
| OpenAPI 3.0 | Coming Soon | OpenAPI/Swagger specification |
Step 4: Explore Available Actions
Before running Actions, you can list all available Actions in an App:
- TypeScript
- Java
- Go
- .NET
// Set the app context
await ductape.app.init({ app: 'stripe-payments' });
// Fetch all actions
const actions = await ductape.api.fetchAll();
actions.forEach((action) => {
console.log(`${action.name} (${action.tag}): ${action.method} ${action.resource}`);
});
// Set the app context
ductape.app.init(Map.of( "app", "stripe-payments" ));
// Fetch all actions
Map<String, Object> actions = ductape.api.fetchAll();
actions.forEach((action) => Map.of(
System.out.println(`$Map.of(action.name) ($Map.of(action.tag)): $Map.of(action.method) $Map.of(action.resource)`);
));
// Set the app context
client.app.init({ "app": "stripe-payments" });
// Fetch all actions
actions := client.api.fetchAll();
actions.forEach((action) => {
fmt.Println(`${action.name} (${action.tag}): ${action.method} ${action.resource}`);
});
// Set the app context
await ductape.app.init({ ["app"] = "stripe-payments" });
// Fetch all actions
var actions = await ductape.api.fetchAll();
actions.forEach((action) => {
Console.WriteLine(`${action.name} (${action.tag}): ${action.method} ${action.resource}`);
});
Fetch a Specific Action
Get details about a single Action by its tag:
- TypeScript
- Java
- Go
- .NET
const action = await ductape.api.fetch('create-charge');
console.log('Action:', action.name);
console.log('Method:', action.method);
console.log('Endpoint:', action.resource);
console.log('Description:', action.description);
Map<String, Object> action = ductape.api.fetch('create-charge');
System.out.println('"Action", ", action.name);
System.out.println(""Method", ", action.method);
System.out.println(""Endpoint", ", action.resource);
System.out.println("Description:', action.description);
action := client.api.fetch('create-charge');
fmt.Println('"Action": ", action.name);
fmt.Println(""Method": ", action.method);
fmt.Println(""Endpoint": ", action.resource);
fmt.Println("Description:', action.description);
var action = await ductape.api.fetch('create-charge');
Console.WriteLine('["Action"] = ", action.name);
Console.WriteLine("["Method"] = ", action.method);
Console.WriteLine("["Endpoint"] = ", action.resource);
Console.WriteLine("Description:', action.description);
Step 5: Run an Action
Call any Action using ductape.api.run():
- TypeScript
- Java
- Go
- .NET
const result = await ductape.api.run({
app: 'stripe-payments',
action: 'create-charge',
input: {
amount: 2000,
currency: 'usd',
source: 'tok_visa'
}
});
console.log('Charge created:', result);
Map<String, Object> result = ductape.api().run(Map<String, Object>.of(
"app", "stripe-payments",
"action", "create-charge",
input: Map.of(
"amount", 2000,
"currency", "usd",
"source", "tok_visa"
)
));
System.out.println('Charge created:', result);
import "context"
result := client.Api.Run(ctx, map[string]any{
"app": "stripe-payments",
"action": "create-charge",
input: {
"amount": 2000,
"currency": "usd",
"source": "tok_visa"
}
});
fmt.Println('Charge created:', result);
var result = await await ductape.Api.RunAsync(new Dictionary<string, object?>
{
["app"] = "stripe-payments",
["action"] = "create-charge",
input: {
["amount"] = 2000,
["currency"] = "usd",
["source"] = "tok_visa"
}
});
Console.WriteLine('Charge created:', result);
Flat Input Format
The SDK uses a flat input format where fields are automatically resolved to the correct location (body, params, query, or headers) based on the action's schema:
- TypeScript
- Java
- Go
- .NET
// Fields are auto-resolved based on the action schema
input: {
amount: 2000, // auto-resolves to body.amount
currency: 'usd', // auto-resolves to body.currency
userId: '123' // auto-resolves to params.userId
}
// Fields are auto-resolved based on the action schema
input: Map.of(
"amount", 2000, // auto-resolves to body.amount
"currency", "usd", // auto-resolves to body.currency
"userId", "123" // auto-resolves to params.userId
)
// Fields are auto-resolved based on the action schema
input: {
"amount": 2000, // auto-resolves to body.amount
"currency": "usd", // auto-resolves to body.currency
"userId": "123" // auto-resolves to params.userId
}
// Fields are auto-resolved based on the action schema
input: {
["amount"] = 2000, // auto-resolves to body.amount
["currency"] = "usd", // auto-resolves to body.currency
["userId"] = "123" // auto-resolves to params.userId
}
Using Prefixes for Conflicts
If a key exists in multiple locations, use prefix syntax:
| Prefix | Target Location | Example |
|---|---|---|
body: | Request body | 'body:id': 'item_456' |
params: | Route parameters | 'params:id': 'user_123' |
query: | Query parameters | 'query:limit': 10 |
headers: | HTTP headers | 'headers:X-Custom': 'value' |
- TypeScript
- Java
- Go
- .NET
// Example with mixed input using prefixes where needed
await ductape.api.run({
app: 'my-api',
action: 'get-user-orders',
input: {
userId: '123', // auto-resolved to params
status: 'pending', // auto-resolved to query
limit: 5, // auto-resolved to query
'headers:X-Request-ID': 'abc' // explicit header
}
});
// Example with mixed input using prefixes where needed
ductape.api().run(Map<String, Object>.of(
"app", "my-api",
"action", "get-user-orders",
input: Map.of(
"userId", "123", // auto-resolved to params
"status", "pending", // auto-resolved to query
"limit", 5, // auto-resolved to query
'headers:X-Request-ID': 'abc' // explicit header
)
));
import "context"
// Example with mixed input using prefixes where needed
client.Api.Run(ctx, map[string]any{
"app": "my-api",
"action": "get-user-orders",
input: {
"userId": "123", // auto-resolved to params
"status": "pending", // auto-resolved to query
"limit": 5, // auto-resolved to query
'headers:X-Request-ID': 'abc' // explicit header
}
});
// Example with mixed input using prefixes where needed
await await ductape.Api.RunAsync(new Dictionary<string, object?>
{
["app"] = "my-api",
["action"] = "get-user-orders",
input: {
["userId"] = "123", // auto-resolved to params
["status"] = "pending", // auto-resolved to query
["limit"] = 5, // auto-resolved to query
"headers:X-Request-ID": 'abc' // explicit header
}
});
Step 6: Update Action Configuration
After importing Actions, you can update their configuration:
- TypeScript
- Java
- Go
- .NET
await ductape.api.update('send-email', {
description: 'Send transactional email via SendGrid',
resource: '/v3/mail/send',
method: 'POST',
});
ductape.api.update('send-email', Map.of(
"description", "Send transactional email via SendGrid",
"resource", "/v3/mail/send",
"method", "POST"
));
client.api.update('send-email', {
"description": "Send transactional email via SendGrid",
"resource": "/v3/mail/send",
"method": "POST",
});
await ductape.api.update('send-email', {
["description"] = "Send transactional email via SendGrid",
["resource"] = "/v3/mail/send",
["method"] = "POST",
});
Complete Example
Here's a complete feature showing how to work with Actions:
- TypeScript
- Java
- Go
- .NET
import Ductape from '@ductape/sdk';
import { ImportDocTypes } from '@ductape/sdk/types';
import fs from 'fs';
async function main() {
// Initialize SDK
const ductape = new Ductape({
accessKey: 'your-access-key',
});
// Import Actions from a Postman collection
const collection = fs.readFileSync('./api.postman_collection.json');
await ductape.api.import({
file: collection,
type: ImportDocTypes.postmanV21,
app: 'my-api',
});
// List all imported Actions
await ductape.app.init({ app: 'my-api' });
const actions = await ductape.api.fetchAll();
console.log(`Imported ${actions.length} actions`);
// Update an Action's configuration
await ductape.api.update('create-user', {
description: 'Create a new user account',
});
// Run the Action
const result = await ductape.api.run({
app: 'my-api',
action: 'create-user',
input: {
name: 'John Doe',
email: 'john@example.com'
}
});
console.log('User created:', result);
}
main().catch(console.error);
import app.ductape.sdk.Ductape;
import app.ductape.sdk.core.EnvType;
import app.ductape.sdk.core.RequestContext;
import fs from 'fs';
async function main() Map.of(
// Initialize SDK
RequestContext auth = new RequestContext(null, null, null, null, 'your-access-key');
Ductape ductape = new Ductape(EnvType.PRODUCTION, auth);
// Import Actions from a Postman collection
Map<String, Object> collection = fs.readFileSync('./api.postman_collection.json');
ductape.api().import(Map<String, Object>.of(
file: collection,
type: ImportDocTypes.postmanV21,
"app", "my-api"
));
// List all imported Actions
ductape.app.init(Map.of( "app", "my-api" ));
Map<String, Object> actions = ductape.api.fetchAll();
System.out.println(`Imported $Map.of(actions.length) actions`);
// Update an Action's configuration
ductape.api.update('create-user', Map.of(
"description", "Create a new user account"
));
// Run the Action
Map<String, Object> result = ductape.api().run(Map<String, Object>.of(
"app", "my-api",
"action", "create-user",
input: Map.of(
"name", "John Doe",
"email", "john@example.com"
)
));
System.out.println('User created:', result);
)
main();
import (
"context"
"github.com/ductape/ductape/sdk/go/core"
ductapesdk "github.com/ductape/ductape/sdk/go/ductape"
)
import fs from 'fs';
async function main() {
// Initialize SDK
auth := core.NewRequestContext("", "", "", "", 'your-access-key')
client, err := ductapesdk.New(core.EnvProduction, auth)
if err != nil {
return err
}
// Import Actions from a Postman collection
collection := fs.readFileSync('./api.postman_collection.json');
client.api.import({
file: collection,
type: ImportDocTypes.postmanV21,
"app": "my-api",
});
// List all imported Actions
client.app.init({ "app": "my-api" });
actions := client.api.fetchAll();
fmt.Println(`Imported ${actions.length} actions`);
// Update an Action's configuration
client.api.update('create-user', {
"description": "Create a new user account",
});
// Run the Action
result := client.Api.Run(ctx, map[string]any{
"app": "my-api",
"action": "create-user",
input: {
"name": "John Doe",
"email": "john@example.com"
}
});
fmt.Println('User created:', result);
}
main().catch(console.error);
using Ductape.Sdk;
using Ductape.Sdk.Core;
import fs from 'fs';
async function main() {
// Initialize SDK
var auth = new RequestContext(null, null, null, null, 'your-access-key', null);
var ductape = new Ductape(EnvType.Production, auth);
// Import Actions from a Postman collection
var collection = fs.readFileSync('./api.postman_collection.json');
await ductape.api.import({
file: collection,
type: ImportDocTypes.postmanV21,
["app"] = "my-api",
});
// List all imported Actions
await ductape.app.init({ ["app"] = "my-api" });
var actions = await ductape.api.fetchAll();
Console.WriteLine(`Imported ${actions.length} actions`);
// Update an Action's configuration
await ductape.api.update('create-user', {
["description"] = "Create a new user account",
});
// Run the Action
var result = await await ductape.Api.RunAsync(new Dictionary<string, object?>
{
["app"] = "my-api",
["action"] = "create-user",
input: {
["name"] = "John Doe",
["email"] = "john@example.com"
}
});
Console.WriteLine('User created:', result);
}
main().catch(console.error);
Action Lifecycle
Understanding the Action lifecycle helps you build reliable integrations:
Import → Configure → Validate → Run → Handle Response
- Import: Actions are imported from Postman/OpenAPI specs
- Configure: Update descriptions, validation rules, and settings
- Validate: Set up input validation for data integrity
- Run: Execute the Action with your input
- Handle: Process the response or handle errors
Next Steps
Now that you understand Actions, learn how to:
- Run Actions - Advanced execution patterns with caching and retries
- Manage Actions - Update and organize your Actions
- Data Validation - Validate inputs before execution
See Also
- Getting Started with Apps - Create Apps and import Actions
- Sessions - Inject dynamic user data into Actions
- Caching - Cache Action responses for better performance