ironbee verify starts a verification job through the IronBee API. A cloud agent verifies your application and returns a verdict:
- the target: a deployed URL, or a local app reached through a reverse tunnel
- how it verifies: in a browser for a web app, by calling it for an API
Earlier CLI versions used
ironbee verify [session-id] for a local dry-run of a session’s verdict checks. That command is now ironbee verdict. ironbee verify is exclusively the cloud verification job runner.Targets
Both take the same flags. The target is exactly one of:
--url <url>: a publicly reachable deployment. The cloud agent reaches it directly.--port <number>: a local application. The CLI holds a reverse tunnel open for the duration of the run, so the cloud agent reaches127.0.0.1:<port>on your machine without you exposing anything.
Credentials
The command authenticates with your IronBee account. Runironbee login once, or supply a credential from the environment, which is the way to do it in CI:
When both an OAuth token and an API key are available, the OAuth token wins. With no credential at all, the command fails with
no IronBee credential. The credential is never a CLI argument, so it can’t leak through the process table.
Flags
Target flags
--header and --secret-header apply to --url only: a tunnel target’s traffic passes no layer that could apply them.
Common flags
--prompt and --prompt-file are alternatives. --no-wait can’t be combined with --port: the run reaches the application only while the command is holding the tunnel open.
Secret headers for protected deployments
A preview deployment behind deployment protection, such as Vercel’s, needs a header to reach it. Pass it with--secret-header so the value is treated as a secret end to end:
::add-mask:: on GitHub Actions runners. It’s sent in a dedicated secretHeaders field that the service encrypts and never reads back. Plain --header values get none of that treatment, so use them only for non-sensitive headers.
To store a bypass for every run instead of passing it each time, add it as a project secret.
Environments
--environment <name> names the deployment the run is against, such as staging or preview. The run uses that environment’s variables and secrets on top of the project-wide ones. Without it, the run uses the project-wide ones only.
- A name the project hasn’t seen yet is created by the job.
- When the environment has Domain properties,
--urlmust fall inside those domains, and a--porttarget is refused because a tunnel has no domain.
See Environments.
Binding the run to your repository
By default the run is bound to your repository and current commit, so the verdict lands next to the right code in the console. Fine-tune or opt out:
The binding attaches only when it would actually work:
- The remote must be GitHub.
- A commit derived from local HEAD must be reachable from a remote-tracking branch. An unpushed HEAD produces a warning, and the run proceeds unbound rather than pointing at code the service can’t read. An explicit
--commit, orGITHUB_SHAin CI, is taken at face value. - A dirty working tree only warns. The run stays bound to the committed SHA, so what the agent reads and what’s on your disk may differ.
GITHUB_REPOSITORY, GITHUB_SHA and GITHUB_REF fill in the repo, commit and pull request. The job is declared with a github_actions trigger, so the console shows it as GitHub Actions. If the job fails with a repository checkout error, grant the IronBee GitHub App access to the repository, or re-run with --no-repo.
Which project. Without --project, the project name comes from your repository. That’s the origin remote’s repository name, else the first remote’s, else the repository folder’s name. A checkout of a github.com repository whose name matches reports to that repository’s GitHub project. --project <name> names the project explicitly and doesn’t claim a GitHub project. See How IronBee identifies your project.
How a tunnel run starts
For a--port target, the CLI first probes the local port, before creating the job, so a doomed run is never started:
- It keeps waiting while nothing is listening, and while the port accepts and immediately hangs up (Docker publishes a container’s port before the app inside is ready).
- A server that accepts but stays silent is treated as ready (just slow), not as a failure.
- A TLS listener fails immediately: the tunnel carries plain HTTP, so serve the app over HTTP locally.
- When
--app-waitruns out (default 60s), the command exits 1 with the last probe error.
queued, starting, running, then a terminal status) while holding the tunnel. When the run finishes, the service closes the tunnel with a dedicated “run ended” signal and the command stops without reconnecting. If that signal is ever lost, the command falls back to the job’s own status polling; the verdict is the same either way. Ctrl-C requests a cancel and waits for the service to confirm; a second Ctrl-C exits immediately.
Output and exit codes
When the job is created, the CLI printscreated job <id>. Any notices from the service follow as warnings, for example when the project the job resolved to has none of the variables or secrets a same-named project holds.
A finished run prints:
- the job’s summary
- its
checks,issues,fixesandreasonlists - a warning when the run refused or narrowed part of the request
- a final banner:
PASS,FAIL,NOT APPLICABLE, orno verdictwith the job’s status
That makes it CI-gate-ready:
ironbee verify run web --url … && deploy.sh.
--json
With --json, stdout carries exactly one JSON document, and all decoration goes to stderr. A run that produced a job prints the job body:
statusis one ofqueued,starting,running,succeeded,failedorcancelled.result.statusispass,failornot_applicable.- The body also carries the resolved
environment, the project, the target type ininput.type, the trigger, and the created, started and ended timestamps. - The result may carry
refused: truewith areasonCodewhen the run declined or narrowed part of the request. A refused run can still besucceeded, so a caller should check it. - The job may carry an
error: { type, message }. - With
--no-wait --json, the output also includes the create-timenotices.
id:
error.code is the API’s own error code, and the field to branch on. Codes worth handling include:
UNAUTHORIZEDandACCOUNT_INACTIVEPROJECT_ARCHIVED- the environment codes above
status and details appear only when the failure came from the API. A purely local failure, such as flag validation or a missing credential, carries only message.
On a GitHub Actions runner the CLI also writes
::add-mask:: workflow-command lines to stdout, masking the credential and any --secret-header values with the runner. A --json consumer there should parse the last JSON document rather than assume the stream is pure JSON.ironbee verify status and cancel
status accepts the same --json, --api-url and -C, --project-dir flags as run, plus --queue-wait <seconds> with --watch. As an observer it never cancels: if the queue wait elapses, it just warns that the job is still queued.
What’s next?
Verification
The in-editor verification gate: cycles, modes, and the verifier sub-agent.
Authentication
OAuth tokens for interactive use, API keys for CI.