Self-hosted · Configuration

Integrations

Connectors, REST hooks, notifications, automations and the programmatic API — what each one is for, which of them can reach inside your network, and which cannot. Sign in with the email on your subscription.

We send a 6-digit code. Access is limited to emails on an active self-hosted subscription — talk to sales if you do not have one yet.

We sent a code to . It expires in 10 minutes.

Self-hosted · Configuration

Integrations

Six ways TestVibe talks to the rest of your toolchain. This page is a map — what each one is for, what an operator has to do, and the network restrictions that bite hardest on a self-hosted install. Your users do the rest inside the application.

01

The map

These overlap less than their names suggest. Pick by the question you are trying to answer.

You want…UseConfigured by
TestVibe to be able to send email at all Mail You, in the admin console
a failing run to become a Jira ticket Connectors A workspace owner/admin, in Settings
to seed or reset your test environment before a run, and tear it down after Hooks Per project, in Settings
to be told when something finishes or breaks Notifications Each user, for themselves
tests to run on a schedule, or in reaction to another run Automations Per project, in the app
to drive TestVibe from CI or a script API / CLI / MCP Each user, via an API key
Only two of these are operator configuration. There are no console settings for connectors, hooks, notifications or automations — those are per-workspace and per-project features your users set up themselves. What you own is outbound network access and the mail relay.
02

What can reach your network

The most valuable thing this page can tell a self-hosted administrator: two of these deliberately cannot call an address inside your own network, and no setting changes that. Plan around it rather than debugging it.

FeatureCalls out fromCan it reach a private address?
Hooks The application No — loopback and private ranges are blocked outright, with no opt-out.
Notification webhooks The application No — restricted to a fixed list of public chat hosts.
Email The application Yes — your own SMTP relay, wherever it is.
Connectors The application Yes — a self-managed GitLab, Jira Data Center or Azure DevOps Server URL is accepted.
The site under test The test runner Yes — that is the whole point of self-hosting.
This is the one to read twice Hooks cannot call your internal API. The guard blocking loopback and private ranges is a server-side-request-forgery defence with no configuration switch. A hook pointed at http://10.1.2.3/reset or http://internal.corp/seed is rejected when it is saved. Expose the endpoint on a routable address, or use the correlation approach in section 05 to have the tests themselves do the work.
03

Mail

Optional, but you will want it. TestVibe runs fine with no mail at all, and nobody is blocked from signing in. But several useful things quietly do nothing until you set it up. This is the only page in the guide that explains how.

What works without it, and what does not

Works with no mailNeeds mail
Creating the first user — done in the console's First user panel, already verified. Password resets. Without mail a locked-out user has no way back in on their own.
Adding everyone elseSettings → Members → Invite produces a copyable link you can send however you like. Run, generation and load-test notifications. Email is every user's default channel, so with no relay nobody is told anything.
Signing in, generating, running tests — all of it. Emailed invitations (the copy-link path still works) and the output of an assistant automation, which is delivered by email.

Setting it up

Everything is in the admin console under Mail. There are two ways to send, and you pick one.

  1. Choose a transport.
    • Your own SMTP relay — preferred, and the only option that works without outbound internet.
    • Mandrill — one HTTPS call per message to mandrillapp.com, with no relay to run, but this install then needs outbound internet. Set TESTVIBE_MANDRILL_API_KEY (Mandrill API key). Your from address must be on a domain verified in your Mandrill account or every message is rejected.
    If both are set, the relay wins and the Mandrill key is ignored.
  2. Fill in the fields for the transport you chose (table below).
  3. Set a from address on a domain your relay will accept. Most relays reject a sender on a domain they do not own.
  4. Press Send test message. You should receive the test mail. Do not move on until you do — every other mail problem in this product traces back to this step.
Console fieldKeyNotes
SMTP relay hostTV_SMTP_HOSTThe relay TestVibe hands messages to. Leave every mail field blank to disable email entirely.
PortTV_SMTP_PORTDefault 587 (submission, STARTTLS) or 25 (internal relay). Implicit-TLS port 465 is not supported — use 587, which every mainstream relay also offers.
Use STARTTLSTV_SMTP_USE_TLSOn by default. Leave it on unless this is an internal relay on a trusted network that does not offer TLS.
UsernameTV_SMTP_USERNAMELeave blank if the relay authorises this host by IP instead of credentials.
Password or app tokenTV_SMTP_PASSWORDStored encrypted. Office 365 and Google Workspace need an app password or a dedicated service account here, not a user's own password.
From addressTESTVIBE_EMAIL_SENDER_ADDRESSRequired.
From nameTESTVIBE_EMAIL_SENDER_NAMEDefault TestVibe.
Set mail in the console, not in your deployment file Unlike the sign-on keys, the shipped Compose .env.example and the Helm values.yaml carry no TV_SMTP_* keys at all, and the compose web service does not forward them — so a value you add to .env is simply ignored, with no error. Use Console → Mail. To manage it as code anyway, add the variables to the web service's own environment (web.extraEnv on Kubernetes); the console then shows them locked.

