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
| Term | Meaning |
|---|---|
| Workflow connector | A project that connects Stacksync to one system. Identified by its app_type, such as acme_crm. |
| Module | One step a workflow can run, such as Create records. A connector has one or more. |
| Module version | A folder modules/<module>/v<N>/. Workflows keep using the version they were built with. |
| Connection | The credentials a user stores in Stacksync for your system. Stacksync passes them to your module on each call. |
| Context | The 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 instacksync.yml. - Module folder names are lowercase words joined by underscores, such as
create_records. Version folders arevfollowed by digits, such asv1. main.pymust keep definingapp. 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. ADockerfileorgunicorn_config.pyis 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"| Field | Required | Description |
|---|---|---|
deployment_settings.workspace | Yes | Your 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.region | Yes | Where 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_type | Yes | The 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_name | Yes | The name shown in the workflow builder. |
app_settings.app_type_display | Yes | The display name of the app. Usually the same as app_name. |
app_settings.app_icon_svg_url | Yes | A 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_description | Yes | One sentence on what the connector does. |
app_settings.connection_app_type | Yes | The connection users choose for every module. See Connections and credentials. |
app_settings.options | No | Extra 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.",
)| Field | Default | Description |
|---|---|---|
module_name | The folder name in title case | Name in the workflow builder. |
module_description | "" | One-line description. |
module_category | "action" | Must stay "action". Other values work locally but the deployment fails. |
supports_action | True | The module runs as a workflow action. |
supports_trigger | False | Reserved. Connector triggers are not available yet. |
requires_credentials_for_schema | False | Set True when schema() calls your API, so Stacksync sends the connection to /schema. |
module_icon_svg_url | None | The module's icon. Defaults to the connector's icon. |
on_content_update | True | Stacksync rebuilds the form with the step's values before it runs. Keep it on for any form that changes with input. |
on_content_initialized | True | Load dropdown options when the form first opens. |
on_create, on_update, on_delete | False | Reserved 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
v1in place for changes that keep existing workflows working: a new optional field, a bug fix, a clearer label. - Add
modules/<module>/v2/next tov1for a breaking change to inputs or behavior. Both versions are served, so workflows built onv1keep working and new workflows usev2. - 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:
| Reference | Effect |
|---|---|
@v1 | The latest 1.x release. Recommended: fixes and new features arrive, breaking changes don't. |
@v1.1.1 | Exactly this release. |
@v2 | The next major version, when you are ready to move to it. |
@prod | The 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:
| Name | What it is |
|---|---|
create_app(root) | Builds the connector's web app from the project folder. Used by main.py. |
Request | What every handler receives: data, form_data, content_object_names, credentials, json. |
Credentials | The connection, read from request.credentials. |
SchemaResponse | The return value of schema(). |
ContentResponse | The return value of content(). |
ExecuteResponse | The return value of execute(). |
ManagedError | An error with a message for the user and an HTTP status. |
ModuleConfig | The module settings in config.py. |
validate_and_parse_json, validate_array, validate_object, parse_str_to_json | Turn 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.
| Endpoint | Purpose |
|---|---|
POST /<module>/v<N>/schema | The module's input form, from schema(). |
POST /<module>/v<N>/content | Dropdown options, from content(). |
POST /<module>/v<N>/execute | Runs the step, with execute(). |
GET /<module>/v<N>/context | The module's context.md. See Context. |
GET /context | The connector's context.md. |
GET /app-config | The 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/contextLimits
| Limit | Value |
|---|---|
| Request time, deployed | About 6 minutes per call. Longer calls are cut off. |
Request time, during run | 10 seconds per call. |
app_type | 24 characters. |
| Module versions per connector | 200. |
| Deployment package | Less than 32 MiB, at most 5,000 files, at most 100 MiB uncompressed. |