Run one workflow first
Your first result does not require a custom module. Build and test a workflow, publish it, and only then connect a real event.
Create a workflow
Declare typed inputs and connect every path from Start to a terminal result.
Test it
A test run shows inputs, step results, and the exact point of failure.
Publish
Publishing creates an immutable revision. Later edits stay in a new draft.
Connect an event
Select an exact module version and event, add trigger conditions, and bind event fields to inputs.
Run and observe
Enable the connection and follow events, retries, delivery queues, and execution metrics.
Create an account and test a built-in workflow before developing an integration.
What are you integrating?
The public Edge SDK supports two extension kinds. A marketplace module describes a platform; an adapter-driver adds operations for one specialized API.
Marketplace module
For a platform with orders, messages, a seller catalog, and seller-authorized actions.
- receives events
- may publish catalogs
- credentials stay in Edge
Managed adapter
For a specialized API using HMAC, TOTP, sessions, IP allowlists, or local secrets.
- declares operations
- signed manifest
- account-scoped blocks
Add your marketplace
A module translates one platform API into versioned Buywell events, actions, and catalogs. The SDK owns the Edge protocol, delivery, heartbeat, and package manifest.
1. Define the contract
Choose a stable extension ID and semver. Declare Pydantic event and action models with equivalent RU and EN field meaning.
2. Connect the provider
Start the platform client in on_start. Emit only expected events through session.emit_event and use a deterministic event_id for deduplication.
3. Add actions
Each action receives typed input and returns only its output schema. Redelivery keeps the same idempotency key.
4. Build the package
The local builder generates Manifest v2, pins files and dependencies, calculates the digest, and signs a deterministic archive.
5. Verify in Edge
Install in dev mode, connect a test account, and verify health, one event, redelivery, and an upgrade from the previous version.
Install the public SDK
The Edge repository contains the daemon, CLI, package builder, and the same Python SDK used by official integrations.
github.com/moreveal/buywell-edge ↗git clone https://github.com/moreveal/buywell-edge.git
cd buywell-edge
python3.12 -m venv .venv
. .venv/bin/activate
pip install -e '.[test]'
# From your extension repository:
buywell-edge module build extension:extension --source .import asyncio
from pydantic import BaseModel
from buywell_edge_sdk import contract_field, module
class PaidOrder(BaseModel):
order_id: str = contract_field(
label={"ru": "Номер заказа", "en": "Order ID"},
)
buyer: str = contract_field(
label={"ru": "Покупатель", "en": "Buyer"},
)
extension = module(
extension_id="example.market",
version="1.0.0",
display_name={"ru": "Пример", "en": "Example"},
publisher="Example",
entrypoint="extension:extension",
network_domains=["api.example.com"],
)
@extension.event(
"commerce.purchase.created",
"1.0.0",
payload_model=PaidOrder,
identity_fields=["order_id"],
display_name={"ru": "Заказ оплачен", "en": "Order paid"},
)
async def paid_order_contract(_context):
return {}
async def forward_order(session, order):
await session.emit_event(
"commerce.purchase.created",
"1.0.0",
PaidOrder(order_id=order.id, buyer=order.buyer).model_dump(),
{"shopId": order.shop_id},
event_id=f"example:{order.id}:paid",
)Do not implement the Buywell WebSocket, leases, outbox, or heartbeat in an extension; Edge owns them.
Never put platform tokens in the package, manifest, or workflow. Configuration secrets belong to the local connection.
Trigger selectors filter launches; binding fields and catalogs supply typed values. They are different contracts.
Changing a public event, action, or field requires a new contract version. Updating internal provider code may not.
Build a signed adapter-driver
Use a managed adapter when a request cannot honestly be represented by a regular cloud HTTP adapter. The driver runs near the user, and its blocks appear only after signature verification and a live connection.
- HMAC or a custom per-request signature
- TOTP, refresh sessions, or device identity
- supplier-side IP allowlisting
- a local library or pinned upstream
- secrets must not leave the user's device
from pydantic import BaseModel, SecretStr
from buywell_edge_sdk import adapter_driver, contract_field
class Settings(BaseModel):
api_key: SecretStr
class ReserveRequest(BaseModel):
item_id: str = contract_field(
label={"ru": "ID товара", "en": "Item ID"},
)
class ReserveResult(BaseModel):
reserved: bool = contract_field(
label={"ru": "Зарезервировано", "en": "Reserved"},
)
driver = adapter_driver(
extension_id="adapter.example-supplier",
version="1.0.0",
display_name={"ru": "Поставщик", "en": "Supplier"},
publisher="Example",
entrypoint="driver:driver",
config_model=Settings,
network_domains=["api.example.com"],
)
@driver.operation(
"adapter.example-supplier/reserve",
"1.0.0",
input_model=ReserveRequest,
output_model=ReserveResult,
display_name={"ru": "Зарезервировать", "en": "Reserve"},
)
async def reserve(context, value):
# Sign the provider request here with context.secrets["api_key"].
return ReserveResult(reserved=True)Secrets stay local
SecretStr and secret metadata become configuration.secretFields, never workflow inputs.
Network is bounded
The package declares network_domains up front. Permission is not inferred from a user URL.
Contract is signed
Edge sends the manifest, digest, and Ed25519 signature; Buywell verifies them before registering operations.
Visibility is isolated
Operations exist only for the account with that verified connection and disappear with it.
Pydantic models become operation schemas. contract_field supplies localized UI labels. The signed managedAdapter and adapterOperations sections create account-scoped workflow blocks; no duplicate cloud definition is required.
Read working implementations
Official extensions use the same public SDK and package format available to third-party developers.
moreveal/buywell-runtimes
The buywell-runtimes repository is the source of truth for official integration source and immutable release packages.
FunPay Cardinal
Orders, status changes, messages, replies, buyer forms, and catalog.
View source ↗Marketplace moduleGGSel Seller
Purchases, messages, replies, forms, and seller product catalog.
View source ↗Marketplace modulePlayerok Universal
Sales, messages, contextual replies, forms, and category catalogs.
View source ↗Managed adapterNSGifts
Request signing, TOTP, IP allowlisting, diagnostics, and live stock catalog.
View source ↗buywell-automationControl plane, workflow contracts, validation, editor, and execution.
buywell-edgeUser-owned daemon, CLI, package builder, public SDK, and binary releases.
buywell-runtimesOfficial provider implementations, tests, pinned upstreams, and package releases.
Git remains the source of truth
A version already delivered to a user is immutable. New code gets a new version, while existing workflows keep their previous contract.
Source
Commit implementation, schemas, RU/EN guides, changelog, and exact dependencies in a public or reviewable git commit.
Checks
Run unit and contract tests, self-test, and an upgrade test. The builder must not read files outside the source tree.
Release
Create the tag and release assets from that commit. Never overwrite a published archive, digest, or version.
Delivery
The official catalog points to an exact release URL and SHA-256. Edge downloads, verifies, and cuts the connection over atomically.
Migration
A new version installs alongside the old one. Contract changes and published-workflow migration are always explicit.
1.0.0immutable1.1.0new packageNew integrations, Edge and runtime releases, product changes, and migration notes.
Complete Manifest reference
The reference below is generated from the current validator and uses checked examples. For Edge packages, prefer typed SDK declarations over hand-written JSON.
Everything manifest.json may declare
Every example on this page is checked by the same contract used when a package is installed. Start with the compact example and use the field reference for events, data sources, nodes, and compatibility rules.
For Buywell Edge, the Python SDK generates Manifest v2, schemas, file inventory, digest, and signature from typed declarations. A signed adapter-driver supplies its managed adapter and operation fields directly when Edge connects; Buywell verifies the signature before showing its user-scoped blocks. Existing modules can preserve the entire v1 manifest as their compatibility contract.
{
"schemaVersion": 1,
"protocolVersion": "1.0.0",
"module": {
"id": "example.delivery",
"version": "1.0.0",
"displayName": "Example Delivery",
"description": "A minimal module package example.",
"publisher": "Example developer",
"supportedPlatforms": [
"Example Platform"
]
},
"nodes": [
{
"type": "example.delivery/send-message",
"version": "1.0.0",
"kind": "action",
"displayName": "Send message",
"localization": {
"ru": {
"displayName": "Отправить сообщение"
},
"en": {
"displayName": "Send message"
}
},
"inputSchema": {
"type": "object",
"properties": {
"message": {
"type": "string"
}
},
"required": [
"message"
],
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {},
"required": [],
"additionalProperties": false
},
"configSchema": {
"type": "object",
"properties": {},
"required": [],
"additionalProperties": false
},
"requiredEventContext": [
{
"eventType": "commerce.purchase.created",
"eventVersion": "1.0.0",
"source": "scope",
"path": "conversationId"
}
],
"ui": {
"category": "Messages",
"icon": "message"
}
}
],
"events": [
{
"type": "commerce.purchase.created",
"version": "1.0.0",
"displayName": "Purchase received",
"localization": {
"ru": {
"displayName": "Получена покупка"
},
"en": {
"displayName": "Purchase received"
}
},
"payloadSchema": {
"type": "object",
"properties": {
"purchaseId": {
"type": "string"
},
"recipient": {
"type": "string"
}
},
"required": [
"purchaseId",
"recipient"
],
"additionalProperties": false
},
"scopeSchema": {
"type": "object",
"properties": {
"conversationId": {
"type": "string"
},
"orderUrl": {
"type": "string"
}
},
"required": [
"conversationId",
"orderUrl"
],
"additionalProperties": false
},
"selectors": [],
"bindingFields": [
{
"id": "recipient",
"source": "payload",
"path": "recipient",
"valueSchema": {
"type": "string"
},
"displayName": "Recipient",
"localization": {
"ru": {
"displayName": "Получатель"
},
"en": {
"displayName": "Recipient"
}
},
"recommended": true,
"availability": "always"
}
],
"identityFields": [
"purchaseId"
],
"buyerForm": {
"returnUrl": {
"source": "scope",
"path": "orderUrl",
"allowedOrigins": [
"https://shop.example"
]
}
},
"ui": {
"category": "Sales",
"icon": "cart"
}
}
],
"abstractions": [
{
"abstractionId": "messaging.send-in-context",
"abstractionVersion": "1.0.0",
"nodeType": "example.delivery/send-message",
"nodeVersion": "1.0.0"
}
],
"package": {
"branding": {
"icon": "assets/icon.png"
},
"artifact": {
"path": "edge/driver.py",
"filename": "driver.py"
},
"guides": {
"installation": {
"ru": "guides/install.ru.md",
"en": "guides/install.en.md"
},
"readme": {
"ru": "guides/README.ru.md",
"en": "guides/README.en.md"
},
"changelog": {
"ru": "guides/CHANGELOG.ru.md",
"en": "guides/CHANGELOG.en.md"
}
},
"compatibility": {
"environments": [
"Example Runtime 1.x"
]
},
"release": {
"critical": false
}
}
}What every field controls and which values it accepts
Root manifest7 fields
Package identity, contents, and public capabilities.
schemaVersion1requiredMay be omitted; parsing and the canonical manifest set it to 1. · default: 1
protocolVersion"1.0.0"requiredMay be omitted; parsing and the canonical manifest set it to 1.0.0. · default: "1.0.0"
moduleobjectrequiredIdentity, publisher, and supported platforms. · unknown fields are rejected
nodesarrayrequiredBlocks owned by this package. · maximum items: 100
eventsarrayVersioned module events. · maximum items: 100
abstractionsarrayImplementations of platform-neutral contracts. · maximum items: 100
packageobjectArchive files, compatibility, and release metadata. · unknown fields are rejected
module10 fields
Purpose, accepted values, and validation limits for this part of the manifest.
module.idstringrequiredLowercase dot/dash-separated segments, such as example.delivery. · pattern: ^[a-z0-9]+(?:[.-][a-z0-9]+)+$
module.versionstringrequiredx.y.z with an optional prerelease suffix. · pattern: ^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$
module.displayNamestringrequiredBase name; RU/EN localization may replace it. · minimum length: 1 · maximum length: 120
module.descriptionstringOptional short user-facing description. · maximum length: 2000
module.publisherstringrequiredPackage publisher name. · minimum length: 1 · maximum length: 160
module.supportedPlatformsarrayrequiredHuman-readable supported platform names. · minimum items: 1 · maximum items: 50
module.documentationobjectOptional HTTP(S) homepageUrl, supportUrl, and sourceUrl. · unknown fields are rejected
module.documentation.homepageUrlstringLinks to the module or publisher homepage. · maximum length: 2048 · format: uri
module.documentation.supportUrlstringTells users where to get support for the module. · maximum length: 2048 · format: uri
module.documentation.sourceUrlstringLinks to the module source when the publisher makes it available. · maximum length: 2048 · format: uri
nodes[]31 fields
Purpose, accepted values, and validation limits for this part of the manifest.
nodes[].typestringrequiredMust begin with the exact module.id/ namespace. · pattern: ^[a-z0-9]+(?:[.-][a-z0-9]+)*\/[a-z][a-z0-9.-]*$
nodes[].versionstringrequiredNode contract version. · pattern: ^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$
nodes[].kind"action" | "condition"requiredcondition requires branches; action forbids branches.
nodes[].displayNamestringrequireddisplayName is required; description and RU/EN localization are optional. · minimum length: 1 · maximum length: 120
nodes[].descriptionstringdisplayName is required; description and RU/EN localization are optional. · maximum length: 2000
nodes[].localizationobjectdisplayName is required; description and RU/EN localization are optional. · unknown fields are rejected
nodes[].localization.ruobjectRequired non-empty UTF-8 Markdown guide. · unknown fields are rejected
nodes[].localization.ru.displayNamestringdisplayName is required; description and RU/EN localization are optional. · minimum length: 1 · maximum length: 120
nodes[].localization.ru.descriptionstringdisplayName is required; description and RU/EN localization are optional. · maximum length: 2000
nodes[].localization.enobjectOptional translation; RU is the fallback. · unknown fields are rejected
nodes[].localization.en.displayNamestringdisplayName is required; description and RU/EN localization are optional. · minimum length: 1 · maximum length: 120
nodes[].localization.en.descriptionstringdisplayName is required; description and RU/EN localization are optional. · maximum length: 2000
nodes[].inputSchemavaluerequiredAction or condition inputs.
nodes[].outputSchemavaluerequiredDefault to closed empty objects. · default: {"type":"object","properties":{},"required":[],"additionalProperties":false}
nodes[].configSchemavaluerequiredDefault to closed empty objects. · default: {"type":"object","properties":{},"required":[],"additionalProperties":false}
nodes[].requiredEventContextarrayExisting eventType@version and payload/scope paths. · maximum items: 100
nodes[].requiredEventContext[].eventTypestringrequiredSelects the event whose context the node needs to run. · pattern: ^[a-z0-9]+(?:[.-][a-z0-9]+)+$
nodes[].requiredEventContext[].eventVersionstringrequiredPins the exact required event version. · pattern: ^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$
nodes[].requiredEventContext[].source"payload" | "scope"requiredThe path must exist in the selected schema.
nodes[].requiredEventContext[].pathstringrequiredThe single Edge driver stored in the archive. · minimum length: 1 · maximum length: 512
nodes[].branchesarraycondition only; every branch contains id and label. · minimum items: 2 · maximum items: 20
nodes[].branches[].idstringrequiredLowercase dot/dash-separated segments, such as example.delivery. · pattern: ^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$
nodes[].branches[].labelstringrequiredNames a condition output as users see it in the editor. · minimum length: 1 · maximum length: 120
nodes[].retryPolicyobjectmaxAttempts, initialBackoffMs, and maxBackoffMs within contract limits. · unknown fields are rejected
nodes[].retryPolicy.maxAttemptsintegerrequiredLimits the total number of node execution attempts. · ≥ 1 · ≤ 10
nodes[].retryPolicy.initialBackoffMsintegerrequiredSets the delay before the first node retry. · ≥ 0 · ≤ 300000
nodes[].retryPolicy.maxBackoffMsintegerrequiredCaps the delay between node retries. · ≥ 0 · ≤ 3600000
nodes[].uiobjectrequiredcategory, #RRGGBB color, and icon. · default: {"category":"Other"} · unknown fields are rejected
nodes[].ui.categorystringrequiredPlaces the node in a section of the block library. · default: "Other" · minimum length: 1 · maximum length: 80
nodes[].ui.colorstringSets the node accent color in the editor. · pattern: ^#[0-9A-Fa-f]{6}$
nodes[].ui.iconstringPNG, JPEG, or WebP with content matching its extension. · minimum length: 1 · maximum length: 80
events[]111 fields
Purpose, accepted values, and validation limits for this part of the manifest.
events[].typestringrequiredVersioned event identity. · pattern: ^[a-z0-9]+(?:[.-][a-z0-9]+)+$
events[].versionstringrequiredVersioned event identity. · pattern: ^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$
events[].displayNamestringrequireddisplayName is required; description is optional and limited to 2,000 characters. · minimum length: 1 · maximum length: 120
events[].descriptionstringdisplayName is required; description is optional and limited to 2,000 characters. · maximum length: 2000
events[].localizationobjectdisplayName is required; description and RU/EN localization are optional. · unknown fields are rejected
events[].localization.ruobjectRequired non-empty UTF-8 Markdown guide. · unknown fields are rejected
events[].localization.ru.displayNamestringdisplayName is required; description is optional and limited to 2,000 characters. · minimum length: 1 · maximum length: 120
events[].localization.ru.descriptionstringdisplayName is required; description is optional and limited to 2,000 characters. · maximum length: 2000
events[].localization.enobjectOptional translation; RU is the fallback. · unknown fields are rejected
events[].localization.en.displayNamestringdisplayName is required; description is optional and limited to 2,000 characters. · minimum length: 1 · maximum length: 120
events[].localization.en.descriptionstringdisplayName is required; description is optional and limited to 2,000 characters. · maximum length: 2000
events[].payloadSchemavaluerequiredEvent body; every identityFields path must exist and be required.
events[].scopeSchemavaluerequiredExecution context. Defaults to a closed empty object. · default: {"type":"object","properties":{},"required":[],"additionalProperties":false}
events[].selectorsarrayrequiredLaunch filter fields; defaults to []. · default: [] · maximum items: 100
events[].selectors[].idstringrequiredLowercase dot/dash-separated segments, such as example.delivery. · pattern: ^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$
events[].selectors[].displayNamestringrequireddisplayName is required; description is optional and limited to 2,000 characters. · minimum length: 1 · maximum length: 120
events[].selectors[].source"payload" | "scope"requiredThe path must exist in the selected schema.
events[].selectors[].pathstringrequiredThe single Edge driver stored in the archive. · minimum length: 1 · maximum length: 512
events[].selectors[].operatorsarrayrequiredexists, equals, not-equals, in, contains, starts-with, ends-with, or matches. · minimum items: 1 · maximum items: 8
events[].selectors[].localizationobjectdisplayName is required; description and RU/EN localization are optional. · unknown fields are rejected
events[].selectors[].localization.ruobjectRequired non-empty UTF-8 Markdown guide. · unknown fields are rejected
events[].selectors[].localization.ru.displayNamestringdisplayName is required; description is optional and limited to 2,000 characters. · minimum length: 1 · maximum length: 120
events[].selectors[].localization.ru.descriptionstringdisplayName is required; description is optional and limited to 2,000 characters. · maximum length: 2000
events[].selectors[].localization.enobjectOptional translation; RU is the fallback. · unknown fields are rejected
events[].selectors[].localization.en.displayNamestringdisplayName is required; description is optional and limited to 2,000 characters. · minimum length: 1 · maximum length: 120
events[].selectors[].localization.en.descriptionstringdisplayName is required; description is optional and limited to 2,000 characters. · maximum length: 2000
events[].bindingFieldsarrayTyped payload/scope values exposed as input sources. · maximum items: 200
events[].bindingFields[].idstringrequiredLowercase dot/dash-separated segments, such as example.delivery. · pattern: ^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$
events[].bindingFields[].source"payload" | "scope"requiredThe path must exist in the selected schema.
events[].bindingFields[].pathstringrequiredThe single Edge driver stored in the archive. · minimum length: 1 · maximum length: 512
events[].bindingFields[].valueSchemavaluerequiredType must match the declared path type.
events[].bindingFields[].displayNamestringrequireddisplayName is required; description is optional and limited to 2,000 characters. · minimum length: 1 · maximum length: 120
events[].bindingFields[].descriptionstringdisplayName is required; description is optional and limited to 2,000 characters. · maximum length: 2000
events[].bindingFields[].localizationobjectdisplayName is required; description and RU/EN localization are optional. · unknown fields are rejected
events[].bindingFields[].localization.ruobjectRequired non-empty UTF-8 Markdown guide. · unknown fields are rejected
events[].bindingFields[].localization.ru.displayNamestringdisplayName is required; description is optional and limited to 2,000 characters. · minimum length: 1 · maximum length: 120
events[].bindingFields[].localization.ru.descriptionstringdisplayName is required; description is optional and limited to 2,000 characters. · maximum length: 2000
events[].bindingFields[].localization.enobjectOptional translation; RU is the fallback. · unknown fields are rejected
events[].bindingFields[].localization.en.displayNamestringdisplayName is required; description is optional and limited to 2,000 characters. · minimum length: 1 · maximum length: 120
events[].bindingFields[].localization.en.descriptionstringdisplayName is required; description is optional and limited to 2,000 characters. · maximum length: 2000
events[].bindingFields[].categorystringGroups the value in the data-source picker. · minimum length: 1 · maximum length: 80
events[].bindingFields[].orderintegerOrders the value among other event data sources. · ≥ 0 · ≤ 10000
events[].bindingFields[].recommendedbooleanUI hints and data-handling metadata.
events[].bindingFields[].nullablebooleanUI hints and data-handling metadata.
events[].bindingFields[].availability"always" | "when-present"requiredDefaults to always. · default: "always"
events[].bindingFields[].sensitivebooleanUI hints and data-handling metadata.
events[].bindingFields[].keyedobjectFor an object path, declares guardPath and guardParameter for a parameterized string value. · unknown fields are rejected
events[].bindingFields[].keyed.guardPathstringrequiredChecks context before reading a parameterized key from an object field. · minimum length: 1 · maximum length: 512
events[].bindingFields[].keyed.guardParameterstringrequiredNames the binding parameter whose value must match guardPath. · pattern: ^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$
events[].bindingCatalogsarrayDeclares category, product, or other scope catalogs from which users can select event context. · maximum items: 20
events[].bindingCatalogs[].idstringrequiredLowercase dot/dash-separated segments, such as example.delivery. · pattern: ^[a-z0-9]+(?:[.-][a-z0-9]+)+$
events[].bindingCatalogs[].versionstringrequiredVersioned event identity. · pattern: ^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$
events[].bindingCatalogs[].displayNamestringrequireddisplayName is required; description is optional and limited to 2,000 characters. · minimum length: 1 · maximum length: 120
events[].bindingCatalogs[].localizationobjectdisplayName is required; description and RU/EN localization are optional. · unknown fields are rejected
events[].bindingCatalogs[].localization.ruobjectRequired non-empty UTF-8 Markdown guide. · unknown fields are rejected
events[].bindingCatalogs[].localization.ru.displayNamestringdisplayName is required; description is optional and limited to 2,000 characters. · minimum length: 1 · maximum length: 120
events[].bindingCatalogs[].localization.ru.descriptionstringdisplayName is required; description is optional and limited to 2,000 characters. · maximum length: 2000
events[].bindingCatalogs[].localization.enobjectOptional translation; RU is the fallback. · unknown fields are rejected
events[].bindingCatalogs[].localization.en.displayNamestringdisplayName is required; description is optional and limited to 2,000 characters. · minimum length: 1 · maximum length: 120
events[].bindingCatalogs[].localization.en.descriptionstringdisplayName is required; description is optional and limited to 2,000 characters. · maximum length: 2000
events[].bindingCatalogs[].scopeobjectrequiredDefines the selector and parameter that bind a catalog choice to event context. · unknown fields are rejected
events[].bindingCatalogs[].scope.displayNamestringrequireddisplayName is required; description is optional and limited to 2,000 characters. · minimum length: 1 · maximum length: 120
events[].bindingCatalogs[].scope.localizationobjectdisplayName is required; description and RU/EN localization are optional. · unknown fields are rejected
events[].bindingCatalogs[].scope.localization.ruobjectRequired non-empty UTF-8 Markdown guide. · unknown fields are rejected
events[].bindingCatalogs[].scope.localization.ru.displayNamestringdisplayName is required; description is optional and limited to 2,000 characters. · minimum length: 1 · maximum length: 120
events[].bindingCatalogs[].scope.localization.ru.descriptionstringdisplayName is required; description is optional and limited to 2,000 characters. · maximum length: 2000
events[].bindingCatalogs[].scope.localization.enobjectOptional translation; RU is the fallback. · unknown fields are rejected
events[].bindingCatalogs[].scope.localization.en.displayNamestringdisplayName is required; description is optional and limited to 2,000 characters. · minimum length: 1 · maximum length: 120
events[].bindingCatalogs[].scope.localization.en.descriptionstringdisplayName is required; description is optional and limited to 2,000 characters. · maximum length: 2000
events[].bindingCatalogs[].scope.selectorIdstringrequiredReferences an event selector with the equals operator that constrains the selected scope. · pattern: ^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$
events[].bindingCatalogs[].scope.guardParameterstringrequiredNames the parameter that passes the selected catalog value into keyed fields. · pattern: ^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$
events[].bindingCatalogs[].valueFieldIdstringrequiredReferences the keyed field containing a catalog item's internal value. · pattern: ^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$
events[].bindingCatalogs[].choiceFieldIdstringrequiredReferences the keyed field containing the catalog choice shown to users. · pattern: ^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$
events[].identityFieldsarrayrequiredPayload paths that form event identity. · minimum items: 1 · maximum items: 20
events[].buyerFormobjectDeclares a safe store return for the public buyer form. · unknown fields are rejected
events[].buyerForm.returnUrlobjectrequiredDefines the return URL source and allowed stores. · unknown fields are rejected
events[].buyerForm.returnUrl.source"payload" | "scope"requiredSelects payload or scope as the source of the return URL.
events[].buyerForm.returnUrl.pathstringrequiredPoints to the guaranteed string path containing the return URL. · minimum length: 1 · maximum length: 512
events[].buyerForm.returnUrl.allowedOriginsarrayrequiredRestricts redirects to an exact list of allowed HTTPS origins. · minimum items: 1 · maximum items: 20
events[].inputResolversarrayVersioned immediate/deferred data sources; messaging.collect-input enables chat and may coexist with buyerForm. · maximum items: 50
events[].inputResolvers[].idstringrequiredLowercase dot/dash-separated segments, such as example.delivery. · pattern: ^[a-z0-9]+(?:[.-][a-z0-9]+)+$
events[].inputResolvers[].versionstringrequiredVersioned event identity. · pattern: ^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$
events[].inputResolvers[].abstractionIdstringOptional exact Buywell abstraction; both fields are declared together. · pattern: ^[a-z0-9]+(?:[.-][a-z0-9]+)+$
events[].inputResolvers[].abstractionVersionstringOptional exact Buywell abstraction; both fields are declared together. · pattern: ^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$
events[].inputResolvers[].displayNamestringrequireddisplayName is required; description is optional and limited to 2,000 characters. · minimum length: 1 · maximum length: 120
events[].inputResolvers[].descriptionstringdisplayName is required; description is optional and limited to 2,000 characters. · maximum length: 2000
events[].inputResolvers[].outputSchemavaluerequiredExact returned value type.
events[].inputResolvers[].parameterSchemavalueFor an abstraction, it must exactly match its versioned contract.
events[].inputResolvers[].mode"immediate" | "deferred"requiredProduction executes deferred; immediate is not activated as a remote job.
events[].inputResolvers[].localizationobjectdisplayName is required; description and RU/EN localization are optional. · unknown fields are rejected
events[].inputResolvers[].localization.ruobjectRequired non-empty UTF-8 Markdown guide. · unknown fields are rejected
events[].inputResolvers[].localization.ru.displayNamestringdisplayName is required; description is optional and limited to 2,000 characters. · minimum length: 1 · maximum length: 120
events[].inputResolvers[].localization.ru.descriptionstringdisplayName is required; description is optional and limited to 2,000 characters. · maximum length: 2000
events[].inputResolvers[].localization.enobjectOptional translation; RU is the fallback. · unknown fields are rejected
events[].inputResolvers[].localization.en.displayNamestringdisplayName is required; description is optional and limited to 2,000 characters. · minimum length: 1 · maximum length: 120
events[].inputResolvers[].localization.en.descriptionstringdisplayName is required; description is optional and limited to 2,000 characters. · maximum length: 2000
events[].inputResolvers[].requiredContextarrayEvery payload/scope path exists and is guaranteed required. · maximum items: 50
events[].inputResolvers[].requiredContext[].source"payload" | "scope"requiredThe path must exist in the selected schema.
events[].inputResolvers[].requiredContext[].pathstringrequiredThe single Edge driver stored in the archive. · minimum length: 1 · maximum length: 512
events[].inputResolvers[].timeoutMsintegerOptional wait limit. · ≥ 100 · ≤ 300000
events[].inputResolvers[].retryobjectmaxAttempts 1…10 and initialBackoffMs 0…300000. · unknown fields are rejected
events[].inputResolvers[].retry.maxAttemptsintegerrequiredLimits attempts to obtain deferred data. · ≥ 1 · ≤ 10
events[].inputResolvers[].retry.initialBackoffMsintegerrequiredSets the delay before retrying deferred data resolution. · ≥ 0 · ≤ 300000
events[].inputResolvers[].sensitivebooleanUI hints and data-handling metadata.
events[].inputResolvers[].uiobjectcategory defaults to Other; icon is optional. · unknown fields are rejected
events[].inputResolvers[].ui.categorystringGroups the data resolver in the editor. · minimum length: 1 · maximum length: 80
events[].inputResolvers[].ui.orderintegerOrders the data resolver in the editor. · ≥ 0 · ≤ 10000
events[].inputResolvers[].ui.recommendedbooleanUI hints and data-handling metadata.
events[].uiobjectrequiredcategory defaults to Other; icon is optional. · default: {"category":"Other"} · unknown fields are rejected
events[].ui.categorystringrequiredPlaces the event in a section of the trigger picker. · default: "Other" · minimum length: 1 · maximum length: 80
events[].ui.iconstringPNG, JPEG, or WebP with content matching its extension. · minimum length: 1 · maximum length: 80
abstractions[]4 fields
Purpose, accepted values, and validation limits for this part of the manifest.
abstractions[].abstractionIdstringrequiredSelects the stable neutral-action contract implemented by the module. · pattern: ^[a-z0-9]+(?:[.-][a-z0-9]+)+$
abstractions[].abstractionVersionstringrequiredPins the exact version of that neutral-action contract. · pattern: ^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$
abstractions[].nodeTypestringrequiredReferences an existing node owned by this module.id. · pattern: ^[a-z0-9]+(?:[.-][a-z0-9]+)*\/[a-z][a-z0-9.-]*$
abstractions[].nodeVersionstringrequiredReferences an existing node owned by this module.id. · pattern: ^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$
package25 fields
Purpose, accepted values, and validation limits for this part of the manifest.
package.brandingobjectDescribes the module visuals included in the archive. · unknown fields are rejected
package.branding.iconstringrequiredPNG, JPEG, or WebP with content matching its extension. · minimum length: 1 · maximum length: 240
package.artifactobjectrequiredPoints to the executable Edge driver for this module version. · unknown fields are rejected
package.artifact.pathstringrequiredThe single Edge driver stored in the archive. · minimum length: 1 · maximum length: 240
package.artifact.filenamestringDownload filename; never a URL. · minimum length: 1 · maximum length: 180
package.guidesobjectrequiredCollects installation and maintenance guidance for the module. · unknown fields are rejected
package.guides.installationobjectrequiredContains the required Russian installation guide and optional English translation. · unknown fields are rejected
package.guides.installation.rustringrequiredRequired non-empty UTF-8 Markdown guide. · minimum length: 1 · maximum length: 240
package.guides.installation.enstringOptional translation; RU is the fallback. · minimum length: 1 · maximum length: 240
package.guides.readmeobjectContains the localized module overview shown in About. · unknown fields are rejected
package.guides.readme.rustringrequiredPoints to the required Russian About Markdown when a readme is declared. · minimum length: 1 · maximum length: 240
package.guides.readme.enstringPoints to the optional English readme; the Russian file is the fallback. · minimum length: 1 · maximum length: 240
package.guides.changelogobjectContains the localized, version-by-version user changelog. · unknown fields are rejected
package.guides.changelog.rustringrequiredPoints to the required Russian changelog Markdown when a changelog is declared. · minimum length: 1 · maximum length: 240
package.guides.changelog.enstringPoints to the optional English changelog; the Russian file is the fallback. · minimum length: 1 · maximum length: 240
package.guides.updateUrlstringLinks to module update instructions. · maximum length: 2048 · format: uri
package.guides.rollbackUrlstringLinks to rollback instructions. · maximum length: 2048 · format: uri
package.guides.troubleshootingUrlstringLinks to troubleshooting guidance. · maximum length: 2048 · format: uri
package.guides.removalUrlstringLinks to safe module removal instructions. · maximum length: 2048 · format: uri
package.compatibilityobjectrequiredDeclares the service versions and environments where the package can run. · unknown fields are rejected
package.compatibility.minimumBuywellVersionstringOptional minimum compatible service version. · pattern: ^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$
package.compatibility.environmentsarrayrequiredSupported Buywell Edge environments. · minimum items: 1 · maximum items: 30
package.releaseobjectrequiredCarries release importance and changelog information. · default: {"critical":false} · unknown fields are rejected
package.release.criticalbooleanrequiredDefaults to false. · default: false
package.release.changelogUrlstringOptional release changelog. · maximum length: 2048 · format: uri
Neutral actions a module may implement
An abstraction lets a workflow request a familiar action without depending on one platform. A compatible module binds that action to its own node.
messaging.send-in-context@1.0.0actionSend message
Send a message in the current conversation.
- Workflow block
abstract/send-in-context@1.0.0- Inputs
message*- Known implementations
example.delivery/send-message@1.0.0
Extended neutral manifest1 events · 2 nodes
This example demonstrates events, filters, workflow data, resolvers, actions, and conditions executed through Buywell Edge.
{
"schemaVersion": 1,
"protocolVersion": "1.0.0",
"module": {
"id": "example.delivery",
"version": "1.1.0",
"displayName": "Example Delivery",
"description": "A complete production-compatible manifest example.",
"publisher": "Example developer",
"supportedPlatforms": [
"Example Platform"
]
},
"nodes": [
{
"type": "example.delivery/send-message",
"version": "1.0.0",
"kind": "action",
"displayName": "Send message",
"localization": {
"ru": {
"displayName": "Отправить сообщение"
},
"en": {
"displayName": "Send message"
}
},
"inputSchema": {
"type": "object",
"properties": {
"message": {
"type": "string"
}
},
"required": [
"message"
],
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {},
"required": [],
"additionalProperties": false
},
"configSchema": {
"type": "object",
"properties": {},
"required": [],
"additionalProperties": false
},
"requiredEventContext": [
{
"eventType": "commerce.purchase.created",
"eventVersion": "1.0.0",
"source": "scope",
"path": "conversationId"
}
],
"ui": {
"category": "Messages",
"icon": "message"
}
},
{
"type": "example.delivery/has-recipient",
"version": "1.0.0",
"kind": "condition",
"displayName": "Recipient is present",
"localization": {
"ru": {
"displayName": "Получатель указан"
},
"en": {
"displayName": "Recipient is present"
}
},
"inputSchema": {
"type": "object",
"properties": {
"recipient": {
"type": "string"
}
},
"required": [
"recipient"
],
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {},
"required": [],
"additionalProperties": false
},
"configSchema": {
"type": "object",
"properties": {},
"required": [],
"additionalProperties": false
},
"branches": [
{
"id": "yes",
"label": "Yes"
},
{
"id": "no",
"label": "No"
}
],
"ui": {
"category": "Checks",
"icon": "question"
}
}
],
"events": [
{
"type": "commerce.purchase.created",
"version": "1.0.0",
"displayName": "Purchase received",
"localization": {
"ru": {
"displayName": "Получена покупка"
},
"en": {
"displayName": "Purchase received"
}
},
"payloadSchema": {
"type": "object",
"properties": {
"purchaseId": {
"type": "string"
},
"recipient": {
"type": "string"
},
"categoryId": {
"type": "string"
},
"customFields": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"purchaseId",
"recipient"
],
"additionalProperties": false
},
"scopeSchema": {
"type": "object",
"properties": {
"conversationId": {
"type": "string"
},
"orderUrl": {
"type": "string"
}
},
"required": [
"conversationId",
"orderUrl"
],
"additionalProperties": false
},
"selectors": [
{
"id": "category-id",
"displayName": "Category",
"source": "payload",
"path": "categoryId",
"operators": [
"equals"
]
},
{
"id": "recipient",
"displayName": "Recipient",
"source": "payload",
"path": "recipient",
"operators": [
"exists",
"equals",
"contains"
],
"localization": {
"ru": {
"displayName": "Получатель"
},
"en": {
"displayName": "Recipient"
}
}
}
],
"bindingFields": [
{
"id": "recipient",
"source": "payload",
"path": "recipient",
"valueSchema": {
"type": "string"
},
"displayName": "Recipient",
"localization": {
"ru": {
"displayName": "Получатель"
},
"en": {
"displayName": "Recipient"
}
},
"recommended": true,
"availability": "always"
},
{
"id": "custom-field",
"source": "payload",
"path": "customFields",
"valueSchema": {
"type": "string"
},
"displayName": "Custom field",
"localization": {
"ru": {
"displayName": "Дополнительное поле"
},
"en": {
"displayName": "Custom field"
}
},
"availability": "when-present",
"keyed": {
"guardPath": "categoryId",
"guardParameter": "categoryId"
}
}
],
"bindingCatalogs": [
{
"id": "example.categories",
"version": "1.0.0",
"displayName": "Category fields",
"scope": {
"displayName": "Category",
"selectorId": "category-id",
"guardParameter": "categoryId"
},
"valueFieldId": "custom-field",
"choiceFieldId": "custom-field"
}
],
"identityFields": [
"purchaseId"
],
"buyerForm": {
"returnUrl": {
"source": "scope",
"path": "orderUrl",
"allowedOrigins": [
"https://shop.example"
]
}
},
"inputResolvers": [
{
"id": "commerce.recipient-profile",
"version": "1.0.0",
"displayName": "Recipient profile",
"outputSchema": {
"type": "object",
"additionalProperties": true
},
"mode": "deferred",
"localization": {
"ru": {
"displayName": "Профиль получателя"
},
"en": {
"displayName": "Recipient profile"
}
},
"requiredContext": [
{
"source": "scope",
"path": "conversationId"
}
],
"timeoutMs": 30000,
"retry": {
"maxAttempts": 3,
"initialBackoffMs": 1000
},
"ui": {
"category": "Customer data",
"order": 20
}
}
],
"ui": {
"category": "Sales",
"icon": "cart"
}
}
],
"abstractions": [
{
"abstractionId": "messaging.send-in-context",
"abstractionVersion": "1.0.0",
"nodeType": "example.delivery/send-message",
"nodeVersion": "1.0.0"
}
],
"package": {
"branding": {
"icon": "assets/icon.png"
},
"artifact": {
"path": "edge/driver.py",
"filename": "driver.py"
},
"guides": {
"installation": {
"ru": "guides/install.ru.md",
"en": "guides/install.en.md"
},
"readme": {
"ru": "guides/README.ru.md",
"en": "guides/README.en.md"
},
"changelog": {
"ru": "guides/CHANGELOG.ru.md",
"en": "guides/CHANGELOG.en.md"
}
},
"compatibility": {
"environments": [
"Example Runtime 1.x"
]
},
"release": {
"critical": false
}
}
}