Guides

Setup and teardown hooks

Create test data before a run and clean it up after, by letting Outqube call your own HTTPS endpoints and hand variables to the tests.

Outqube calls your server before the first test of a run starts (setup) and after the last one ends (teardown), so you can create test data and clean it up. Organisation admins set the URLs per app in Settings, Hooks, or with a manage key (GET and PUT /hooks, POST /hooks/rotate-secret). A suite can have its own hooks, which replace the app's for that suite. Both URLs are optional.

The calls

Both are a POST with a JSON body, once per run (a suite, a list of tests or a tag), from Outqube's fixed IP addresses, which are listed in settings. Setup waits for your answer before any test starts and before any AI model is used.

Setup call
{
  "run_id": "3e0d6f52-0b5e-4c0e-9d57-1f6a8c2b7a10",
  "phase": "setup",
  "app_id": "9a1c4d7e-2f3b-4a68-8c15-5b0e7d9f1a23",
  "tests": [{ "id": "0b6f2a14-5c1d-4e8a-b3f7-9d2c6e1a4b58", "name": "Sign in" }],
  "base_url": "https://staging.example.com",
  "metadata": {
    "source": "github",
    "commit_sha": "4f1c2a9",
    "branch": "feature/login",
    "pr_number": 42,
    "repo": "acme/web",
    "suite_id": null,
    "environment_id": "5d8e1f3a-7b2c-4a96-8e04-c1b7a9d3f620",
    "url": "https://app.outqube.com/acme/runs?execution=3e0d6f52-0b5e-4c0e-9d57-1f6a8c2b7a10"
  }
}

Teardown has "phase": "teardown" and an outcome: passed, failed, error or cancelled. The run_id is the same id the API and event webhooks use. tests lists the run's tests as they are when the call is made, up to 100. Teardown lists them again at the end. The test button in settings calls your setup URL with "phase": "test", no tests and a null base_url.

Verify the signature

Calls are signed like event webhooks, with Standard Webhooks and the app's whsec_ secret from settings. Rotating the secret gives 24 hours of double signatures. webhook-id is the call's id and stays the same on a retry. Verify the raw body and reject a timestamp more than five minutes off.

Answer setup with variables

Answer any 2xx within the timeout: 30 seconds by default, and from 1 to 120 seconds. The body can hand values to the tests.

{
  "variables": {
    "user_email": { "value": "qa+1042@example.com" },
    "api_token": { "value": "<generated token>", "secret": true }
  }
}
  • Names are lower case letters, digits and underscores, up to 40 characters. You can send up to 50 variables of up to 500 characters each. The whole response may be up to 64 KB.
  • Tests use them as {{user_email}}. A variable wins over a test variable with the same name.
  • Secret variables are redacted from everything Outqube stores, such as steps, logs, reports, bugs and the hook's own response excerpt, and read [REDACTED:hook_api_token]. Outqube keeps the values encrypted until an hour after the run ends.
  • A secret shorter than 6 characters, or a common word, cannot be redacted. Make secrets long and random.
  • Any other body (empty, ok, another shape) hands over nothing. A malformed variables member, or a body over 64 KB, fails the setup.

When something fails

  • If setup answers non-2xx, does not answer in time, cannot be reached or returns variables that cannot be read, every run of that start ends as error with the reason setup_failed. No browser opens and no AI model is called. The run page shows the status, the latency and the first 1 KB of the response. Setup is not retried: fix the receiver and run again.
  • Setup is called once, however many tests the run has. The run's other tests wait for it while holding their worker slots, so up to the timeout of capacity sits idle. Stopping the run ends the wait. A run cancelled while setup is in flight gets its teardown after the setup call has finished.
  • Teardown is called whatever the outcome, including cancelled, as long as setup was called or the app has no setup URL. A failure is tried once more after 30 seconds, then given up. Teardown never changes the outcome.
  • URLs must be https:// on port 443 with a public host. This is checked when you save and at every call. Redirects are not followed.

Evaluation runs and pull request check lanes call no hooks, and neither do AI builder sessions.

Example receiver

hooks.ts
import express from "express";
import { Webhook } from "standardwebhooks";

const wh = new Webhook(process.env.OUTQUBE_HOOK_SECRET!); // whsec_...
const app = express();

app.post("/outqube/hooks", express.raw({ type: "*/*" }), async (req, res) => {
  let call;
  try {
    call = wh.verify(req.body.toString("utf8"), req.headers as Record<string, string>);
  } catch {
    return res.sendStatus(400);
  }
  if (call.phase === "setup") {
    const user = await createTestUser(call.metadata.branch);
    return res.json({
      variables: {
        user_email: { value: user.email },
        api_token: { value: user.token, secret: true },
      },
    });
  }
  if (call.phase === "teardown") await deleteTestUsers(call.run_id);
  res.sendStatus(204);
});

createTestUser and deleteTestUsers are yours. Keying the data by run_id lets teardown remove exactly what setup made.

Next steps

On this page