Build a custom connector

Choose JSON, ConnectorBuilder, custom execution, or Postman conversion to define a connector.

Package-name note: These approved examples use cliodot-flosync. Other supplied SDK material uses cliodot or @cliodot/flosync; confirm the current package before copying an import.

This guide explains how to build connectors in all supported ways: JSON, ConnectorBuilder (SDK chaining), custom execute (local), and Postman conversion. Each method includes building, registering/pushing, and using the connector.

Prerequisites#

  • FlowSync API access with connectors.write permission
  • API credentials: apiKey and apiSecret (from user registration)
  • JWT token obtained via POST /api-core/cliodot/user/login-with-api-key

Overview#

Method Build Register/Push Use
JSON JSON file Push to server (cURL/SDK) Workflows, client.connectors.execute()
ConnectorBuilder SDK chaining Push to server Workflows, client.connectors.execute()
Custom Execute Define with execute function flosync.registerConnector() (local only) Workflows (local runs)
Postman Convert collection Push to server Workflows, client.connectors.execute()

API Base Path#

https://your-api-domain.com/api-core/cliodot/connectors

Method 1: JSON Definition#

Create a JSON file matching the connector schema. Required: name, type, auth. For REST: base_url, endpoints.

Build (connectors/my-api.json):

{
 "_id": "my-api",
 "name": "My API",
 "slug": "my-api",
 "description": "Custom API connector",
 "type": "REST",
 "base_url": "https://api.example.com/v1",
 "auth": {
   "type": "bearer",
   "token": "",
   "header_name": "Authorization",
   "prefix": "Bearer "
 },
 "endpoints": [
   {
     "name": "Get Item",
     "method": "GET",
     "path": "/items/{{ pathParams.id }}",
     "pathParams": { "id": "string" }
   },
   {
     "name": "Create Item",
     "method": "POST",
     "path": "/items",
     "headers": { "Content-Type": "application/json" },
     "body_schema": { "name": "string", "value": "number" }
   }
 ],
 "meta": {
   "category": "Custom",
   "logo": "https://via.placeholder.com/64",
   "status": "active",
   "visibility_settings": { "visibility": "private", "public_access": "none" },
   "last_updated": "2025-01-27T00:00:00Z"
 }
}

Push (cURL):

curl -X POST https://your-api-domain.com/api-core/cliodot/connectors \
 -H "Authorization: Bearer YOUR_JWT" \
 -H "Content-Type: application/json" \
 -d @connectors/my-api.json

Register (SDK):

const { FlosyncClient } = require('cliodot-flosync');
const connectorDef = require('./connectors/my-api.json');

const client = new FlosyncClient({ baseUrl, apiKey, apiSecret });
await client.authenticate();
await client.connectors.push(connectorDef);
await client.connectors.install('my-api', { auth: { token: process.env.MY_API_TOKEN } });

Use (workflow):

s.connector('my-api', 'Get Item', { pathParams: { id: '123' } });
s.connector('my-api', 'Create Item', { body: { name: 'Widget', value: 99 } });

Use (direct execute):

const result = await client.connectors.execute('my-api', 'Get Item', { pathParams: { id: '123' } });

Method 2: ConnectorBuilder (SDK Chaining)#

Use the SDK connector() builder to define REST connectors programmatically.

Build:

const { connector } = require('cliodot-flosync');

const myConnector = connector('my-api', 'My API')
 .description('Custom API connector')
 .baseUrl('https://api.example.com/v1')
 .bearer('')
 .get('Get Item', '/items/{{ pathParams.id }}', {
   pathParams: { id: 'string' },
   response_mapping: { id: 'id', name: 'name' }
 })
 .post('Create Item', '/items', {
   headers: { 'Content-Type': 'application/json' },
   body_schema: { name: 'string', value: 'number' }
 })
 .meta({
   category: 'Custom',
   logo: 'https://via.placeholder.com/64',
   status: 'active',
   visibility_settings: { visibility: 'private', public_access: 'none' }
 })
 .build();

Push and register:

const { FlosyncClient, connector } = require('cliodot-flosync');

