Skip to content

Creating Connectors

This guide covers how to create connectors for each execution model. For architecture details, see Connector Architecture.

Quick Start

The fastest way to create a connector:

  1. Navigate to Connectors in the sidebar
  2. Click New Connector
  3. Choose an execution model
  4. Fill in details and create

Execution Model Selection

Model When to Use Complexity
Built-in New instance of an existing registered type (e.g. a second Authifi tenant) Low
Script Custom integrations, user-authored logic Medium
OAS-Derived APIs with OpenAPI 3.x specs Low
External Remote services, separate processes Low-Medium

Note: Creating a new built-in connector type (i.e. writing the handler code) requires a server deployment. Creating a new instance of an existing built-in type is a low-complexity UI operation — see below.

Creating Built-in Connector Instances

Use this when you need another instance of a connector type that already exists in the registry (e.g. connecting a second Authifi tenant, or a separate LDAP directory).

Via the UI

  1. Navigate to Connectors and click New Connector
  2. Select Built-in as the execution model and click Next
  3. Choose the connector type from the dropdown (e.g. authifi, http)
  4. Enter a unique Name (e.g. authifi-prod), a Description, and optionally a Category
  5. If the type has connection config fields (API keys, URLs, etc.), fill them in — or leave them for later
  6. Click Next to review, then Create Connector

Via the API

POST /api/connectors
Content-Type: application/json

{
  "name": "authifi-prod",
  "type": "authifi",
  "description": "Authifi connector for the production tenant",
  "version": "1.0.0",
  "executionModel": "built_in",
  "configSchema": { ... },
  "config": {
    "baseUrl": "https://auth.example.com/_api",
    "tenant": "prod",
    "tenantId": "42",
    "clientId": "my-client-id",
    "clientSecret": "my-secret"
  }
}

The type must match a registered built-in handler name. The config field contains connection secrets that are encrypted at rest. You can omit config and set it later via PUT /api/connectors/:id.

Creating Script Connectors

Script connectors run custom JavaScript in a sandboxed QuickJS environment.

Step 1: Define the connector

POST /api/connectors
Content-Type: application/json

{
  "name": "my-crm",
  "type": "my-crm",
  "description": "Integration with My CRM",
  "version": "1.0.0",
  "executionModel": "script",
  "category": "crm",
  "tags": ["sales", "contacts"],
  "configSchema": {
    "connectionConfig": {
      "baseUrl": { "type": "string", "required": true, "description": "CRM API base URL" },
      "apiKey": { "type": "string", "required": true, "secret": true, "description": "API key" }
    },
    "commands": {
      "listContacts": {
        "params": ["limit", "offset"],
        "description": "List contacts from the CRM",
        "outputs": ["contacts", "total"],
        "paramSchema": {
          "limit": { "type": "number", "default": 50 },
          "offset": { "type": "number", "default": 0 }
        }
      },
      "createContact": {
        "params": ["email", "firstName", "lastName"],
        "description": "Create a new contact",
        "outputs": ["contactId"]
      },
      "test": {
        "params": [],
        "description": "Test connectivity",
        "outputs": []
      }
    }
  },
  "scriptSource": "... (see below)"
}

Step 2: Write the script

function listContacts() {
  var url = floh.config.baseUrl + '/api/contacts';
  url += '?limit=' + (floh.params.limit || 50);
  url += '&offset=' + (floh.params.offset || 0);

  var resp = floh.http.get(url, {
    'Authorization': 'Bearer ' + floh.config.apiKey
  });

  if (resp.status >= 400) {
    return { success: false, error: 'API returned ' + resp.status };
  }

  return {
    success: true,
    data: { contacts: resp.body.data, total: resp.body.total }
  };
}

function createContact() {
  var resp = floh.http.post(
    floh.config.baseUrl + '/api/contacts',
    JSON.stringify({
      email: floh.params.email,
      first_name: floh.params.firstName,
      last_name: floh.params.lastName
    }),
    { 'Authorization': 'Bearer ' + floh.config.apiKey }
  );

  if (resp.status >= 400) {
    return { success: false, error: 'Failed to create contact: ' + resp.status };
  }

  return { success: true, data: { contactId: resp.body.id } };
}

function test() {
  var resp = floh.http.get(floh.config.baseUrl + '/api/health', {
    'Authorization': 'Bearer ' + floh.config.apiKey
  });
  return { success: resp.status < 400 };
}

Step 3: Test the connector

POST /api/connectors/:id/test

Script API Reference (floh.*)

All script connectors have access to the floh global object:

floh.http

Method Signature Description
get floh.http.get(url, headers?) HTTP GET request
post floh.http.post(url, body?, headers?) HTTP POST request
put floh.http.put(url, body?, headers?) HTTP PUT request
patch floh.http.patch(url, body?, headers?) HTTP PATCH request
delete floh.http.delete(url, headers?) HTTP DELETE request

