Skip to main content
This guide walks through the structure of StackOne’s Falcon connectors — how files are organized, how authentication works, how actions and steps fit together, and how expressions let you wire data between them. For detailed property-by-property documentation, see the YAML Reference. For working examples, see the connectors-template repository.

File Structure

Connectors are organized into a main connector file and (optionally) partial files for each resource:
src/configs/{provider}/
├── {provider}.connector.s1.yaml           # Main connector (auth, metadata)
└── {provider}.{resource}.s1.partial.yaml  # Actions for each resource
Naming conventions:
  • Use kebab-case for file names
  • Reference partials in the main file using $ref: provider.resource
  • Add references in alphabetical order (not required, but recommended for readability)

Main Connector File

The main connector file defines metadata, authentication, and references to action partials.
StackOne: 1.0.0
info:
  title: Provider Name
  key: provider_name
  version: 1.0.0
  assets:
    icon: https://stackone-logos.com/api/provider_name/filled/png
  description: Brief description of the provider

baseUrl: https://api.provider.com/v1

rateLimit:
  mainRatelimit: 10  # Requests per second

context: "Provider API documentation: https://api.provider.com/docs"

authentication:
  - custom:
      # ... authentication config

actions:
  - $ref: provider.users
  - $ref: provider.files

Info Section

FieldRequiredDescription
titleYesDisplay name shown in the StackOne Hub and dashboard
keyYesUnique lowercase identifier used in action names (e.g., bamboohr_list_employees)
versionYesSemantic version for tracking connector changes
assets.iconNoURL to provider logo (displayed in Hub and dashboard)
descriptionNoBrief description for documentation and AI context
  • title: User-facing name. Use the official provider name (e.g., “BambooHR” not “Bamboo HR”)
  • key: Machine-readable identifier. Must be lowercase with underscores. Cannot be changed after deployment without breaking existing integrations
  • version: Increment when making changes. Use semantic versioning: major.minor.patch
  • assets.icon: Use https://stackone-logos.com/api/{key}/filled/png for consistent styling
  • description: Keep concise. Used by AI agents for context when discovering connectors

Base URL

The baseUrl defines the root URL for all API requests. It supports credential interpolation for providers with dynamic domains.
# Static base URL
baseUrl: https://api.provider.com/v1

# Dynamic subdomain from credentials
baseUrl: https://${credentials.subdomain}.api.provider.com/v2

# Region-specific endpoint
baseUrl: https://api.${credentials.region}.provider.com/v1
Use credential interpolation when the provider requires:
  • Customer-specific subdomains: Many B2B SaaS products use {company}.provider.com URLs
  • Region selection: Some providers have separate endpoints per region (US, EU, APAC)
  • Environment switching: Sandbox vs production endpoints
The interpolated values come from configFields in your authentication config. These are fields your customer fills in when connecting their account.

Authentication

Authentication is defined once in the main connector file. All actions inherit it.

Setup vs Config Fields

Field TypeWho Fills ItWhenExample
setupFieldsYou (the developer)Dashboard integration configClient ID, Client Secret, Scopes
configFieldsYour customerHub account connectionAPI Token, Subdomain
Use setupFields for:
  • OAuth client credentials (Client ID, Client Secret)
  • Scopes that you control
  • API keys that belong to your app, not the end user
  • Values you configure once in the StackOne dashboard
Use configFields for:
  • Customer’s own API tokens or keys
  • Customer-specific identifiers (subdomain, account ID, workspace ID)
  • Any value that differs per customer connection
  • Values the customer enters in the Hub when linking their account
Field properties:
  • secret: true: Masks the field value and encrypts storage (use for tokens, keys, passwords)
  • required: true: Field must be filled before connection can complete
  • placeholder: Shows example format without exposing real values
  • tooltip: Additional help text shown on hover

Example: API Key Authentication

authentication:
  - custom:
      type: custom
      label: API Key
      support:
        link: https://your-connection-guide-url-here.com
        description: Steps require Admin privileges
      authorization:
        type: bearer
        token: $.credentials.apiKey
      configFields:
        - key: apiKey
          label: API Key
          type: password
          required: true
          secret: true
          placeholder: sk_live_xxxxx
          description: Generate in Settings > API Keys
      environments:
        - key: production
          name: Production
      testActionsIds:
        - list_users