const client = new FlosyncClient({ baseUrl, apiKey, apiSecret });
await client.authenticate();

const myConnector = connector('my-api', 'My API')
 .baseUrl('https://api.example.com/v1')
 .bearer('')
 .get('Get Item', '/items/{{ pathParams.id }}', { pathParams: { id: 'string' } })
 .post('Create Item', '/items', { body_schema: { name: 'string', value: 'number' } })
 .meta({ category: 'Custom', logo: '...', status: 'active', visibility_settings: { visibility: 'private', public_access: 'none' } })
 .build();

await client.connectors.push(myConnector);
await client.connectors.install('my-api', { auth: { token: process.env.MY_API_TOKEN } });

Use (workflow):

s.connector('my-api', 'Get Item', { pathParams: { id: '123' } });
s.connector('my-api', 'Create Item', { body: { name: 'Widget', value: 99 } });

ConnectorBuilder methods:

Method Description
baseUrl(url) Base API URL
bearer(token) Bearer token auth
apiKey(key, keyName?) API key auth
botToken(token, prefix?) Bot token (e.g. Discord Bot prefix)
customFlow(flow) Custom auth with token exchange flow (JWT bearer, etc.)
authConfig(auth) Custom auth object
get(name, path, opts?) GET endpoint
post(name, path, opts?) POST endpoint
put(name, path, opts?) PUT endpoint
patch(name, path, opts?) PATCH endpoint
delete(name, path, opts?) DELETE endpoint
endpoint(name, method, path, opts?) Generic endpoint
meta(m) Metadata (category, logo, status, visibility_settings). Adds last_updated automatically.
build() Returns connector object

Endpoint options: params, pathParams, body_schema, headers, response_mapping, description, timeout_ms, sample_responses.

Method 3: Custom Execute (Local Only)#

For full control over logic, define a connector with an execute function. These run locally only (not pushed to the server). Use flosync.registerConnector() to register them.

Build (define connector with execute):

const { flosync, variable: v } = require('cliodot-flosync');

const myCustomConnector = {
 _id: 'my.custom',
 type: 'system',
 name: 'My Custom Logic',
 meta: { category: 'custom' },
 auth: { type: 'none' },
 actions: [
   { id: 'validate', name: 'Validate' },
   { id: 'transform', name: 'Transform' },
 ],
 execute: async (action, options, context) => {
   const body = options.body || options;
   if (action === 'validate') {
     const valid = body.email && body.email.includes('@');
     return { valid, email: body.email };
   }
   if (action === 'transform') {
     return {
       ...body,
       normalized: body.name?.trim().toLowerCase(),
       timestamp: new Date().toISOString(),
     };
   }
   throw new Error('Unknown action: ' + action);
 },
};

Register:

flosync.registerConnector('my.custom', myCustomConnector);

Or with config (for connector-specific settings):

flosync.configure({
 connectors: {
   'my.custom': { /* optional config */ },
 },
});
flosync.registerConnector('my.custom', myCustomConnector);

Use (workflow):

s.connector('my.custom', 'validate', { body: { email: v.body('email') } });
s.connector('my.custom', 'transform', { body: v.stepResult('save') });

Use (direct run):

const result = await flosync.runConnector('my.custom', 'validate', {
 body: { email: 'user@example.com' },
});

Execute signature:

execute(
 action: string,
 options: { body?: any; params?: any; pathParams?: any; headers?: any },
 context: { trigger?: any; stepResults?: any; vars?: any; headers?: any; connectorConfig?: any; env?: any }
): Promise<unknown>

Return format: Return any object. The engine uses it as the step result. For { raw, mapped }, the engine uses mapped ?? raw.

Method 4: Postman Conversion#

Convert an existing Postman collection to a connector:

curl -X POST https://your-api-domain.com/api-core/cliodot/connectors/convert/postman \
 -H "Authorization: Bearer YOUR_JWT" \
 -F "collection=@/path/to/collection.postman_collection.json"

Returns a connector object. Push it via POST /connectors or client.connectors.push(). See /docs/connectors/build#method-4-postman-conversion for details.