One more optional field lives here: TESTVIBE_SIGNUP_NOTIFY_EMAIL (Notify on new sign-ups) tells an address of yours when someone registers. Blank means nobody is notified — this install never reports sign-ups outside your network.

04

Connectors

For: turning a failure into a ticket in the system your team already uses — by hand from the run, or automatically every night.

How it is put together

  • A connection is the credential, and it belongs to the workspace. Created once, in Settings → Connectors, by an owner or admin. It is encrypted at rest and never returned to a browser.
  • A binding says what this project uses that connection for, and which target — which Jira project, which repository. It holds a pointer, never a credential.
ProviderFiles issues?What the user supplies
Jira Yes Site URL, account email, API token. Works against a self-managed site — put its URL in the Site URL field.
GitLab Yes GitLab URL (defaults to gitlab.com — change it for self-managed) and a personal access token with the api scope.
Azure DevOps Yes Organization and a PAT with Work Items (Read & Write).
GitHub Yes, with setup A GitHub App installation — see the note below. No token is stored; a scoped installation token is minted per call.
Slack, Mattermost Not yet An incoming webhook URL. They are connectable and the credential is stored, but no feature consumes a chat connection yet. For chat alerts today, use Notifications.
GitHub needs operator setup that the shipped templates do not mention The GitHub connector authenticates as a GitHub App, and the app's identity is read from the web service's environment — GitHub__AppId (or GITHUB_APP_ID) and GitHub__PrivateKeyPem (or GITHUB_APP_PRIVATE_KEY_PEM). Neither appears in the Compose .env template, the Helm values or the appliance environment file, so you must register your own GitHub App and add them yourself. Until you do, the GitHub connector cannot mint a token. Jira, GitLab and Azure DevOps need none of this — a token in the connect form is enough, so start there.

What it does once connected

  • A File issue button appears on run, load-run and generation detail screens, one per connection that can file issues. Automations can file issues too, and an automated ticket is identical to a hand-filed one.
  • The issue body carries the permanent TestVibe deep link and names the artifacts. It deliberately does not embed artifact links — those expire, and a dead link in a two-year-old ticket helps nobody.
  • Repeat failures with the same signature comment on the existing ticket rather than opening a new one each night. A person clicking the button always gets a new ticket, because they meant it.

Connector credentials are separate from the jira test plugin's credentials, deliberately. The plugin runs inside the test sandbox; connectors run in the application. Same service, different privilege boundary — do not reuse one token for both.

05

Setup & cleanup hooks

For: "call my API to seed a tenant before the suite, and tear it down after." A REST call TestVibe makes on your behalf around a run, a load test or a generation.

How a hook works

  • Six events: before and after each of run, load test and generation. Suite-level only — there are no per-scenario hooks, because scenarios run in parallel and concurrent "reset the database" calls would stomp each other.
  • Five fields: name, event, URL, failure policy, enabled. That is the whole configuration surface, on purpose.
  • TestVibe sends a fixed JSON body and reads only the status code. No custom body, no custom headers, no response parsing — the same contract GitHub and Stripe webhooks use.
  • A before hook can refuse. With the failure policy set to fail, a non-2xx response stops the run before any capacity is taken. An after hook can never block.
  • Signed with a TestVibe-Signature header — t=<unix>,v1=<hex HMAC-SHA256> over "{t}.{body}", Stripe's construction, so most integrators already have verification code. The secret is shown exactly once when the hook is created.
  • Fixed 30-second timeout and two retries.

Correlating a hook with the tests it set up

Both sides see the same value. The hook payload carries a per-run unique token, and the tests get the same value as the TESTVIBE_RUN_UNIQUE environment variable. So your endpoint creates tenant-<unique> and the tests find it, with nothing parsed out of the hook's response.

That is also the answer when the private-address restriction bites: if your seeding endpoint cannot be exposed, have the tests do the seeding, keyed on the same value. Tests run in the runner, which can reach your network.

Hook operator notes

  • Hooks run in the application, not in the test sandbox. That is why they get secrets and retries — and why section 02 applies to them.
  • A run blocked by a failing before-hook ends as error with a banner naming the hook. It is not a test failure, and no capacity was consumed.
  • Three consecutive failures of a blocking hook force a notification even for scheduled runs, which are otherwise quiet — so a broken hook cannot kill a nightly schedule in silence.
  • The execution log stores failure responses but never a successful one — a setup endpoint's 200 is the response most likely to contain a freshly-minted credential.
  • Where: Settings → Project → Hooks. Use the Send test request button, because it is the only place a successful response body is ever shown.
