---
title: "API reference"
description: "Server SDK functions and the Reference List endpoint, with options, results, HTTP statuses and errors."
source: "/docs/api"
updated: "2026-09-24"
---

# API reference

Server SDK functions and the Reference List endpoint, with options, results, HTTP statuses and errors.

## `createAuthUrl(options)`

`createAuthUrl(options)` -> `Promise<{ url, nonce, sig }>`

Generates an HMAC-signed redirect URL and a cryptographic nonce. **Async** - it signs via the Web Crypto API, so it returns a Promise and must be awaited. It performs no network call.

| Option         | Type                        | Required | Description                                                                                                                                                                      |
| -------------- | --------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `spaceId`      | string                      | yes      | Your Space ID (UUID) from the Helix admin panel.                                                                                                                                 |
| `state`        | string                      | yes      | Opaque base64 payload echoed back in the callback.                                                                                                                               |
| `origin`       | string                      | yes      | The browser origin your app is served from, e.g. `https://app.example.com`. Helix targets its completion message at exactly this origin, so it must match. Sent as `sdk_origin`. |
| `secret`       | string                      | yes      | Shared secret from the Helix admin panel. Used to HMAC-SHA256-sign the URL params - server-side only.                                                                            |
| `baseUrl`      | string                      | no       | Override Helix portal URL. Defaults to `https://capsule.helix.id` when omitted.                                                                                                  |
| `env`          | `"sandbox" \| "production"` | no       | Selects which space config Helix looks up. Defaults to `production` - leave it unset against `capsule.helix.id`.                                                                 |
| `enrollmentId` | string                      | no       | Enrollment ID from a previous `verifyCallback` result. Pass it to verify this user against their existing enrollment; omit on their first session to enroll them.                |

**Returns** `Promise<{ url: string, nonce: string, sig: string }>` - store `nonce` in the user session and hand `url` to the browser.

### URL signing

The signature covers the canonical query string: every param except `sig`, sorted by raw key, then each key and value percent-encoded exactly like JavaScript's `encodeURIComponent` (`state=a&b` -> `state=a%26b`), joined as `key=value` pairs with `&`. Other languages: `!'()*` stay unescaped, which Python's `quote` and PHP's `rawurlencode` escape by default. That string is HMAC-SHA256'd with `secret` and appended as `sig` (64 lowercase hex chars):

```text
/voice-auth?space_id=...&nonce=...&state=...&env=...&sdk_origin=...&sig=<hmac-sha256-hex>
```

Because every other param is covered, changing any of them after signing invalidates `sig`. Build the URL once, on the server, and pass it through untouched.

## `verifyAuthUrlSignature(url, secret)`

`verifyAuthUrlSignature(url, secret)` -> `Promise<void>`

Verifies the `sig` on an auth URL produced by `createAuthUrl`. **Async.** Rebuilds the canonical string from the URL's remaining params and compares in constant time via `crypto.subtle.verify`. Rejects a URL that repeats a query param. Server-side only - it needs the secret.

```js
import { verifyAuthUrlSignature, InvalidAuthUrlSignatureError } from "@helix-id/node";

try {
  await verifyAuthUrlSignature(url, process.env.HELIX_SECRET);
} catch (error) {
  if (error instanceof InvalidAuthUrlSignatureError) {
    // URL is unsigned or was tampered with - do not redirect the user
  }
}
```

**Returns** `Promise<void>` - resolves when the signature is valid.

**Throws** `InvalidAuthUrlSignatureError` when `sig` is missing or does not match, or a query param repeats.

Most integrations never call this: your own server produced the URL, so there is nothing to check. It is there for architectures where the URL crosses a trust boundary between services - a gateway, a BFF, or a queue - and the receiving side wants proof it was not rewritten in transit.

## `verifyCallback(query, options)`

`verifyCallback(query, options)` -> `Promise<VerifyCallbackResult>`

Verifies the HMAC-signed callback payload and returns the parsed result. **Async** - it uses the Web Crypto API, so it returns a Promise and must be awaited. The first argument is the raw callback query, every field a plain string.

| Option   | Type   | Required | Description                                               |
| -------- | ------ | -------- | --------------------------------------------------------- |
| `nonce`  | string | yes      | Nonce returned by `createAuthUrl`, stored in the session. |
| `secret` | string | yes      | Shared secret from the Helix admin panel.                 |

**Returns** `Promise<{ status, statusCode, logId, state, enrollmentId?, referenceList? }>` - `enrollmentId` is present when the space has Voice MFA or Reference List enabled, absent otherwise. `referenceList` is `{ result, matchedEnrollmentId?, matchedCategory? }` on a new enrollment in a Reference List space - see [Reference List results](/docs/status-codes.md#reference-list-results).

**Throws** `InvalidSignatureError`, `NonceMismatchError`, `ExpiredTimestampError`, `MalformedCallbackError`.

## Reference List management

People enter your Reference List only when your server adds them. After verifying a voice-auth callback, store its `enrollmentId` against your user. Use that ID when your own review or customer policy calls for adding or removing the person. Lists are separate for each Space and environment.

Adding requires Reference List to be enabled and an active **server-side voice template** belonging to your client. An Enrollment ID alone is insufficient: a person with only a device-held template cannot be added by this endpoint and receives `no_template_available`. Removing an entry is allowed even when Reference List is disabled.

Both helpers run on your backend, hold the space/environment's shared secret, and perform a network request. They use `https://api.helix.id` by default (`HELIX_DEFAULT_API_URL`); `createAuthUrl` uses the portal at `https://capsule.helix.id`. Override the helpers' `baseUrl` for a local or alternate API deployment.