Other Authentication Types

The Falcon engine supports several authorization types:
Typeauthorization.typeUse Case
Bearer tokenbearerAPI key or OAuth access token in Authorization: Bearer header
Basic authbasicUsername/password encoded as Authorization: Basic header
Custom headersnone + customHeadersProvider-specific header-based auth
OAuth 2.0oauth2Full OAuth flow with authorization URL, token exchange, and refresh
For complete authentication examples including OAuth 2.0 with token refresh, see the YAML Reference — Authentication.

Actions

Actions define the operations your connector can perform. Each action consists of metadata, inputs, steps, and result configuration.

Action Structure

- actionId: list_users
  categories:
    - hris
  actionType: list
  schema: users
  label: List Users
  description: Get all users with pagination
  context: https://api.provider.com/docs/users/list
  entrypointUrl: /users
  entrypointHttpMethod: get
  inputs:
    - name: page_size
      type: number
      in: query
      required: false
      description: Results per page (max 100)
  steps:
    - stepId: fetch_users
      stepFunction:
        functionName: request
        parameters:
          url: /users
          method: get
  result:
    data: $.steps.fetch_users.output.data
FieldRequiredDescription
actionIdYesUnique identifier within the connector. Forms the action name: {connector_key}_{actionId}
categoriesYesArray of verticals this action belongs to (e.g., hris, ats, crm). See YAML Reference
actionTypeYesIdentifies the type of the action (list, get, create, update, delete, custom)
schemaConditionalRequired for unified actions. Specifies which StackOne schema to map to
labelYesHuman-readable name shown in Actions Explorer and AI tool descriptions
descriptionYesExplains what the action does. Used by AI agents for tool selection
contextNoURL to provider API docs. Helps AI agents understand the action
entrypointUrlNoAlternative to defining URL in steps. The main endpoint path
entrypointHttpMethodNoAlternative to defining method in steps. HTTP method to use
  • actionId: Keep it descriptive and follow the pattern {verb}_{resource} (e.g., list_users, get_employee, create_candidate). Cannot be changed after deployment.
  • categories: Determines where the action appears in the Actions Explorer. An action can belong to multiple categories.
  • actionType: Choosing custom is fastest since no schema mapping is needed. Use unified types (list, get, etc.) when you want cross-provider compatibility.
  • schema: Must match a StackOne schema name (e.g., users, employees, candidates). Only required when actionType is unified.
  • label + description: These appear in the Actions Explorer and are sent to AI agents. Write them for humans, not machines.
  • context: Adding the provider’s API doc URL helps the Builder Agent and AI tools understand edge cases.

Action Types

TypeDescription
listRetrieve multiple records, typically with pagination
getRetrieve a single record by identifier
createCreate a new resource
updateModify an existing resource
deleteRemove a resource
customProvider-specific operation that doesn’t fit standard CRUD patterns
refresh_tokenInternal OAuth token refresh (use with internal category)
Add a schema field to map provider data to StackOne’s standardized schemas (e.g., schema: employees). Without schema, the action returns provider data as-is. See Field Configs for mapping details.

Inputs

Inputs define the parameters your action accepts. Each input specifies a name, type, location (in), and whether it’s required:
inputs:
  - name: id
    type: string
    in: path           # URL path parameter (/users/${inputs.id})
    required: true
  - name: status
    type: string
    in: query          # Query string (?status=active)
    required: false
  - name: email
    type: string
    in: body           # Request body field
    required: true
Input types: string, number, boolean, datetime_string, object Input locations: path, query, body, headers For full input property documentation including nested objects, see the YAML Reference — Inputs.

Step Functions

Step functions are the building blocks of actions. Each step performs one operation.
ScenarioUse
Single API call (get user, create record)request
List endpoint with cursor paginationpaginated_request
List endpoint with offset/page paginationrequest + manual cursor handling
Per-item API calls (detail fetch for each ID)request with iterator step
Transform provider data to output schemamap_fields
Convert field types after mappingtypecast
Merge results from multiple API callsgroup_data
Return hardcoded enum valuesstatic_values
Combine static + dynamic datamerge_collections
Upload a fileupload_file
Download a filedownload_file
SOAP/XML API callsoap_request
Common patterns:
  • GET single resource: requestresult
  • LIST with pagination: paginated_requestresult
  • Unified action: requestmap_fieldstypecastresult
  • Multi-source data: request (x2) → group_dataresult
  • Per-item enrichment: request → iterator requestgroup_dataresult
  • Static + dynamic merge: static_values + requestmerge_collectionsresult

