Skip to main content
This reference documents the complete YAML structure for Falcon connectors. 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

API Key Authentication

authentication:
  - custom:
      type: custom
      label: API Key
      support:
        link: https://your-connection-guide-url-here.com  # TODO: Replace with actual guide for credential setup
        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
          tooltip: Store securely - shown only once
      environments:
        - key: production
          name: Production
      testActionsIds:
        - list_users

Basic Authentication

authentication:
  - custom:
      type: custom
      label: API Key
      authorization:
        type: basic
        username: $.credentials.email
        password: $.credentials.accessToken
      configFields:
        - key: email
          label: Email
          type: text
          required: true
        - key: accessToken
          label: API Token
          type: password
          required: true
          secret: true

Custom Headers

authentication:
  - custom:
      type: custom
      label: Custom Auth
      authorization:
        type: none
        customHeaders:
          X-Api-Token: $.credentials.apiToken
          X-Account-Id: $.credentials.accountId
      configFields:
        - key: apiToken
          label: API Token
          type: password
          required: true
          secret: true

OAuth 2.0

authentication:
  - oauth2:
      type: oauth2
      label: OAuth 2.0
      support:
        link: https://your-connection-guide-url-here.com  # TODO: Replace with actual guide for credential setup
        description: Login to connect your account
      setupFields:
        - key: clientId
          label: Client ID
          type: text
          required: true
          secret: false
          placeholder: 4A98C887DABEBDF0BD8EF7D
          description: Found in Developer Portal > App Settings
        - key: clientSecret
          label: Client Secret
          type: password
          required: true
          secret: true
        - key: scopes
          label: Scopes
          type: text
          required: false
          placeholder: read write offline_access
          description: Space-separated scopes
      authorization:
        type: oauth2
        authorizationUrl: https://provider.com/oauth/authorize
        authorizationParams:
          response_type: "code"
          client_id: $.credentials.clientId
          redirect_uri: "${apiHostUri}/connect/oauth2/${connector}/callback"
          scope: $.credentials.scopes
          state: "stackone"
        tokenUrl: https://provider.com/oauth/token
        tokenExpiresIn: 1800
        tokenRefreshExpiresIn: 3600
        token: $.credentials.accessToken
        includeBearer: true
      refreshAuthentication:
        action:
          actionId: refresh_token
          categories:
            - internal
          actionType: refresh_token
          label: Refresh Token
          description: Refresh OAuth2 token
          steps:
            - stepId: refresh_token_request
              stepFunction:
                functionName: request
                parameters:
                  baseUrl: "https://provider.com"
                  url: "/oauth/token"
                  method: post
                  authorization:
                    type: basic
                    username: $.credentials.clientId
                    password: $.credentials.clientSecret
                    encoding: base64
                  args:
                    - name: Content-Type
                      value: application/x-www-form-urlencoded
                      in: headers
                    - name: grant_type
                      value: refresh_token
                      in: body
                    - name: refresh_token
                      value: $.credentials.refreshToken
                      in: body
            - stepId: map_tokens
              stepFunction:
                functionName: map_fields
                version: "2"
                parameters:
                  fields:
                    - targetFieldKey: accessToken
                      expression: $.access_token
                      type: string
                    - targetFieldKey: refreshToken
                      expression: $.refresh_token
                      type: string
                    - targetFieldKey: expiresIn
                      expression: $.expires_in
                      type: number
                  dataSource: $.steps.refresh_token_request.output.data
          result:
            data: $.steps.map_tokens.output.data
      environments:
        - key: production
          name: Production

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 (see Categories)
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

Action types define the operations your action performs. All action types can be used with or without schema mapping.
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)
Schema mapping:
  • Add schema field to map provider data to StackOne’s standardized schemas (e.g., schema: employees)
  • Without schema, the action returns provider data as-is
  • Unified actions with schemas require fieldConfigs for field mapping (see Field Configs)

Categories

Categories classify actions by API vertical, making it easy to filter and discover actions across your connector ecosystem.
CategoryDescription
hrisHR Information Systems (employees, time-off, payroll)
atsApplicant Tracking (candidates, jobs, applications)
crmCustomer Relationship Management (contacts, deals)
lmsLearning Management (courses, completions)
documentsDocument Management (files, folders)
ticketingTicketing/Projects (issues, tasks)
messagingCommunication (messages, channels)
iamIdentity & Access (users, groups, roles)
accountingAccounting & Finance (invoices, accounts)
internalInternal only, not exposed in API
Use internal for helper actions like token refresh or data lookups that shouldn’t be exposed to end users.

Inputs

Define parameters your action accepts:
inputs:
  - name: id
    type: string
    in: path
    required: true
    description: User ID

  - name: status
    type: string
    in: query
    required: false
    description: Filter by status (active, inactive, pending)

  - name: email
    type: string
    in: body
    required: true
    description: User email address

  - name: metadata
    type: object
    in: body
    required: false
    description: Additional metadata
    properties:
      - name: department
        type: string
        description: Department name
      - name: role
        type: string
        description: User role
Input types: string, number, boolean, datetime_string, object Input locations:
  • path: URL path parameter (/users/${inputs.id})
  • query: Query string (?status=active)
  • body: Request body field
  • headers: HTTP header
Use path for:
  • Resource identifiers (user ID, file ID)
  • Required route segments that the API expects in the URL
  • Values that identify which resource to access
Use query for:
  • Optional filters and search parameters
  • Pagination controls (page, limit, cursor)
  • Sorting and field selection
  • Values that modify how to retrieve resources
