Authentication

Every request to the Helm Core API carries a bearer token in the Authorization header. There are three ways to get one; answer two questions below and we'll point you at the right one.

Which method should I use?

Who — or what — will be making the calls?
Authorization: Bearer hlm_8Kq2mZ…

Whichever way you get it, a bearer token is checked the same way on every call — REST and MCP alike:

  1. Is the token real? Missing, unknown, revoked or expired tokens get a 401 (invalid_token, revoked, expired).
  2. Is the API switched on for your organisation, and is the scope's area enabled? Otherwise 403 org_disabled or 403 scope_disabled.
  3. Is the caller's IP allowed? Organisation and token allowlists are both checked; otherwise 403 ip_blocked.
  4. Is the token within its rate limit? Otherwise 429 rate_limited with Retry-After.
  5. Does the token carry the operation's scope — and does the user behind it still hold the matching platform permission? Otherwise 403 missing_scope.

Every response carries an X-Request-Id, and every call — accepted or rejected — is written to your organisation's API audit log.

Static tokens

The simplest option: a token created in Settings → API Suite and sent as-is.

  • It looks like hlm_ followed by 40 random characters.
  • The full value is shown once, when you create it. Helm stores only a hash, so a lost token can't be recovered — revoke it and make a new one.
  • You choose its scopes, and optionally an expiry, an IP allowlist (CIDR ranges) and a rate limit.
  • Revoking it takes effect on the next request.

There are two kinds, and the difference matters:

Organisation tokenPersonal token
Acts asIts own service identity — a non-login user with a role holding exactly the token's scopesYou
PermissionsFixed by the scopes you pickedYour live platform permissions, narrowed by the token's scopes
Survives you leavingYesNo — suspend the user and the token stops working
Who can create oneUsers who manage the API SuiteUsers who manage it, and anyone with self-service API access
Good forIntegrations, sync jobs, pipelinesScripts, notebooks, an assistant acting as you
A token can never outrun its user

Every scope maps to platform permissions. On each call Helm checks that the token's user — your service identity or you — still holds one of them, so a scope on the token is necessary but never sufficient. Writes are recorded against that user too, which is why v2 rejects user_id-style "act as" fields in request bodies with a 422.

curl https://your-org-domain/kong/core/api/v2/ping \
-H "Authorization: Bearer $HELM_API_KEY"

OAuth2 client credentials

If your platform prefers short-lived tokens — or already speaks OAuth2, like most iPaaS tools and API gateways — create the organisation token with the OAuth2 authentication method instead. Rather than a bearer, you get a client:

  • a client id, prefixed hlmc_, and
  • a client secret, prefixed hlms_, shown once.

Exchange them for an access token at the public token endpoint. Ask for a subset of the client's scopes with scope, or leave it out to get them all:

curl -X POST https://your-org-domain/kong/core/api/v2/oauth/token \
-d grant_type=client_credentials \
-d client_id=hlmc_… \
-d client_secret=hlms_… \
-d scope="investors:read deals:read"

The access token (prefixed hlma_) lasts an hour and is used exactly like a static token. Cache it and fetch a new one shortly before expires_in runs out.

Failures use the OAuth2 error shape, { "error": …, "error_description": … }:

StatuserrorMeaning
400unsupported_grant_typeOnly client_credentials is accepted here.
400invalid_scopeYou asked for a scope the client doesn't have.
401invalid_clientUnknown, revoked or wrong secret — deliberately vague.
403invalid_clientThe organisation's API access is switched off.
429slow_downOver 20 token requests a minute from your IP; wait Retry-After seconds.

The 20-a-minute limit per IP address slows down secret guessing; cache access tokens and you'll never reach it. See Rate limits.

Interactive OAuth for MCP clients

AI assistants connect to the MCP server as you, and modern MCP clients can do the whole sign-in themselves. Point the client at your MCP URL and it:

  1. Calls the server without a token and gets a 401 with a WWW-Authenticate header pointing at the protected resource metadata.
  2. Reads /.well-known/oauth-protected-resource, then the authorization server metadata at /.well-known/oauth-authorization-server.
  3. Registers itself at POST /oauth/register (dynamic client registration). Public clients are de-duplicated, so reconnecting doesn't pile up registrations.
  4. Opens a browser at /oauth/authorize — authorization code flow with PKCE (S256) — where you sign in and see a consent screen.
  5. Exchanges the code for an access token and a refresh token, and starts calling tools.

All of these live under https://your-org-domain/kong/core/api/v2.

Clients usually ask for every scope going. The consent screen sorts each one into:

  • Granted — you hold the permissions behind it.
  • Switched off — that area is disabled for your organisation.
  • Not yours to grant — your roles don't carry it.

Only the granted scopes are issued (OAuth allows a narrower grant than asked for), so the assistant's tool list is exactly the set of calls that will succeed. If nothing can be granted, approval fails with 403 no_grantable_scope.

You need self-service API access

Approving a connection requires a platform permission that lets you use the API Suite yourself. Without it the consent screen explains you're not enrolled; ask an administrator to grant it.

Keeping tokens safe

  • One token per integration. Revoking one shouldn't take down another, and the audit log stays readable.
  • Least privilege. Grant only the scopes the job needs — and for MCP, the token's scopes decide which tools the assistant can even see.
  • Set an expiry and an IP allowlist on static tokens used from servers.
  • Never ship a token to a browser or mobile app. Call Helm from your backend.
  • Store secrets in a secret manager, not in code or .env files in git.
  • Rotate by creating the new token, deploying it, then revoking the old one.