```js
import { addToReferenceList, ReferenceListError } from "@helix-id/node";

const options = {
  spaceId: process.env.HELIX_SPACE_ID,
  secret: process.env.HELIX_SECRET,
  enrollmentId: user.helixEnrollmentId, // stored from a verified callback
};

try {
  const entry = await addToReferenceList({
    ...options,
    category: "denied", // your label; Helix does not block this person
    reason: "manual_review",
    customerRef: "CASE-7",
    actor: "support-team",
  });
} catch (error) {
  if (error instanceof ReferenceListError) {
    console.error(error.status, error.code); // upstream HTTP status and error code
  } else {
    throw error; // transport or successful-response JSON parsing failure
  }
}
```

Remove the person in a **separate server action**, after your review upholds an appeal:

```js
import { removeFromReferenceList, ReferenceListError } from "@helix-id/node";

try {
  const removal = await removeFromReferenceList({
    spaceId: process.env.HELIX_SPACE_ID,
    secret: process.env.HELIX_SECRET,
    enrollmentId: user.helixEnrollmentId,
    reason: "APPEAL_UPHELD",
  });
} catch (error) {
  if (error instanceof ReferenceListError) console.error(error.status, error.code);
  else throw error;
}
```

### `addToReferenceList(options)`

`addToReferenceList(options)` -> `Promise<AddToReferenceListResult>`

| addToReferenceList option | Type                        | Required | Description                                                                        |
| ------------------------- | --------------------------- | -------- | ---------------------------------------------------------------------------------- |
| `spaceId`                 | string                      | yes      | Space ID (UUID) whose list you manage.                                             |
| `secret`                  | string                      | yes      | Shared secret for that Space and environment. Server-side only; never transmitted. |
| `enrollmentId`            | string                      | yes      | Stored ID from a verified callback: 64 lowercase hex characters.                   |
| `env`                     | `"sandbox" \| "production"` | no       | Defaults to `production`; use the corresponding environment's secret.              |
| `baseUrl`                 | string                      | no       | API origin, default `https://api.helix.id`.                                        |
| `actor`                   | string                      | no       | Audit actor, 1-128 characters; defaults to `api`.                                  |
| `category`                | string                      | no       | Your label matching `[A-Za-z0-9_-]{1,64}`. See the conventions below.              |
| `reason`                  | string                      | no       | Audit reason, 1-64 characters; free text for additions.                            |
| `customerRef`             | string                      | no       | Your case/reference, 1-256 characters; sent as `customer_ref`.                     |

| addToReferenceList result | Type             | Required | Description                                     |
| ------------------------- | ---------------- | -------- | ----------------------------------------------- |
| `enrollmentId`            | string           | yes      | ID of the listed person.                        |
| `category`                | `string \| null` | yes      | Stored label, or `null` if none was supplied.   |
| `addedAt`                 | string           | yes      | Original addition timestamp in ISO 8601 format. |
| `listSize`                | number           | yes      | Entries in this Space/environment's list.       |
| `alreadyListed`           | true             | no       | Present only for an existing entry (HTTP 200).  |

A new entry returns HTTP 201. If the person is already listed, the endpoint returns HTTP 200 and their original timestamp/category; it does **not** update metadata. The enabled-feature and active-template prerequisites still apply to repeated additions.

### `removeFromReferenceList(options)`

`removeFromReferenceList(options)` -> `Promise<RemoveFromReferenceListResult>`

| removeFromReferenceList option | Type                        | Required | Description                                       |
| ------------------------------ | --------------------------- | -------- | ------------------------------------------------- |
| `spaceId`                      | string                      | yes      | Space ID (UUID) whose list you manage.            |
| `secret`                       | string                      | yes      | Shared secret for the selected Space/environment. |
| `enrollmentId`                 | string                      | yes      | Listed person's ID: 64 lowercase hex characters.  |
| `env`                          | `"sandbox" \| "production"` | no       | Defaults to `production`.                         |
| `baseUrl`                      | string                      | no       | API origin, default `https://api.helix.id`.       |
| `actor`                        | string                      | no       | Audit actor, 1-128 characters; defaults to `api`. |
| `reason`                       | ReferenceListRemoveReason   | no       | One of the removal reasons below.                 |

| removeFromReferenceList result | Type   | Required | Description                           |
| ------------------------------ | ------ | -------- | ------------------------------------- |
| `enrollmentId`                 | string | yes      | ID of the removed person.             |
| `removedAt`                    | string | yes      | Removal timestamp in ISO 8601 format. |

