See Stacksync in action with a solution architect. Book a demo ↗

Workflow Connector Development Kit

The Stacksync CDK: how a workflow connector project is laid out, its settings, module versions, CDK versions and the endpoints a connector serves.

The Workflow Connector Development Kit (CDK) is the Python library you build workflow connectors with. You write each module as a few plain functions. The CDK finds them on disk and serves them as the endpoints Stacksync calls when someone builds or runs a workflow.

The Stacksync CLI creates the project, runs it on your machine and deploys it. New to both? Start with Build your first workflow connector.

Concepts

TermMeaning
Workflow connectorA project that connects Stacksync to one system. Identified by its app_type, such as acme_crm.
ModuleOne step a workflow can run, such as Create records. A connector has one or more.
Module versionA folder modules/<module>/v<N>/. Workflows keep using the version they were built with.
ConnectionThe credentials a user stores in Stacksync for your system. Stacksync passes them to your module on each call.
ContextThe context.md files that explain the connector and each module to Stacksync's AI.

Install

The CDK is installed from GitHub through the project's requirements.txt. The CLI installs it for you when you run the connector.

git+https://github.com/stacksyncdata/stacksync-workflows-cdk.git@v1#subdirectory=lib
  • Package: stacksync-cdk. Import: stacksync_cdk. It is not published on PyPI.
  • Python 3.10 or later on your machine. Deployed connectors run on Python 3.11.
  • Dependencies: Flask 3.1.3 or later, Pydantic 2 and PyYAML 6.

Project layout

stacksync workflow-connector create "Acme CRM" creates:

workflows-app-acme-crm/
├── stacksync.yml            connector settings
├── context.md               the connector's context for Stacksync's AI (required)
├── main.py                  entry point: app = create_app(...)
├── requirements.txt         the CDK and your dependencies
├── AGENTS.md                rules for AI coding assistants
├── CLAUDE.md                points Claude Code to AGENTS.md
├── README.md
├── LICENSE
├── .gitignore
├── guide/                   architecture, building, schema, content, execute and context guides
├── utils/                   code shared by modules, such as an API client
└── modules/
    └── create_records/      a sample module
        └── v1/
            ├── schema.py    the input form
            ├── content.py   dropdown options
            ├── execute.py   the action
            ├── config.py    module name, description and settings
            └── context.md   the module's context (required)

Rules for the project folder:

  • Modules are found on disk. Add a folder modules/<module>/v<N>/ and the module exists. Nothing is listed in stacksync.yml.
  • Module folder names are lowercase words joined by underscores, such as create_records. Version folders are v followed by digits, such as v1.
  • main.py must keep defining app. Stacksync serves that object once deployed.
  • The connector root may contain only main.py, stacksync.yml, requirements.txt, context.md, README.md, LICENSE, AGENTS.md, CLAUDE.md, modules/, utils/, tests/, guide/, pyproject.toml, uv.lock, requirements-dev.txt, conftest.py, pytest.ini, and hidden files. A Dockerfile or gunicorn_config.py is allowed but replaced by Stacksync's own. Anything else blocks the deployment. Keep scripts and notes outside the connector folder.

stacksync.yml

deployment_settings:
  workspace: "1234"
  region: "usnv"

app_settings:
  app_type: "acme_crm"
  app_name: "Acme CRM"
  app_type_display: "Acme CRM"
  app_icon_svg_url: "https://www.example.com/acme-crm-logo.svg"
  app_description: "Create, update and look up Acme CRM records."
  connection_app_type: "generic_api_credentials"
FieldRequiredDescription
deployment_settings.workspaceYesYour workspace: run adds the temporary connector to it, and deploy submits the connector from it. create fills it with your default workspace. You must be able to deploy to it. It doesn't limit who can use the deployed connector: see Who can use a deployed connector.
deployment_settings.regionYesWhere run registers the temporary connector: usnv (United States) or besg (Europe). Use your workspace's default region. Deployed connectors are available in every region.
app_settings.app_typeYesThe permanent id of the connector: lowercase letters and digits in words joined by single underscores, at most 24 characters, unique across Stacksync. It is part of every module id. Never change it after the first deployment.
app_settings.app_nameYesThe name shown in the workflow builder.
app_settings.app_type_displayYesThe display name of the app. Usually the same as app_name.
app_settings.app_icon_svg_urlYesA stable public https:// link to an SVG logo. It must answer directly (no redirect), be at most 512 KiB, start with <svg and contain no script.
app_settings.app_descriptionYesOne sentence on what the connector does.
app_settings.connection_app_typeYesThe connection users choose for every module. See Connections and credentials.
app_settings.optionsNoExtra app settings, passed through as they are.