request - HTTP Requests

Make a single HTTP request to the provider API. Use args to pass headers, query parameters, and body fields. Use response.dataKey to extract nested data from the response.
steps:
  - stepId: get_user
    description: Fetch user by ID
    stepFunction:
      functionName: request
      parameters:
        url: /users/${inputs.id}
        method: get
        args:
          - name: include_details
            value: "{{inputs.include_details}}"
            in: query
            condition: "{{present(inputs.include_details)}}"
        response:
          collection: false
          dataKey: user
Always use args for request parameters. Never use a direct body field.

paginated_request - Cursor Pagination

Automatically fetches all pages using cursor-based pagination. StackOne handles cursor tracking, page assembly, and continuation logic.
steps:
  - stepId: list_users
    stepFunction:
      functionName: paginated_request
      parameters:
        url: /users
        method: get
        response:
          dataKey: results       # Where to find the data array
          nextKey: nextCursor    # Where to find the next cursor
        iterator:
          key: cursor            # Parameter name to pass cursor
          in: query              # Where to send it (query or body)
        args:
          - name: limit
            value: "100"
            in: query
How it works: Makes the initial request, extracts results from response.dataKey, checks response.nextKey for a continuation cursor, and repeats until no cursor is returned. All pages are combined automatically.
Only use paginated_request for cursor-based pagination. For offset or page-based pagination, use a regular request step with manual cursor handling.

map_fields - Transform Data

Transform provider response data into your output schema. Takes a dataSource (typically a previous step’s output), evaluates an expression per field against each record, and converts to the specified type.
steps:
  - stepId: map_data
    description: Map user data
    stepFunction:
      functionName: map_fields
      version: "2"
      parameters:
        dataSource: $.steps.fetch_users.output.data
        fields:
          - targetFieldKey: id
            expression: $.user_id
            type: string
          - targetFieldKey: full_name
            expression: "{{$.first_name + ' ' + $.last_name}}"
            type: string
          - targetFieldKey: is_active
            expression: $.status == 'active'
            type: boolean
Supports enum mapping, nested objects, and JEXL expressions for complex transformations. See YAML Reference — map_fields for the full list of field config options.

typecast - Type Conversion

Apply type conversions to data. Used for unified actions.
steps:
  - stepId: typecast_data
    description: Apply types
    stepFunction:
      functionName: typecast
      version: "2"
      parameters:
        dataSource: $.steps.map_data.output.data
        fields:
          - targetFieldKey: created_at
            type: datetime_string
          - targetFieldKey: salary
            type: number
          - targetFieldKey: is_manager
            type: boolean

group_data - Combine Results

Merge data from multiple steps into a single dataset.
steps:
  - stepId: combine_data
    stepFunction:
      functionName: group_data
      parameters:
        stepsDataToGroup:
          - fetch_employees
          - fetch_contractors
        isSingleRecord: false

static_values - Return Static Data

Return predefined values without making an API request. Useful for enum lookups or constant data.
steps:
  - stepId: ticket_types
    stepFunction:
      functionName: static_values
      parameters:
        values:
          - id: question
            name: Question
          - id: incident
            name: Incident

merge_collections - Merge Arrays

Merge multiple data collections into a single array.
steps:
  - stepId: all_types
    stepFunction:
      functionName: merge_collections
      parameters:
        collections:
          - $.steps.static_types.output.data
          - $.steps.api_types.output.data

upload_file / download_file - File Operations

Handle file uploads (multipart form) and downloads (with encoding options).
# Upload
- stepId: upload_document
  stepFunction:
    functionName: upload_file
    parameters:
      url: /files/upload
      method: post
      file:
        fieldName: file
        content: $.inputs.file_content
        filename: $.inputs.filename