Removal returns HTTP 200. Removing an absent entry returns HTTP 404 `not_listed`, which the helper throws as `ReferenceListError`.

| Removal reason   | Description                                    |
| ---------------- | ---------------------------------------------- |
| `APPEAL_UPHELD`  | Your review upheld the person's appeal.        |
| `EXPIRED`        | Your retention or listing period ended.        |
| `CUSTOMER_ERROR` | Your organization added the person by mistake. |
| `OTHER`          | Another reason for removal.                    |

These reasons record your decision; they do not schedule automatic removal.

### Category conventions

`category` is optional, client-defined text within the character/length limits above. These recommended labels are conventions, **not an enum or built-in policies**. Custom labels are supported, and the stored label is returned as `referenceList.matchedCategory` when a new enrollment matches. Your application decides how to act; Helix never blocks or grants privileges based on category.

| Recommended category | Description                                                                           |
| -------------------- | ------------------------------------------------------------------------------------- |
| `denied`             | People your policy marks for denial or review; your application enforces that policy. |
| `vip`                | People your application wants to recognize or give special handling.                  |

### `POST /api/v1/reference-list`

The raw endpoint is `https://api.helix.id/api/v1/reference-list`. Send **query parameters, no JSON body**. The SDK supplies `action`, `ts`, and `sig`; raw clients must supply them too. Unknown query parameters are rejected.

| Reference List parameter | Type                        | Required | Actions | Description                                                                                   |
| ------------------------ | --------------------------- | -------- | ------- | --------------------------------------------------------------------------------------------- |
| `space_id`               | string                      | yes      | both    | Space UUID.                                                                                   |
| `env`                    | `"sandbox" \| "production"` | yes      | both    | Required on the wire; the SDK defaults it to `production`.                                    |
| `ts`                     | string                      | yes      | both    | Current Unix timestamp in seconds, decimal digits. Must be within 300 seconds of server time. |
| `sig`                    | string                      | yes      | both    | HMAC-SHA256 signature in 64 lowercase hex characters.                                         |
| `action`                 | `"add" \| "remove"`         | yes      | both    | Operation to perform.                                                                         |
| `enrollment_id`          | string                      | yes      | both    | Enrollment ID, exactly 64 lowercase hex characters.                                           |
| `actor`                  | string                      | no       | both    | Audit actor, 1-128 characters; defaults to `api`.                                             |
| `category`               | string                      | no       | add     | Optional label matching `[A-Za-z0-9_-]{1,64}`.                                                |
| `reason`                 | string                      | no       | both    | Add: 1-64 characters of free text. Remove: one of the fixed reasons above.                    |
| `customer_ref`           | string                      | no       | add     | Customer case/reference, 1-256 characters.                                                    |

Sign every transmitted parameter except `sig`, including all optional metadata, with the selected Space/environment's shared secret. Use the same [canonical query encoding](#url-signing) as `createAuthUrl`: sort raw keys; percent-encode each key/value with `encodeURIComponent`; join `key=value` pairs with `&`; compute HMAC-SHA256 and append the lowercase hex digest as `sig`. Do not sign the already-serialized query string (`URLSearchParams` encodes spaces differently). Generate a fresh timestamp/signature for each call, keep clocks synchronized, and use HTTPS.

### Endpoint HTTP statuses

These are actual HTTP statuses, separate from the voice-auth callback's `statusCode`. Successful outcomes below describe the operation; the JSON response contains the result fields documented above, without an `outcome` field.

| Reference List action | HTTP | Outcome                   | Description                                                                              |
| --------------------- | ---- | ------------------------- | ---------------------------------------------------------------------------------------- |
| add                   | 201  | `added`                   | New entry created.                                                                       |
| add                   | 200  | `already_listed`          | Existing entry returned with `alreadyListed: true`; metadata unchanged.                  |
| remove                | 200  | `removed`                 | Entry deleted and removal journaled.                                                     |
| both                  | 400  | `VALIDATION_ERROR`        | Missing/malformed or unsupported query parameters. Body: `{ error: { code, message } }`. |
| both                  | 401  | `unauthorized`            | Invalid signature, stale/future timestamp, or unavailable Space/environment/secret.      |
| remove                | 404  | `not_listed`              | No entry exists in this Space/environment.                                               |
| add                   | 409  | `reference_list_disabled` | Reference List is disabled for this Space/environment.                                   |
| add                   | 409  | `no_template_available`   | No active server-side voice template belongs to this client's Enrollment ID.             |
| add                   | 409  | `reference_list_full`     | The Space/environment's configured list capacity has been reached.                       |

Business/authentication errors use `{ error: "code" }`; `no_template_available` also includes a message. Both helpers throw `ReferenceListError` for any non-2xx response, exposing the upstream `.status` and backend `.code`. If the failure body has no usable code (including a non-JSON response), `.code` is `REFERENCE_LIST_REQUEST_FAILED`. Transport failures and JSON parsing failures on successful responses propagate unchanged. The helpers do not retry; decide how your server handles each failure rather than forwarding upstream errors to the user automatically.


## Sitemap

See the full [sitemap](https://helix.id/sitemap.md) for all pages.
