Skip to main content
To hand the work over to your coding agent, set it up with the difyctl skill and point it to this page.

Install difyctl in the Job

Use the installer, and always set DIFY_VERSION to your server’s version, so a new Dify release doesn’t change the build the job gets. If the installer stops at GitHub’s rate limit, set GITHUB_TOKEN or GH_TOKEN in the job’s environment.

Sign In

For a job that runs unattended, sign in yourself and pass it the token. When someone can approve a code each time, a script can sign in itself.

Pass a Token to the Job

Get the token with an account used only for CI, or on a machine you don’t otherwise run difyctl on. The account needs the Editor role or above to import, test, and publish. If the same account signs in again on that machine, the job’s sign-in ends. Use --no-keyring, so you can copy the token from a file:
Copy it from tokens.yml in the config folder:
Store it as a CI secret, and set these environment variables in the job:
  • DIFY_SERVER: the address you signed in with.
  • DIFY_TOKEN: the token, from the secret.
  • DIFY_WORKSPACE_ID: the workspace’s id, which difyctl get workspace lists.
If the job can’t write to its default config folder, set DIFY_CONFIG_DIR to a folder it can. Then delete tokens.yml, so the token lives only in the CI secret. This signs out the machine you signed in from, while the job keeps working. Running logout instead would end the job’s sign-in too.

Renew the Sign-In

When the job’s commands fail with auth_expired (exit code 4), sign in again and replace the token in the CI secret. The sign-in stops working when:
  • It expires: after 14 days by default, or the number of days OAUTH_TTL_DAYS sets on your server.
  • The same account signs in again on that machine, even in another config folder.
To renew before the job fails, run difyctl get account session in the job and check expires_at on the session whose device_label is the machine you signed in from.

Sign In from a Script

Start the sign-in with --no-wait. Like any login, it first signs out the current session, then prints the code and exits without waiting:
Pass verification_uri and user_code to someone who can open the URL and approve the code within 15 minutes. Until you finish the sign-in, don’t start another login: it discards this one, so approving its code no longer signs you in. Then finish the sign-in in the same config folder:
  • Until the code is approved, it prints {"status":"pending"} and exits with 0, so check status rather than the exit code.
  • Once the code is approved, it prints the signed-in account instead, with no status.
  • If the code is denied or expires first, it fails with exit code 4. Start over with login --no-wait.
For what to check before you approve a coding agent’s code, see Sign In.

Handle Failures

Run each step in bash with set -eo pipefail. These commands exit 0 even when the job should stop. Check their output with jq -e, which fails when its test is false:
  • check console_app dsl, when the file is invalid: jq -e '.valid'
  • import console_app dsl, when the import is left pending for someone to confirm: jq -e '.status | startswith("completed")'
  • check console_app dependency, when the workspace lacks integrations the app uses: jq -e '.leaked_dependencies | length == 0'
  • test console_app workflow, when the run paused or was cut off: jq -e '.status == "ended"'
  • check console_app release, when the draft isn’t ready to publish: jq -e '.ready'
A command that exits 7 hit the rate limit and did nothing. Have the job wait a minute and run it again.

Example: Update an App on Merge

This GitHub Actions workflow checks the app’s DSL file, apps/order-status.yml, against the app when a pull request changes it. On merge to main, it updates and publishes the app. For what each step does, see Update an App.
Each import replaces the app’s draft, including changes someone made in Studio, so change the app only through the repository. The import also applies the file’s name, description, icon, and web app settings right away. If a later step fails, the live workflow stays as it was.
To use it, in the repository’s Settings > Secrets and variables > Actions, add the secret DIFY_TOKEN and the variable DIFY_WORKSPACE_ID. Then replace the example values with your own: the server address, the app’s ID and name (difyctl get console_app lists both), the file path, the Dify version, and the test inputs.
.github/workflows/update-order-status.yml
Last modified on October 23, 2026