# Download
- stepId: download_document
  stepFunction:
    functionName: download_file
    parameters:
      url: /files/${inputs.file_id}/content
      method: get
      encoding: base64

soap_request - SOAP API

Make SOAP (XML) requests. Used for enterprise providers like Workday.
steps:
  - stepId: get_workers
    stepFunction:
      functionName: soap_request
      parameters:
        url: /Human_Resources
        method: post
        soapOperation: Get_Workers
        soapAction: http://example.com/Get_Workers
        namespaces:
          - namespaceIdentifier: bsvc
            namespace: urn:com.workday/bsvc
For full parameter documentation on all step functions, see the YAML Reference — Steps.

Iterator Steps (Foreach)

Iterator steps execute one or more step functions for each item in a collection. Use them when you need to make per-item API calls — for example, fetching detailed data for each record returned by a list endpoint.

Structure

An iterator step is defined by adding the iterator property to a step. The iterator value is a JSONPath expression that resolves to an array.
steps:
  - stepId: get_employee_details
    description: Fetch detailed data for each employee
    iterator: $.steps.list_employees.output.data.employees[*].id
    stepFunction:
      functionName: request
      parameters:
        url: /employees/${iterator.item}
        method: get
        response:
          collection: false

How It Works

  1. The iterator expression is evaluated to produce an array
  2. For each item in the array, the step function(s) execute
  3. Inside the step, you can access:
    • $.iterator.item — the current array element
    • $.iterator.index — the current iteration index (0-based)
    • $.iterator.current — the output of the previous step function in the same iteration (when using stepFunctions)
  4. All iteration outputs are collected into an array at $.steps.{stepId}.output.data

Single Step Function

The most common pattern — make one API call per item:
steps:
  - stepId: list_employees
    description: Get all employee IDs
    stepFunction:
      functionName: request
      parameters:
        url: /employees
        method: get
        response:
          collection: true
          dataKey: employees

  - stepId: get_employee_details
    description: Fetch details for each employee
    iterator: $.steps.list_employees.output.data.employees[*].id
    stepFunction:
      functionName: request
      parameters:
        url: /employees/${iterator.item}
        method: get
        response:
          collection: false

Multiple Step Functions

Use stepFunctions (plural) to execute a sequence of functions per iteration. Each function can access the output of the previous one via $.iterator.current:
steps:
  - stepId: enrich_employees
    description: Fetch and transform data for each employee
    iterator: $.steps.list_employees.output.data.employees[*].id
    stepFunctions:
      - functionName: request
        parameters:
          url: /employees/${iterator.item}/details
          method: get
          response:
            collection: false
      - functionName: request
        parameters:
          url: /employees/${iterator.item}/compensation
          method: get
          response:
            collection: false
You must provide either stepFunction (singular) or stepFunctions (plural) — not both. The YAML validator enforces this.

Conditional Iterator

Like regular steps, iterator steps support the condition and ignoreError properties:
steps:
  - stepId: get_employee_managers
    description: Fetch manager data if employees have managers
    iterator: $.steps.list_employees.output.data.employees[*]
    condition: '{{present(steps.list_employees.output.data.employees)}}'
    ignoreError: true
    stepFunction:
      functionName: request
      parameters:
        url: /employees/${iterator.item.managerId}
        method: get
        condition: '{{present(iterator.item.managerId)}}'

Iterator Context Reference

ExpressionDescription
$.iterator.itemCurrent item from the iterated array
$.iterator.indexCurrent iteration index (0-based)
$.iterator.currentOutput from the previous step function in stepFunctions
${iterator.item}String interpolation of current item (for URLs)
${iterator.item.fieldName}Nested field from current item

Expression Syntax

The Falcon engine supports three expression formats for dynamic values. For the complete expression language reference including all built-in functions, operators, and detailed examples, see the Expression Language page.

JSONPath (Preferred)

Direct data access. Use for any direct reference.
# Credentials
token: $.credentials.apiKey

# Inputs
value: $.inputs.userId

# Step output
dataSource: $.steps.fetch_users.output.data

String Interpolation (${...})

Use for embedding values within strings.
# URL path parameters
url: /users/${inputs.id}

# Dynamic domains
baseUrl: https://${credentials.subdomain}.api.provider.com

JEXL Expressions ('{{...}}')