Response format:

{
  status: 200,           // HTTP status code
  body: { ... },         // Parsed JSON body
  headers: { ... }       // Response headers
}

All HTTP calls are proxied through the main thread. The sandbox has no direct network access.

floh.log

Method Description
floh.log.trace(message) Trace-level log
floh.log.debug(message) Debug-level log
floh.log.info(message) Info-level log
floh.log.warn(message) Warning-level log
floh.log.error(message) Error-level log

Logs are routed through the connector's ConnectorLogger and respect the configured logLevel.

floh.config

The connector's decrypted connection configuration. Contains the fields defined in connectionConfig:

floh.config.baseUrl   // "https://api.example.com"
floh.config.apiKey    // "sk-abc123..." (decrypted at runtime)

floh.params

The step parameters passed from the workflow. Contains the fields specified in the step's config:

floh.params.command   // "listContacts"
floh.params.limit     // 50
floh.params.userId    // "user-123"

floh.variables

The workflow run's current variable state:

floh.variables.initiated_by  // "user-456"
floh.variables.project_id    // "proj-789"

Return Format

Every command function must return a ConnectorResult:

// Success
return {
  success: true,
  data: { contacts: [...], total: 42 },
  outputVariables: { contactCount: 42 }
};

// Error
return {
  success: false,
  error: "Connection refused"
};

Creating OAS-Derived Connectors

Via the UI

  1. Navigate to Connectors → Import from OpenAPI
  2. Paste the OpenAPI 3.x JSON spec
  3. Click Parse Spec to preview generated commands
  4. Select/deselect commands to include
  5. Review the generated script and connection config
  6. Click Create Connector

Via the API

POST /api/connectors/parse-oas
Content-Type: application/json

{
  "spec": {
    "openapi": "3.0.3",
    "info": { "title": "My API", "version": "1.0.0" },
    "paths": { ... },
    "components": {
      "securitySchemes": {
        "bearerAuth": { "type": "http", "scheme": "bearer" }
      }
    }
  }
}

The response contains a draft connector definition ready to be sent to POST /api/connectors.

Supported Security Schemes

OAS Scheme Generated Config Fields
apiKey Named field with secret: true
http/bearer bearerToken
http/basic username, password
oauth2 clientId, clientSecret, tokenUrl

Creating External Connectors

External connectors communicate with Floh via a standardized HTTP protocol.

Protocol Endpoints

Your service must implement:

Endpoint Method Description
/execute POST Execute a connector command
/schema GET Return the connector's schema (optional)
/health GET Return health status (optional)

Execute Request

{
  "command": "listUsers",
  "config": { "apiKey": "..." },
  "params": { "limit": 50 },
  "variables": { "project_id": "proj-1" },
  "metadata": {
    "stepId": "step-1",
    "runId": "run-123",
    "timeout": 30000
  }
}

Execute Response

{
  "success": true,
  "data": { "users": [...] },
  "outputVariables": { "userCount": 10 }
}

Registration

POST /api/connectors
Content-Type: application/json

{
  "name": "my-external-service",
  "type": "my-external-service",
  "executionModel": "external",
  "endpointUrl": "https://connector.example.com",
  "version": "1.0.0",
  "configSchema": { ... }
}

On-Premise Agent

For connectors that need to reach services inside a private network, use the on-premise agent. The agent establishes an outbound WebSocket connection to Floh, so no inbound ports need to be opened.

Agent Registration

WebSocket: wss://floh.example.com/api/connectors/agents/ws

Registration message:
{
  "agentId": "agent-prod-01",
  "agentName": "Production Agent",
  "connectorNames": ["internal-ldap", "sap-hr"],
  "apiToken": "..."
}

Command Flow

Floh Server ──WebSocket──▶ Agent ──HTTP──▶ Internal Service
Agent ◀──HTTP──────────────────────────────────┘
Floh Server ◀──WebSocket──┘

The agent receives AgentCommand messages, executes them against internal services, and returns AgentResponse messages.

AI-Assisted Building

The Floh MCP server provides tools for AI-assisted connector development:

MCP Tool Description
generate_connector_script Generate a JavaScript script from a description
parse_openapi_spec Parse an OAS spec into a connector definition
create_connector Create a connector via the API
update_connector_script Update a connector's script
test_connector Test a connector's connectivity
execute_connector_command Execute a specific command

Example AI Workflow

  1. Ask the AI: "Create a connector for the Acme CRM API"
  2. Provide the OpenAPI spec or describe the API endpoints
  3. The AI uses parse_openapi_spec or generate_connector_script
  4. Review and refine the generated connector
  5. The AI uses create_connector and test_connector to deploy and verify