06

Notifications

For: telling a person that a run, generation or load test finished — outside the app. Per user, on by default. Operators provide nothing here except the mail relay; each user picks their own channels in Settings → Notifications.

Channels

ChannelTargetOn a self-hosted install
Email The user's verified address, or another they add. Works anywhere — through your relay.
Slack An incoming webhook on hooks.slack.com. Needs internet
Google Chat A space webhook on chat.googleapis.com. Needs internet
Microsoft Teams A Workflows webhook on webhook.office.com or logic.azure.com. Needs internet

Per channel and per event type — run, generation, load — a user chooses all, failures only, or off. Cancellations never notify.

Chat webhooks are restricted to those hosts The host list above is a fixed allowlist, and it is what keeps a user-supplied webhook URL from becoming a request-forgery tool. A URL on any other host is rejected when the channel is saved. You cannot point a notification channel at a self-hosted Mattermost or Rocket.Chat — for an internal chat system, use email, or subscribe an address your chat system ingests.
Scripted runs are silent on purpose A dispatch that came from the API, the CLI, the MCP server, the assistant or an automation does not fan out to email or chat by default — the caller already has the outcome, and a 164-feature regeneration would otherwise produce 164 emails. The in-app bell still fires. To opt a specific dispatch back in, pass notify: true on the REST body or --notify on the CLI. Runs dispatched from the interface always notify normally.
07

Automations

For: "run the suite every night at 2am", "file a ticket whenever a run fails", "have the assistant investigate and tell me the root cause". A trigger → condition → action engine, configured per project in the app.

Triggers

  • On a schedule — chosen from presets, with a timezone. There is no raw cron field.
  • On an event — a run finishing, optionally filtered to failures.

Actions

  • Run features — the whole suite as one run, or a subset.
  • Run a load test.
  • Run an assistant job — one headless assistant turn against a prompt, emailed to you.
  • Create an issue in a connected tracker.

There is also a per-automation AI triage on failure toggle: when a run this automation triggered fails, the assistant investigates and the outcome carries a root cause instead of just "failed" — shown on the run, in the failure notification, and in the automation's history.

Automation operator notes

  • Scheduling is safe across restarts and replicas. A due automation is claimed atomically, so a restart mid-tick cannot double-fire and a second instance cannot either.
  • An automation-produced run cannot re-trigger an automation. There is a loop guard; you will not wake up to a runaway chain.
  • Time zone matters. A blank or unrecognised timezone is treated as UTC. On a "why did it run at the wrong time" question, check that field first.
  • Automations consume capacity like any other dispatch. Several nightly schedules landing on the same minute will queue against your concurrency caps — see Capacity.
  • Assistant automations run inline on the scheduler tick with a 5-minute ceiling, so many of them due at once will serialise. Their output arrives by email.
  • Automation-dispatched runs are silent by default — see section 06. Enabling AI triage makes its failures deliver anyway, because you opted in.
08

API, CLI and MCP

For: CI pipelines, scripts, and AI coding tools. One core behind three surfaces — an HTTP API, a command-line tool, and an MCP server that plugs TestVibe into an AI assistant.

What a user needs

  1. An API key. Created in the application under Settings → CLI & API keys. Keys begin tvb_ and are shown once. They are sent as Authorization: Bearer tvb_….
  2. Your install's address. Everything is under {your base URL}/api/v1/ops. There is no TestVibe-hosted endpoint involved.
  3. The CLI, if they want one. You should see your projects listed back.
    shell
    npm install -g testvibe
    export TESTVIBE_SERVER=https://testvibe.example.com
    export TESTVIBE_API_KEY=tvb_…
    testvibe projects

    The same package runs as an MCP stdio server, so an AI assistant can drive the same operations. TESTVIBE_PROJECT pins a default project.

API operator notes

  • Plan gating does not apply. On the cloud, programmatic access is a paid capability; on a self-hosted install that check is skipped entirely. Every workspace has full API, CLI and MCP access.
  • You can still put a ceiling on it. TESTVIBE_OPS_RATE_LIMIT_PER_HOUR caps requests per workspace per hour when set — unset means no per-workspace limit on self-hosted. A separate per-IP limiter, TESTVIBE_OPS_RATE_LIMIT_IP_PER_HOUR, defaults to 10,000 per hour. Both are environment-only; neither is in the console. Clients get the standard RateLimit-* response headers whenever limiting is on.
  • The API is a full control plane — it can create features, dispatch runs and generations, and edit project files. Treat a tvb_ key as equivalent to the user's access, and make sure your reverse proxy is not caching authenticated responses.

Where to go next