Use for conditional logic and transformations. Must be in single quotes.
# Conditionals
condition: '{{present(inputs.filter)}}'

# Default values
value: '{{inputs.limit ?? 100}}'

# Ternary operations
value: '{{$.count > 0 ? $.count : 0}}'
Common functions: present(), missing(), includes(), join(), capitalize(), truncate(), regexMatch(), now() — see full reference.

Conditional Arguments

Include arguments only when conditions are met:
args:
  - name: filter
    value: '{{inputs.filter}}'
    in: query
    condition: '{{present(inputs.filter)}}'

  - name: updated_since
    value: $.inputs.updated_after
    in: query
    condition: '{{present(inputs.updated_after)}}'

  - name: include_inactive
    value: "true"
    in: query
    condition: '{{inputs.include_inactive == true}}'

Result

Define the action’s response output. Read operations:
result:
  data: $.steps.fetch_users.output.data
Write operations:
result:
  message: User created successfully
  data:
    id: '{{inputs.id}}'
With pagination info:
result:
  data: $.steps.list_users.output.data
  next: $.steps.list_users.output.next

Field Configs (Unified Actions)

For unified actions (those with a schema field), fieldConfigs map provider response fields to StackOne’s standardized schema:
- actionId: list_users
  actionType: list
  schema: users
  fieldConfigs:
    - targetFieldKey: id
      expression: $.accountId
      type: string
    - targetFieldKey: email
      expression: $.emailAddress
      type: string
    - targetFieldKey: status
      expression: $.userStatus
      type: enum
      enumMapper:
        matcher:
          - matchExpression: '{{$.userStatus == "ACTIVE"}}'
            value: active
          - matchExpression: '{{$.userStatus == "SUSPENDED"}}'
            value: inactive
For built-in enum matchers and full field config options, see the YAML Reference — map_fields.

Rate Limiting

Configure request throttling:
rateLimit:
  mainRatelimit: 10    # Max requests per second

Validation

Validate connector files using the CLI:
npm install -g @stackone/cli

stackone validate path/to/connector.s1.yaml

Complete Example

# provider.connector.s1.yaml
StackOne: 1.0.0
info:
  title: Acme HR
  key: acme_hr
  version: 1.0.0
  assets:
    icon: https://stackone-logos.com/api/acme_hr/filled/png
  description: HR management platform

baseUrl: https://api.acme.com/v2

rateLimit:
  mainRatelimit: 10

authentication:
  - custom:
      type: custom
      label: API Key
      authorization:
        type: bearer
        token: $.credentials.apiKey
      configFields:
        - key: apiKey
          label: API Key
          type: password
          required: true
          secret: true
      environments:
        - key: production
          name: Production
      testActionsIds:
        - list_employees

actions:
  - $ref: acme_hr.employees
# provider.employees.s1.partial.yaml
- actionId: list_employees
  categories:
    - hris
  actionType: custom
  label: List Employees
  description: Get all employees with pagination
  context: https://api.acme.com/docs/employees
  inputs:
    - name: page_size
      type: number
      in: query
      required: false
      description: Results per page (max 100)
    - name: status
      type: string
      in: query
      required: false
      description: Filter by status (active, inactive)
  steps:
    - stepId: fetch_employees
      stepFunction:
        functionName: paginated_request
        parameters:
          url: /employees
          method: get
          response:
            dataKey: data
            nextKey: pagination.next_cursor
          iterator:
            key: cursor
            in: query
          args:
            - name: limit
              value: '{{inputs.page_size || 100}}'
              in: query
            - name: status
              value: '{{inputs.status}}'
              in: query
              condition: '{{present(inputs.status)}}'
  result:
    data: $.steps.fetch_employees.output.data

- actionId: get_employee
  categories:
    - hris
  actionType: custom
  label: Get Employee
  description: Get employee by ID
  context: https://api.acme.com/docs/employees/{id}
  inputs:
    - name: id
      type: string
      in: path
      required: true
      description: Employee ID
  steps:
    - stepId: fetch_employee
      stepFunction:
        functionName: request
        parameters:
          url: /employees/${inputs.id}
          method: get
  result:
    data: $.steps.fetch_employee.output.data

Next Steps