Quick start
- Sign up and log in to the Web app.
- Create a new workflow — the canvas already has a "Trigger" node, which is the starting point.
- Drag out an "Action" node, choose a connector (say, Email), and fill in its settings.
- Connect the trigger node to the action node and save.
- Click Run, enter some test data, and check each node's result in the execution log.
Workflow editing guide
A workflow is made of three kinds of nodes:
- Trigger: the starting point — every workflow has exactly one. Whatever data comes in at run time becomes its output.
- Condition: makes one decision, with "Yes" and "No" outputs — only the matching branch runs.
- Action: calls a connector — for example sending an email, calling an API, or writing to a database.
Saving checks: exactly one trigger node, every connection has both ends attached, no cycles, condition outputs are labeled Yes/No, and every action's required fields are filled in. Any problems are listed one by one.
At run time, execution follows the connections starting from the trigger node; if any node fails, the whole run stops immediately and is marked failed.
Conditions
A condition has a left value, an operator and a right value; either side can be a fixed value or a reference (see the next section).
| Operator | Meaning | Example |
|---|---|---|
| eq / ne | equal / not equal | $.input.status eq "paid" |
| gt / gte | greater than / greater than or equal | $.input.amount gt 1000 |
| lt / lte | less than / less than or equal | $.input.stock lt 10 |
| in | left value is in the right-hand list | $.input.city in ["北京", "上海"] |
| contains | left value contains the right value | $.input.tags contains "VIP" |
| exists | field exists and isn't empty | $.input.coupon exists |
If the referenced field doesn't exist, exists evaluates to "No", and every other operator also evaluates to "No".
Passing values between nodes
In a condition or action's settings, any value starting with $. gets replaced with runtime data:
$.input.amount 运行时传入数据的 amount 字段
$.query.data.0.name 节点 query 输出中 data 列表第 1 项的 name
$.notify.status 节点 notify 输出的 statusWhen a field's entire value is exactly one reference, the replacement keeps its original type (numbers, lists and objects all work). To insert data in the middle of some text, use {{$.path}}:
大额订单 {{$.input.order_id}}:金额 {{$.input.amount}},请风控复核- Strings are inserted as-is; numbers, lists and objects are converted to JSON; empty values are inserted as empty.
- If the path doesn't exist, it's replaced with nothing (a whole-value reference becomes empty) — it won't fail the node, but it's flagged in the corresponding step of the execution log.
- Only values starting with $. count as references — "$100 off", for example, is just plain text.
- Email subject and body additionally support {{variable}} templates, with variables taken from an upstream node's output.
Connector guide
Where the address and credentials for each connector come from, where the official docs are, and what to do about common errors. The "Where do I get this?" panel above each connector form in the app is a short version of this guide.
HTTP
Setup steps
- Have the endpoint URL (url) and the request method (GET, POST, …) ready.
- If the API needs authentication, pick basic or bearer in auth_type and enter the username and password or the token. Don't put secrets directly into headers — they would be stored in plain text.
- Required
- url
- Optional
- method (default GET), headers, body, query_params, auth_type (none / basic / bearer), timeout, retries
When body is empty, the upstream node's output is sent instead. Can only reach public addresses.
Common errors
- "not a public address": the target is an internal or local address (including a redirect that lands on one) and is blocked for security.
- Only http and https URLs are supported.
- When body is empty, the upstream node's output is sent.
- The timeout defaults to 30 seconds and can be at most 60.
DingTalk
Setup steps
- Open the DingTalk group → Group settings → Group Assistant → Add robot → Custom (via Webhook).
- For security, choose Sign or Custom keywords (you can enable both). With Sign, copy the secret that starts with SEC into secret. With keywords, enter a word that will always appear in your messages, and add Opengency as a keyword too, because "Test connection" sends the text "Opengency 连接测试".
- Copy the generated webhook URL into webhook_url.
- Required
- webhook_url
- Optional
- secret (signing secret), msg_type (text / markdown / link / card), message, at_mobiles, at_all
Get webhook_url from your DingTalk group's settings → Group Assistant → Add Bot. "Test connection" actually sends a test message to the group. The test message text is fixed as "Opengency 连接测试". If the robot uses custom keywords, one of them must be "Opengency", otherwise the test is rejected for a keyword mismatch (not an issue if only Sign is enabled).
Common errors
- Signature mismatch (sign not match): the robot uses Sign but secret is empty or wrong. Enter the signing secret that starts with SEC, not the access_token from the webhook URL.
- Keyword mismatch: the message doesn't contain any of the robot's keywords. Put a keyword in the message, or add a keyword to the robot that your messages will contain.
- Invalid timestamp: the clock of the server running Opengency differs from DingTalk's by more than an hour. Correct the server time.
- "Test connection" is not a dry run — it really posts "Opengency 连接测试" to the group.
WeCom
Setup steps
- Open the WeCom group → "…" in the top right → Group robots → Add robot.
- The webhook URL is shown as soon as the robot is created.
- Copy that URL into webhook_url.
- Required
- webhook_url
- Optional
- msg_type (text / markdown / card), message, at_mobiles, at_all, url and title (for card)
Get webhook_url from your WeCom group → Group Bot; the key in the URL is the credential. @ mentions only work with text. "Test connection" actually sends a test message to the group.
Common errors
- The key in the webhook URL is the credential: anyone who has the URL can post to the group. If it leaks, remove the robot from the group and add a new one.
- @ mentions only work with the text type. The card type also needs url (where the card links to); saving fails without it.
- WeCom rate-limits robots that post too often, and messages stop going through. Don't schedule runs too close together.
- "Test connection" really posts a test message to the group.
Feishu
Setup steps
- Open the Feishu group → Settings → Group bots → Add bot → Custom bot.
- For security you can turn on Signature verification and put the secret shown on that page into secret. Custom keywords also work; add Opengency as a keyword there too.
- Copy the generated webhook URL into webhook_url.
- Required
- webhook_url
- Optional
- secret (if the bot has signature verification enabled), msg_type (text / post / markdown / card), message, title, at_open_ids, at_all
Get webhook_url from your Feishu group settings → Group Bots → Custom Bot. "Test connection" actually sends a test message to the group.
Common errors
- Signature verification is on but secret is empty or wrong: Feishu rejects the message (signature check failed). The secret can be viewed again in the bot's settings.
- Custom keywords are set but the message doesn't contain one: the message is rejected.
- at_open_ids takes open_ids that start with ou_, not phone numbers or names.
- "Test connection" really posts a test message to the group.
Slack
Setup steps
- Open https://api.slack.com/apps, create an app (or pick an existing one), and turn on Incoming Webhooks under Features.
- Click Add New Webhook to Workspace, choose the channel to post to, and authorize.
- Copy the generated webhook URL (it starts with https://hooks.slack.com/services/) into webhook_url.
- Required
- webhook_url
- Optional
- message, username, icon_emoji, channel
webhook_url is itself the credential (anyone who has it can post to that channel); Opengency stores it encrypted and masks it in the UI and logs. A webhook from a current Slack app always posts to the channel chosen when it was created, so username, icon_emoji and channel are ignored; to post somewhere else, create another webhook. "Test connection" actually posts a test message to the channel.
Common errors
- no_service: the webhook was deleted, disabled, or the address is wrong. Check the app's Incoming Webhooks page, and re-create it and update the connection if needed.
- channel_not_found: the channel doesn't exist, or this webhook has no access to it. Check the channel name, or re-create the webhook for the right channel.
- channel_is_archived: the channel is archived. Unarchive it, or use a webhook for another channel.
- action_prohibited: a workspace admin restricts what this webhook can do. Contact your Slack admin.
- invalid_payload or no_text: the message is empty or malformed. Make sure message isn't empty.
- HTTP 429: you are posting too often and Slack rate-limits you. Try again later, and don't schedule runs too close together.
- "Test connection" is not a dry run — it really posts "Opengency connection test" to the channel.
Teams
Setup steps
- In Teams, open the channel to post to → the "…" next to the channel name → Workflows.
- Choose the "Send webhook alerts to a channel" template, pick the team and channel as prompted, and save.
- After it is created, copy the webhook URL from the flow's details page into webhook_url.
- Required
- webhook_url
- Optional
- message, title
webhook_url is itself the credential (the sig in the address is the flow's signature); Opengency stores it encrypted and masks it in the UI and logs. Only Workflows (Power Automate) webhooks are supported; the old Office 365 connector webhooks (outlook.office.com/webhook/…) are being retired by Microsoft and are not supported. Messages are sent as an Adaptive Card: title is the bold heading at the top of the card and message is the body (basic Markdown works). A message can't exceed 28 KB, so an overly long body is truncated. "Test connection" actually posts a test message to the channel.
Common errors
- HTTP 401 / 403 (invalid or revoked address): the signature in the webhook URL is wrong or was regenerated. Open the flow in Teams, copy the latest address and update the connection.
- HTTP 404 (flow not found): the flow may have been deleted. Re-create it from the template and update the address in the connection.
- A message saying the flow is disabled / turned off: turn it back on in the Teams Workflows app or Power Automate. A flow belongs to its creator, and it can be turned off if they leave or their account is disabled, so ask an admin to hand it to someone else.
- HTTP 400 (the trigger didn't accept the message): make sure the flow was created from the "Send webhook alerts to a channel" template, that its trigger is When a Teams webhook request is received, and that you haven't changed the message format it accepts.
- HTTP 429: Teams rate-limits webhooks (roughly four requests per second), and posting too often gets rejected. Try again later, and don't schedule runs too close together.
- "Test connection" is not a dry run — it really posts "Opengency connection test" to the channel.
Setup steps
- Turn on SMTP in your mail provider's settings.
- Generate an app password (some providers call it an authorization code). Most providers, including Gmail, Outlook and Yahoo, don't let third-party apps send mail with your login password.
- Note the SMTP server and port, for example smtp.gmail.com:587 or smtp.qq.com:465.
- Put your address in sender_email and the app password from step 2 in sender_password.
- Required
- smtp_server, sender_email, sender_password, to
- Optional
- smtp_port (default 587), smtp_ssl (auto / implicit / starttls / none), use_tls, cc, bcc, subject, body, format (text / html / markdown)
subject and body support {{variables}}, taken from an upstream node's output. Most providers require an app-specific password rather than your login password.
Common errors
- Authentication failed: sender_password is your login password. Use an app password or authorization code instead.
- Connection timeout or handshake failure: the port and the encryption don't match. Port 465 uses implicit TLS and port 587 uses STARTTLS; with smtp_ssl left on auto the right one is picked from the port, so you rarely need to change it.
- "not a public address": the SMTP server resolves to an internal or local address (common with in-house mail servers) and is blocked for security. Use an SMTP server that is reachable from the public internet.
- to needs at least one recipient; separate several with commas.
SQLite
Setup steps
- No account or password is needed: the database file lives in your own data directory.
- Put just a file name in database_path, for example orders.db; the file is created if it doesn't exist.
- Required
- database_path, sql
- Optional
- params, operation (select / insert / update / delete / execute), return_type (default dict), timeout
database_path only needs a filename (e.g. orders.db) — the file is stored automatically in your own data directory. Use ? placeholders in SQL, with values in params.
Common errors
- "no such table": a mistyped file name doesn't raise a file-not-found error. A new, empty database is created instead, so the table isn't there. Check the file name first.
- database_path is a file name only. The database stays inside your own data directory and can't reach files anywhere else.
- Use ? placeholders, with the values in params.
MySQL
Setup steps
- Have the host, port (default 3306), user, password and database name ready.
- Create a dedicated database account for this connection; if it only runs queries, grant just SELECT.
- Make sure the database is reachable from the public internet: the firewall or your cloud provider's security group must allow the port.
- Required
- host, user, password, database, sql
- Optional
- port (default 3306), ssl (disabled / required / verify), params, read_only (on by default), max_rows (default 1000), timeout
Use %s placeholders in SQL, with values in params (a literal % must be written %% when params is used). Read-only by default — turn off read_only to write; for read-only nodes, use an account with SELECT-only privileges. Only one statement runs at a time, and only public addresses can be reached.
Common errors
- "not a public address": host is an internal address (10.x, 172.16–31.x, 192.168.x, 127.0.0.1). The connector can only reach public addresses, so the database needs a public endpoint.
- Writes are rejected: read_only is on by default and the SQL runs in a read-only transaction. Turn read_only off when you really need to write, and check that the account has write privileges.
- Results are incomplete: at most 1000 rows are returned by default (max_rows, up to 10000); anything beyond that is cut off and marked truncated.
- Only one SQL statement runs at a time. Use %s placeholders with the values in params; don't build values into the SQL string.
- Connection timeout: usually the firewall or security group isn't letting the port through.
Webhook
Setup steps
- Have a public URL that accepts POST requests and put it in webhook_url.
- If the receiver should be able to confirm that a request came from your workflow, agree on a secret and put it in secret_key.
- On the receiving side: every request carries X-Webhook-Timestamp; with secret_key set it also carries X-Webhook-Signature, the hex HMAC-SHA256 of (timestamp + "." + raw_body) computed with that secret.
- Required
- webhook_url
- Optional
- secret_key, verify_signature, event_types, retry_count, timeout
When secret_key is set, requests carry an X-Webhook-Signature header (HMAC-SHA256) that the receiver can use to verify them.
Common errors
- "Test connection" really sends an event with event_type ping to the receiver.
- A 4xx or 500 response, or a timeout, is not retried (the receiver may already have processed it). Only 429, 502, 503, 504 and connections that were never established are retried.
- "not a public address": the receiving URL is an internal or local address.
- An empty event_types forwards everything; once it has entries, data whose event_type isn't listed is skipped.
Security restriction: HTTP, DingTalk, WeCom, Feishu, Slack, Teams, Email, MySQL and Webhook can only reach public addresses (internal addresses like 127.0.0.1, 10.x and 192.168.x are rejected); SQLite can only access your own data directory.
API keys
For scripts and other systems to call the API — for example, a scheduled script that triggers a workflow. Create one under Account → API Keys in the app; the full key is shown only once, so save it right away — afterward the list only shows the first few characters, for identification. Limits: 1 for Free, 5 for Pro, unlimited for Business and Enterprise (counting only keys that haven't been revoked).
curl -X POST https://app.opengency.ai/api/executions/workflows/<工作流 ID>/execute \
-H "Authorization: Bearer ofk_…" \
-H "Content-Type: application/json" \
-d '{"input_data": {"order_id": 7}}'- Has the same permissions as the account that created it; executions it triggers are labeled "API" in the execution log, showing the key's first few characters.
- Managing API keys, changing your password, and deleting your account all require logging in with your account password — an API key can't do any of these.
- Revoking a key takes effect immediately (returns 401). Changing your password does not invalidate existing keys — if you suspect a leak, revoke them individually.
- Keep keys on the server side only — never put one in front-end web code or share it in a chat.
Frequently asked questions
- Why does my HTTP node say "not a public address"?
- The target is an internal or local address and was blocked for safety. Use a publicly reachable address instead.
- Can I get notified when a workflow execution fails?
- Yes, on every plan. Under Account → Failure notifications, choose to send to your account email, or to a saved DingTalk, WeCom, Feishu or email connection. Multiple failures within about 10 minutes are merged into one message, with each workflow's failure count, the node that errored, and a link to the details; manually cancelled runs don't trigger a notification.
- Can workflows loop?
- Not yet — workflows can't form a cycle, and saving will flag it if you try.
- Can workflows run on a schedule?
- Yes. In the editor, change the trigger to "Scheduled", pick a frequency (every N minutes, hourly, daily, weekly) or write your own cron expression, and choose a timezone; save the workflow, then turn on the "Enabled" switch on the workflow's detail page. Free lets you have 1 active scheduled workflow at a time; Pro allows 10.