Skip to content

REST API

The Helena REST API lets service accounts list environments and tests, trigger runs and read the Test Log.

  • Base URL: https://app.helena.dev/api/v1
  • Format: JSON. Successful responses include "success": true and a data field.
  • Identifiers: every resource is addressed by its UUID. You can copy environment and test UUIDs from the API responses or from the URL of the page in the Helena UI.

Authentication

Exchange a credential for a bearer token at POST /service-account/auth. The body can be form-encoded or JSON.

curl -s -X POST https://app.helena.dev/api/v1/service-account/auth \
  -d grant_type=client_credentials \
  -d client_id="$HELENA_CLIENT_ID" \
  -d client_secret="$HELENA_CLIENT_SECRET"
curl -s -X POST https://app.helena.dev/api/v1/service-account/auth \
  -d grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer \
  -d client_id="$HELENA_CLIENT_ID" \
  -d assertion="$OIDC_ID_TOKEN"
Parameter Notes
grant_type client_credentials or urn:ietf:params:oauth:grant-type:jwt-bearer. If you leave it out, Helena uses jwt-bearer when assertion is present and client_credentials otherwise.
client_id The credential's helena_… client ID.
client_secret For client_credentials.
assertion For jwt-bearer: an OIDC ID token that matches the credential's trust policy. See OIDC workload identity trust.

A successful response:

{
  "success": true,
  "access_token": "9f2c…(64 characters)",
  "token_type": "Bearer",
  "expires_in": 3600,
  "expires_at": "2026-10-29T13:00:00Z",
  "service_account": {
    "uuid": "…", "name": "github-actions-ci",
    "scopes": ["write"], "all_environments": false, "tenant_uuid": "…"
  }
}

Send the token on every other request:

Authorization: Bearer 9f2c…

Tokens last 1 hour. Request a new one when it expires. There are no refresh tokens. A token stops working straight away if its credential is revoked, its account is deactivated, or the tenant is locked.

Endpoint Purpose
GET /service-account/auth/verify Returns the account and the current token's credential and expiry.
POST /service-account/auth/revoke Revokes the current token. Call it at the end of a pipeline job.

Rate limits: 60 authentication requests per minute per IP address, and 10 per minute per client ID and IP address.

Endpoints

Method and path Scope Description
GET /environments read Environments the account can access
GET /environments/{env}/tests read Tests in the environment. Optional filter: ?type=oidc_wif_client, client_credentials, scim_client or siem_rest_api_poll.
GET /environments/{env}/tests/{test} read One test with its configuration (without secrets)
POST /environments/{env}/tests/{test}/runs write Trigger a run
GET /environments/{env}/test-runs read The Test Log, newest first
GET /environments/{env}/test-runs/{run} read One run with steps and assertions

An environment the account cannot access returns 404, as does a test or run outside that environment.

Tests

A test resource:

{
  "uuid": "…",
  "environment_uuid": "…",
  "type": "client_credentials",
  "type_label": "Client Id/Secret",
  "name": "Auth0 – CI client",
  "description": null,
  "enabled": true,
  "schedule": { "cron": "0 * * * *", "enabled": true, "next_run_at": "2026-10-29T13:00:00Z" },
  "last_run": { "uuid": "…", "status": "passed", "finished_at": "2026-10-29T12:00:04Z" },
  "created_at": "2026-10-01T09:30:00Z",
  "config": { "token_endpoint": "https://…", "client_id": "…", "client_secret_set": true }
}

config is returned only by the single-test endpoint. Its fields are the keys listed in each test type's field reference. Secrets are never returned. Instead, flags such as client_secret_set, bearer_token_set or api_key_set say whether one is stored. OIDC WIF Client tests also return issuer, discovery_url and jwks_uri, along with the effective subject.

Triggering a run

curl -s -X POST -H "Authorization: Bearer $HELENA_TOKEN" -H 'Content-Type: application/json' \
  -d '{"wait": true}' \
  "https://app.helena.dev/api/v1/environments/$ENV_UUID/tests/$TEST_UUID/runs"
Body Response
{} or no body 202 Accepted. The run is queued. The Location header points at the run. Poll it until status is passed, failed or error.
{"wait": true} 200 OK once the run has finished, with the full run (steps and assertions). Usually the simplest choice for CI.

The run appears in the Test Log with trigger api and the service account as the actor.

409 TEST_NOT_RUNNABLE means the test is disabled or the tenant's subscription doesn't allow runs. Triggering is limited to 30 runs per minute per service account.

Test Logs

GET /environments/{env}/test-runs supports:

Query parameter Values
test Test UUID
status queued, running, passed, failed, error
trigger manual, api, schedule
per_page 1–100 (default 25)
page Page number (default 1)

The response has a meta object with current_page, per_page, total and last_page. List items omit steps and assertions.

A run:

{
  "uuid": "…",
  "test_uuid": "…", "test_name": "Auth0 – CI client", "test_type": "client_credentials",
  "environment_uuid": "…",
  "trigger": "api",
  "triggered_by": { "type": "service_account", "uuid": "…", "name": "github-actions-ci" },
  "status": "failed",
  "summary": "Target issued a token; expected success. HTTP 200. 1 assertion(s) failed.",
  "queued_at": "…", "started_at": "…", "finished_at": "…", "duration_ms": 412,
  "steps": [
    { "name": "Send token request", "status": "passed", "at": "…", "duration_ms": 388, "detail": { "status": 200 } }
  ],
  "assertions": [
    { "name": "Cache-Control: no-store (RFC 6749 §5.1)", "expected": "no-store", "actual": null, "passed": false, "message": null }
  ]
}

triggered_by.type is user, service_account or system. A step's status is passed, failed, skipped or info.

Errors

Errors return "success": false and an error object:

{ "success": false, "error": { "code": "INSUFFICIENT_SCOPE", "message": "…", "required_scope": "write" } }
HTTP code Meaning
400 INVALID_REQUEST A required parameter is missing or malformed.
400 UNSUPPORTED_GRANT_TYPE grant_type is not one of the two supported values.
401 INVALID_CLIENT Unknown client ID, wrong secret, wrong credential type, or a revoked or expired credential. These all return the same error on purpose.
401 INVALID_GRANT The OIDC assertion was rejected: a signature, issuer, audience, subject, claim, lifetime or replay check failed. Compare the token's claims with the credential's trust policy.
401 SERVICE_ACCOUNT_INACTIVE The account is inactive or past its expiry date.
401 NO_TOKEN No Authorization: Bearer header was sent.
401 INVALID_TOKEN The token is unknown, expired or revoked.
403 TENANT_INACTIVE The tenant's subscription is locked.
403 INSUFFICIENT_SCOPE The account's scope is too low. required_scope says what is needed.
404 — The environment, test or run doesn't exist or isn't accessible to this account.
409 TEST_NOT_RUNNABLE The test is disabled, or the tenant cannot run tests.
422 — Invalid filter value.
429 — Rate limit exceeded. Wait and retry.