Skip to content

Creating a Tool

Tools in superglue are reusable workflows that you can execute on-demand, schedule, or trigger via webhooks. Each tool consists of sequential steps with built-in data transformations, loops, and error handling.

Learn more about how tools fit into the bigger picture in our core concepts page.

The fastest way to create a tool is by talking to our agent. Describe what you want to accomplish and the agent will build the tool for you.

Simple example:

"Create a tool that fetches all active customers from Stripe"

superglue will:

  1. Identify which system to use (or create one if needed)
  2. Find the relevant API endpoint from the documentation
  3. Configure the API call with appropriate parameters
  4. Test it and make it available for execution

Complex example:

"Create a tool that loops through all contacts in HubSpot,
filters those with email domains matching @company.com,
and updates their lifecycle stage to 'customer'"

The agent can build multi-step tools with:

  • Sequential API calls
  • Data transformations between steps
  • Loops for batch processing
  • Error handling and retries

Every tool consists of:

Steps

Sequential API calls that fetch or modify data. Each step can reference data from previous steps.

Transformations

JavaScript functions that shape the step inputs and the final output. Ensures tool results adhere to response schemas.

Variables

Access data from previous steps, credentials, and input payload using <<variable>> syntax.

Each step in a tool represents a single API call with the following configuration:

{
id: "stepId", // Unique identifier for this step
instruction: "Fetch customer data from Stripe", // Human-readable description
dataSelector: "(sourceData) => { return [\"one\", \"two\"]}", // The data selector for the current step
config: {
systemId: "stripe", // Which system to use
method: "POST", // HTTP method
url: "https://api.stripe.com/v1/customers", // Full API endpoint URL
queryParams: {}, // URL query parameters
headers: { "Authorization": "Bearer <<stripe_apiKey>>" }, // Custom headers
body: "" // Request body
}
}

Use <<variable>> to access dynamic values:

Access credentials:

{
headers: {
"Authorization": "Bearer <<systemId_access_token>>"
}
}

Access previous step data:

{
urlPath: "/customers/<<getCustomer.data.id>>",
body: {
email: "<<getCustomer.data.email>>"
}
}

Access input payload:

{
queryParams: {
user_id: "<<userId>>",
date: "<<startDate>>"
}
}

Execute JavaScript expressions:

{
body: {
ids: "<<(sourceData) => JSON.stringify(sourceData.users.map(u => u.id))>>",
timestamp: "<<(sourceData) => new Date().toISOString()>>",
count: "<<(sourceData) => sourceData.items.length>>",
uppercaseName: "<<(sourceData) => sourceData.name.toUpperCase()>>"
}
}

Extract an array to iterate over:

{
dataSelector: `(sourceData) => {
const items = sourceData.fetchItems.results || [];
const excludeIds = sourceData.excludeIds || [];
return items.filter(item => !excludeIds.includes(item.id));
}`;
}

Shape the final output of the entire tool:

{
outputTransform: `(sourceData) => {
const customers = sourceData.getCustomers.data || [];
const updated = sourceData.updateCustomers || [];
return {
success: true,
total: customers.length,
updated: updated.length,
results: updated.map(r => ({
id: r.currentItem.id,
status: r.data.status
}))
};
}`;
}

Transform steps allow you to reshape data between API calls without making an external request. Use them when you need complex data transformations that don’t fit in a dataSelector.

{
id: "formatCustomerData",
instruction: "Format customer data for the next API call",
config: {
type: "transform",
transformCode: "(sourceData) => sourceData.getCustomers.data.map(c => ({ id: c.id, name: c.fullName, email: c.contactEmail }))"
}
}
  • Complex data reshaping between API calls
  • Aggregating data from multiple previous steps
  • Filtering or sorting data before the next request
  • Preparing payloads in a specific format

Transform step results are wrapped in the same { currentItem, data, success } envelope as request steps:

// In a subsequent step — access the transformed data via .data
{
body: "<<(sourceData) => JSON.stringify(sourceData.formatCustomerData.data)>>";
}

Query databases using the postgres:// URL scheme:

{
id: "queryDatabase",
config: {
systemId: "postgres_db",
url: "postgres://<<postgres_db_username>>:<<postgres_db_password>>@<<postgres_db_hostname>>:5432/<<postgres_db_database>>",
body: {
query: "SELECT * FROM users WHERE status = $1 AND created_at > $2",
params: ["active", "<<(sourceData) => sourceData.startDate>>"]
}
}
}

Always use parameterized queries with $1, $2, etc. placeholders to prevent SQL injection. Provide values in the params array, which can include static values or <<>> expressions.

Execute Redis commands using the redis:// (or rediss:// for TLS) URL scheme:

{
id: "getUserProfile",
config: {
systemId: "redis_cache",
url: "redis://<<redis_cache_username>>:<<redis_cache_password>>@<<redis_cache_host>>:6379/0",
body: {
command: "HGETALL",
args: ["<<(sourceData) => `user:${sourceData.userId}`>>"]
}
}
}

The body takes a command string and optional args array. You can also pass an array of commands to execute them as a pipeline in a single round-trip:

body: [
{ command: "GET", args: ["user:123:name"] },
{ command: "HGETALL", args: ["user:123"] },
];

Query MongoDB using the mongodb:// (or mongodb+srv:// for Atlas/SRV) URL scheme:

{
id: "findActiveUsers",
config: {
systemId: "mongo_db",
url: "mongodb://<<mongo_db_username>>:<<mongo_db_password>>@<<mongo_db_host>>:27017/<<mongo_db_database>>?authSource=admin",
body: {
collection: "users",
operation: "find",
filter: { status: "active" },
options: { limit: 50, sort: { createdAt: -1 } }
}
}
}

The body takes an operation (find, aggregate, insertOne, updateMany, deleteOne, and other CRUD operations) and a collection. Bodies are parsed as MongoDB Extended JSON, so { "$oid": "..." } and { "$date": "..." } resolve to ObjectId and Date. Pass an array of operations to run several in one step.

Access files on FTP servers:

{
id: "listFiles",
config: {
systemId: "ftp-server",
url: "sftp://<<ftp-server_username>>:<<ftp-server_password>>@<<ftp-server_hostname>>:22/data",
body: {
operation: "list",
path: "/reports"
}
}
}

Supported operations:

  • list - List directory contents
  • get - Download file (auto-parses CSV/JSON/XML)
  • put - Upload file
  • delete - Delete file
  • rename - Rename/move file
  • mkdir - Create directory
  • rmdir - Remove directory
  • exists - Check if file exists
  • stat - Get file metadata

Automatic retries

Failed steps can be automatically retried with exponential backoff

Validation

Response data is validated against response schemas if specified

Graceful degradation

Handle missing data with optional chaining and defaults in transformations

Define schemas to make your tools more robust:

Input schema - Validates payload before execution:

{
"type": "object",
"properties": {
"userId": { "type": "string" },
"startDate": { "type": "string", "format": "date" }
},
"required": ["userId"]
}

Response schema - Validates final output:

{
"type": "object",
"properties": {
"success": { "type": "boolean" },
"data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "string" },
"email": { "type": "string" }
}
}
}
}
}