Test-Lab.aiDocs
API Reference

Import Test Plans API

Programmatically create and list test plans, labels, and credentials. Built for one-shot import of existing test suites, drivable by the testlab CLI or an AI agent.

Import Test Plans API

These endpoints let you create test plans (plus the labels, credentials, and pipeline pre-steps they depend on) without the dashboard. They are the foundation of the testlab CLI and are designed for a one-shot import: point an AI agent at your existing tests, have it convert each into a plan, and push them into your account.

POST https://test-lab.ai/api/v1/test-plans
GET  https://test-lab.ai/api/v1/test-plans
POST https://test-lab.ai/api/v1/labels
GET  https://test-lab.ai/api/v1/labels
POST https://test-lab.ai/api/v1/credentials
GET  https://test-lab.ai/api/v1/credentials

Authentication

Every request uses a full-scope API key:

Authorization: Bearer tl_xxxxx

Create a key under Settings → API Keys (/admin/settings/api-keys). The key resolves to exactly one account, so the key you use IS the import target. To import into an organization account, mint the key while you have that org selected.

The key's account is the only account these endpoints touch. Labels, pre-steps, and credentials are always resolved within that account, never another.

Create a test plan

POST /api/v1/test-plans

Imported plans are account-scoped (no project), so the target URL lives in the prompt text. That is exactly what the test-lab plan format already produces.

FieldTypeRequiredDescription
namestringYesPlan name (max 200 chars)
promptstringYesThe natural-language test, with explicit URL(s) and {{credentials.X}} placeholders (max 32 KB)
testTypestringNo"quickTest" or "deepTest"
agentTypestringNofunctional (default), accessibility, uiux, exploratory, performance, or security
devicesstring[]Noe.g. ["Desktop Chrome"] (default) or ["iPhone 15 Pro"]
labels(string | number)[]NoLabel names (auto-created) and/or existing label ids. Max 25.
preStepsobject[]NoPipeline pre-steps. Each is { testPlanId } or { name } (+ optional inputValues). Max 25.
failOnPreStepFailurebooleanNoDefault true
cookiesarrayNo[{ name, value, domain }] injected at run time
headersarrayNo[{ name, value }] injected at run time

projectId and proxyCountry (geolocation) are not configurable via import yet. Imported plans have no project; set those in the dashboard if needed.

curl -X POST https://test-lab.ai/api/v1/test-plans \
  -H "Authorization: Bearer tl_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Login smoke test",
    "prompt": "Go to https://app.example.com/login and sign in with {{credentials.email}} / {{credentials.password}}. Confirm the dashboard loads.",
    "testType": "quickTest",
    "agentType": "functional",
    "labels": ["smoke", "auth"]
  }'

Response (201):

{
  "testPlan": {
    "id": 123,
    "name": "Login smoke test",
    "prompt": "Go to https://app.example.com/login …",
    "default_agent_type": "functional",
    "labels": [{ "id": 5, "name": "smoke", "color": "#22c55e" }],
    "preSteps": []
  }
}

Pipeline pre-steps

To make one plan depend on another, reference the dependency by name (resolved to the most recent plan with that name in your account) or by id:

curl -X POST https://test-lab.ai/api/v1/test-plans \
  -H "Authorization: Bearer tl_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Checkout",
    "prompt": "From the cart at https://app.example.com/cart, complete checkout.",
    "preSteps": [{ "name": "Login smoke test" }]
  }'

Create the dependency first so the name resolves. An unresolved pre-step returns 404 (the create API fails loudly rather than dropping a dependency silently). The testlab import command handles ordering for you.

List test plans

GET /api/v1/test-plans?page=1&limit=25

Returns a lean, paginated view, enough to de-duplicate before importing and to look up ids/names for pre-step wiring:

{
  "testPlans": [
    {
      "id": 123,
      "name": "Login smoke test",
      "default_agent_type": "functional",
      "labels": [{ "id": 5, "name": "smoke" }],
      "preSteps": []
    }
  ],
  "total": 1,
  "page": 1,
  "limit": 25,
  "totalPages": 1
}

Labels

POST /api/v1/labels      { "name": "smoke" }
GET  /api/v1/labels

POST is idempotent: a name that already exists returns the existing label (no error), so re-running an import is safe. You rarely need to call this directly, since POST /api/v1/test-plans auto-creates labels passed by name.

Credentials

POST /api/v1/credentials   { "key": "email", "value": "qa@example.com" }
GET  /api/v1/credentials

POST is an upsert (create, or replace the value of an existing key). Values are stored AES-256-GCM encrypted and are never returned: GET lists keys with masked values only.

Credential keys must start with a letter and contain only letters, numbers, and underscores (max 50 chars). Values are capped at 1000 chars. If server-side encryption is not configured, the write is rejected with 503 rather than storing a secret in plaintext.

curl -X POST https://test-lab.ai/api/v1/credentials \
  -H "Authorization: Bearer tl_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "key": "password", "value": "hunter2" }'

A plan prompt then references it as {{credentials.password}}.

Data fixtures (test data)

Reusable, generated test data. A fixture is a named bundle of typed fields; reference a field in a prompt as {{data.<fixtureKey>.<fieldKey>}}.

POST /api/v1/data-fixtures   { "key": "...", "label"?: "...", "fields": [...] }
GET  /api/v1/data-fixtures

Each field is { key, value?, generator?, mode? }:

  • static (default): a literal value (may template {{run.X}} for per-run uniqueness).
  • dynamic ("mode": "dynamic"): a generator rolls a fresh value on every run. Dynamic fields require a generator.
curl -X POST https://test-lab.ai/api/v1/data-fixtures \
  -H "Authorization: Bearer tl_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "newUser",
    "label": "A fresh signup",
    "fields": [
      { "key": "firstName", "mode": "dynamic", "generator": "person.firstName" },
      { "key": "email",     "mode": "dynamic", "generator": "internet.email" },
      { "key": "plan",      "mode": "static",  "value": "pro" }
    ]
  }'

A plan prompt then references it as {{data.newUser.email}}.

Generators (for dynamic fields): person.firstName, person.lastName, person.fullName, person.jobTitle, internet.email, internet.username, internet.url, internet.ipv4, phone.number, location.streetAddress, location.city, location.state, location.zipCode, location.country, company.name, lorem.word, lorem.sentence, lorem.paragraph, date.past, date.future, string.uuid, string.alphanumeric, string.numeric, number.int, boolean.

Fixture and field keys: start with a letter; letters/digits/underscores; max 50 chars. Max 50 fields per fixture; static values capped at 4000 chars; max 200 fixtures per account.

Error responses

StatusMeaning
400Missing/invalid field, or an input cap exceeded
401Missing or invalid API key
403Account blocked (ACCOUNT_BLOCKED) or signed-in blocked (SIGNIN_BLOCKED)
404A referenced pre-step plan was not found in your account
409A label/fixture with that name/key already exists
503Credential storage unavailable (encryption not configured)

Next steps

On this page

Import Test Plans API | Test-Lab.ai