Use body for:
  • Data being created or updated (POST, PUT, PATCH)
  • Complex objects and nested structures
  • Bulk operations with multiple items
Use headers for:
  • Request-specific configuration (content type, accept)
  • API versioning (X-API-Version: 2)
  • Custom provider headers not covered by authentication
Most inputs are either path (for IDs) or query (for filters). Body inputs are mainly for write operations.

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
Transform provider data to output schemamap_fields
Convert field types after mappingtypecast
Merge results from multiple API callsgroup_data
Common patterns:
  • GET single resource: requestresult
  • LIST with pagination: paginated_requestresult
  • Unified action: requestmap_fieldstypecastresult
  • Multi-source data: request (x2) → group_dataresult

request - HTTP Requests

Make a single HTTP request to the provider API.
steps:
  - stepId: get_user
    description: Fetch user by ID
    stepFunction:
      functionName: request
      parameters:
        url: /users/${inputs.id}
        method: get
        args:
          - name: Accept
            value: application/json
            in: headers
          - name: include_details
            value: "{{inputs.include_details}}"
            in: query
            condition: "{{present(inputs.include_details)}}"
        response:
          collection: false
          dataKey: user
        customErrors:
          - receivedStatus: 404
            targetStatus: 400
            message: User not found
Parameters:
ParameterDescription
urlEndpoint path (supports interpolation)
methodget, post, put, patch, delete
argsRequest arguments (headers, query, body)
authorizationOverride auth for this request
response.collectionWhether response is an array
response.dataKeyExtract data from this key
response.indexFieldField to use as index
customErrorsRemap provider error responses
Always use args for request parameters. Never use a direct body field.

paginated_request - Cursor Pagination

Automatically fetches all pages of results 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:
  1. Makes initial request without cursor
  2. Extracts results from response.dataKey
  3. Checks response.nextKey for continuation cursor
  4. If cursor exists, makes next request with cursor in iterator.key
  5. Repeats until no cursor is returned
  6. Returns combined results from all pages
Parameters:
ParameterRequiredDescription
response.dataKeyYesJSONPath to the results array (e.g., data, results, items)
response.nextKeyYesJSONPath to the next cursor value (e.g., pagination.next_cursor, meta.nextPageToken)
iterator.keyYesParameter name for passing cursor to API
iterator.inYesLocation: query for URL params, body for request body
Common provider patterns:
# Simple cursor at root level
response:
  dataKey: data
  nextKey: cursor

# Nested pagination object
response:
  dataKey: results
  nextKey: pagination.next_page_token

# Header-based cursor (rare)
iterator:
  key: X-Page-Token
  in: headers
Only use paginated_request for cursor-based pagination. For offset (?offset=100) or page-based (?page=2) pagination, use a regular request step with manual cursor handling via inputs.

map_fields - Transform Data

Transform provider response data into your output schema. Essential for unified actions that map to StackOne schemas. How it works:
  1. Takes input data from dataSource (typically a previous step’s output)
  2. For each field in fields, evaluates the expression against each record
  3. Converts the result to the specified type
  4. For arrays, processes each item individually
  5. Returns transformed data with the new field structure
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
Common transformation patterns:
Map provider values to standardized enum values:
- targetFieldKey: user_type
  expression: $.account_type
  type: enum
  enumMapper:
    matcher:
      - matchExpression: '{{$.account_type == "admin"}}'
        value: administrator
      - matchExpression: '{{$.account_type == "user"}}'
        value: standard
Order matchers from most specific to least specific. The first matching expression wins.
Parameters:
ParameterRequiredDescription
dataSourceYesJSONPath to input data (e.g., $.steps.fetch_users.output.data)
fieldsYesArray of field mapping configurations
Field config options:
OptionDescription
targetFieldKeyOutput field name
expressionJSONPath or JEXL expression to extract value
typestring, number, boolean, datetime_string, enum, object
arrayWhether field is an array (default: false)
enumMapperMap provider values to standardized enums
propertiesNested field configs for object types
customMark as custom field outside schema (default: false)
hiddenExclude from output (default: false)

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.
steps:
  - stepId: combine_data
    stepFunction:
      functionName: group_data
      parameters:
        stepsDataToGroup:
          - fetch_employees
          - fetch_contractors
        isSingleRecord: false

Expression Syntax

Falcon supports three expression formats for dynamic values.

JSONPath (Preferred)

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

# Inputs
value: $.inputs.userId

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

# Nested access
expression: $.user.address.city

String Interpolation (${...})

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

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

# Environment variables
redirect_uri: ${apiHostUri}/connect/oauth2/${connector}/callback

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

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

# Transformations
value: '{{inputs.name.toUpperCase()}}'
expression: '{{$.first_name + " " + $.last_name}}'

# Ternary operations
value: '{{$.count > 0 ? $.count : 0}}'

# Default values
value: '{{inputs.limit || 100}}'
Common JEXL functions:
  • present(value) - Returns true if value exists
  • empty(value) - Returns true if value is empty/null

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, fieldConfigs map provider response to StackOne 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

Custom Enum Matchers

For large enums (like file formats with 1200+ values), use built-in matchers:
fieldConfigs:
  - targetFieldKey: file_format
    expression: $.extension
    type: enum
    enumMapper:
      matcher: "document_file_format_from_extension"
Built-in matchers include:
  • country_alpha2code_by_alpha2code
  • document_file_format_from_extension

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