Never put credentials in stacksync.yml or in code. Users store them in a Stacksync connection.

Module settings: config.py

Each module version has a config.py that exports CONFIG. Every field has a default, so set only what differs:

from stacksync_cdk import ModuleConfig

CONFIG = ModuleConfig(
    module_name="Create Records",
    module_description="Create one or more records of any object type.",
)
FieldDefaultDescription
module_nameThe folder name in title caseName in the workflow builder.
module_description""One-line description.
module_category"action"Must stay "action". Other values work locally but the deployment fails.
supports_actionTrueThe module runs as a workflow action.
supports_triggerFalseReserved. Connector triggers are not available yet.
requires_credentials_for_schemaFalseSet True when schema() calls your API, so Stacksync sends the connection to /schema.
module_icon_svg_urlNoneThe module's icon. Defaults to the connector's icon.
on_content_updateTrueStacksync rebuilds the form with the step's values before it runs. Keep it on for any form that changes with input.
on_content_initializedTrueLoad dropdown options when the form first opens.
on_create, on_update, on_deleteFalseReserved for platform hooks. Leave them off.

Module versions

The module id is <app_type>-<module>-<N>, for example acme_crm-create_records-1.

  • Start every module at v1.
  • Edit v1 in place for changes that keep existing workflows working: a new optional field, a bug fix, a clearer label.
  • Add modules/<module>/v2/ next to v1 for a breaking change to inputs or behavior. Both versions are served, so workflows built on v1 keep working and new workflows use v2.
  • Never delete a deployed version: Stacksync refuses the deployment.
  • The name, description, icon and settings of a version are recorded the first time it is deployed. Changes to them later don't appear. Ship such a change in a new version.

CDK versions

The CDK follows semantic versioning. The @ref in requirements.txt decides which release you get:

ReferenceEffect
@v1The latest 1.x release. Recommended: fixes and new features arrive, breaking changes don't.
@v1.1.1Exactly this release.
@v2The next major version, when you are ready to move to it.
@prodThe newest unreleased changes.

Change it with stacksync workflow-connector cdk <ref>. run reinstalls the moving references (@v1, @v2, @prod) each time it starts and prints the installed version. Deployed connectors install the reference in requirements.txt when they are built. The current release is 1.1.1. Deployments need 1.1.0 or later.

The library

Everything you need is imported from stacksync_cdk:

NameWhat it is
create_app(root)Builds the connector's web app from the project folder. Used by main.py.
RequestWhat every handler receives: data, form_data, content_object_names, credentials, json.
CredentialsThe connection, read from request.credentials.
SchemaResponseThe return value of schema().
ContentResponseThe return value of content().
ExecuteResponseThe return value of execute().
ManagedErrorAn error with a message for the user and an HTTP status.
ModuleConfigThe module settings in config.py.
validate_and_parse_json, validate_array, validate_object, parse_str_to_jsonTurn JSON text from a form field into Python values. They raise ValueError on bad input.

How to use them is covered in Build modules.

create_app refuses to start without app_type. It still starts when the connector breaks the connector standard, and logs each problem as Must fix before deploying: …. Logging is set to INFO unless you configure it. Use Python's logging module in handlers, and never log credentials.

Endpoints

The CDK serves these endpoints. Stacksync calls them, so you never write them yourself. You can call them on the local server that run starts, which helps with testing.

EndpointPurpose
POST /<module>/v<N>/schemaThe module's input form, from schema().
POST /<module>/v<N>/contentDropdown options, from content().
POST /<module>/v<N>/executeRuns the step, with execute().
GET /<module>/v<N>/contextThe module's context.md. See Context.
GET /contextThe connector's context.md.
GET /app-configThe connector's settings and every module with its settings. Stacksync reads it to register the modules.
GET /health{"data": {"status": "healthy"}}

Every answer is JSON with a data key. Errors answer {"error": "<message>"} with the HTTP status of the error. The request and response bodies of each endpoint are described in Build modules.

curl http://127.0.0.1:2323/app-config
curl http://127.0.0.1:2323/create_records/v1/context

Limits

LimitValue
Request time, deployedAbout 6 minutes per call. Longer calls are cut off.
Request time, during run10 seconds per call.
app_type24 characters.
Module versions per connector200.
Deployment packageLess than 32 MiB, at most 5,000 files, at most 100 MiB uncompressed.

Next steps

On this page