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": trueand adatafield. - 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.
| 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:
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. |