Skip to main content
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
Nothing runs in your editor: no AI client, no local devtools server, no completion gate. It’s the same kind of verification your agent performs locally, packaged as a command you can run from any shell or CI pipeline. Each job appears in the console as a verification run on the project’s Verifications tab.
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 reaches 127.0.0.1:<port> on your machine without you exposing anything.
An API run may reach only the hosts it’s told about: the target, the environment’s URL and OpenAPI properties, and the hosts its secrets are bound to.

Credentials

The command authenticates with your IronBee account. Run ironbee 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:
The value is masked from all CLI output the moment it’s parsed, and registered with ::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, --url must fall inside those domains, and a --port target is refused because a tunnel has no domain.
When the API refuses a job over the environment, the error line ends with how to recover: 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, or GITHUB_SHA in 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.
In GitHub Actions the binding is automatic: 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-wait runs out (default 60s), the command exits 1 with the last probe error.
Once the job is created, the CLI prints every status change (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 prints created 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, fixes and reason lists
  • a warning when the run refused or narrowed part of the request
  • a final banner: PASS, FAIL, NOT APPLICABLE, or no verdict with 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:
  • status is one of queued, starting, running, succeeded, failed or cancelled.
  • result.status is pass, fail or not_applicable.
  • The body also carries the resolved environment, the project, the target type in input.type, the trigger, and the created, started and ended timestamps.
  • The result may carry refused: true with a reasonCode when the run declined or narrowed part of the request. A refused run can still be succeeded, so a caller should check it.
  • The job may carry an error: { type, message }.
  • With --no-wait --json, the output also includes the create-time notices.
A failure that produced no job prints a machine-readable error envelope instead. Tell the two apart by the presence of id:
error.code is the API’s own error code, and the field to branch on. Codes worth handling include: 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.