Documentation menu

Authenticate against the Light Cloud API - creating and managing API keys, what a key can and cannot do, and the session tokens the console and MCP server use.

Authentication

Every endpoint except health needs a credential. There are two, for two situations:

Use it forAvailable on
API keyScripts, CI, servers - anything running with nobody watchingPaid plans
Session tokenThe MCP server, the CLI, the console - anything with a person present (a browser, or a phone to approve a device code on)Every plan, including free

If a browser can open, you do not need a key. If one cannot, a key is the only way in.

Using a key#

Authorization: Bearer lc_...

No refreshing and no expiry to handle unless you set one. A key belongs to one organisation, so targetOrganisationId is optional on every call - it is inferred, and naming a different organisation is refused with CA003.

curl -X POST https://api.light-cloud.com/api/applications \
  -H "Authorization: Bearer $LIGHT_CLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"limit": 100}'

What a key cannot do#

By design, not by configuration:

  • Billing and organisation settings. No key, at any access level, can change a payment method, invite a member, or delete a workspace. Those need a signed-in person.
  • Manage keys. The endpoints below are session-only. A leaked key cannot mint a replacement or widen its own access - not even its own listing.
  • Reach another workspace. A key is bound to the organisation it was created in.

Warning

A key is a live credential for the whole workspace. Put it in your CI secret store, never in a repository, and never in a client-side bundle where a browser can read it.

Plans#

Keys require a paid plan; creating one on a free plan returns 402. The entitlement is re-checked on every request, so a downgrade stops existing keys working rather than letting them outlive it.

This is a limit on unattended credentials, not on automation - the MCP server and the CLI work on every account and can do everything a key can, and more (a key can never touch billing). They just need a person to approve the initial sign-in, from any browser.


Create an API key#

POST/api/api-keys/create

Issues a key and returns its secret. This is the only time the secret is ever readable - only a bcrypt hash is stored. Session tokens only.

permission
update:organisations

Parameters

targetOrganisationIdstringrequired
The organisation the key will belong to
namestringrequired
What will use it, e.g. "GitHub Actions"
rolestringdefault user
user for read-only, admin to deploy and manage. owner is not assignable
expiresAtstringoptional
RFC 3339 timestamp in the future. Omit for a key that never expires
curl -X POST https://api.light-cloud.com/api/api-keys/create \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "targetOrganisationId": "org_1Ab",
    "name": "GitHub Actions",
    "role": "admin",
    "expiresAt": "2027-08-28T00:00:00.000Z"
  }'
201ResponseThe key, including its secret
{
  "id": "key_3Wq8",
  "name": "GitHub Actions",
  "prefix": "lc_RdiJfPH9X",
  "role": "admin",
  "expires_at": "2027-08-28T00:00:00.000Z",
  "created_at": "2026-08-28T11:16:35.617Z",
  "secret": "lc_RdiJfPH9XmS2cA7vK1pQ0tZbN4eL8yG6uW3rD5oX9jH"
}

Important

secret appears in this response and nowhere else. If it is lost, revoke the key and create another.

List API keys#

POST/api/api-keys

Every key in the organisation, newest first. Never returns the secret or its hash - prefix is the display form, enough to tell keys apart and useless on its own. Session tokens only.

permission
read:organisations

Parameters

targetOrganisationIdstringrequired
The organisation to read
curl -X POST https://api.light-cloud.com/api/api-keys \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"targetOrganisationId": "org_1Ab"}'
200ResponseKeys, without secrets
[
  {
    "id": "key_3Wq8",
    "name": "GitHub Actions",
    "prefix": "lc_RdiJfPH9X",
    "role": "admin",
    "created_by": "usr_5Kd",
    "expires_at": null,
    "revoked_at": null,
    "last_used_at": "2026-08-28T09:41:02.000Z",
    "created_at": "2026-08-20T10:00:00.000Z"
  }
]

Revoke an API key#

POST/api/api-keys/revoke

Stops a key working immediately - the next request with it is rejected. The row is kept rather than deleted, so the record of what existed survives. Session tokens only.

permission
update:organisations

Parameters

targetOrganisationIdstringrequired
The organisation the key belongs to
keyIdstringrequired
The key to revoke
curl -X POST https://api.light-cloud.com/api/api-keys/revoke \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"targetOrganisationId": "org_1Ab", "keyId": "key_3Wq8"}'
200ResponseRevoked
{ "revoked": true }

Revoke a key when the person who created it leaves, when the job it served is retired, or the moment you suspect it has leaked. A key without an expiry does not stop working on its own.

Tip

Set an expiry on anything temporary. A key that outlives its purpose is the one that turns up in an old pipeline two years later.

You can also do all of this in the console: left sidebar → your workspace → API keys.


Session tokens#

The console, the MCP server and the CLI use short-lived tokens rather than keys: an access token valid for 15 minutes, plus a refresh token valid for 30 days. Sessions started from a terminal (the device-code flow) are listed under Settings → Security → Connected devices, where they can be revoked.

Obtaining one happens in the browser, through the console. The MCP server does this for you; a person confirms the connection, and the tokens are written to ~/.lightcloud/credentials.json. Implementing that flow yourself is not a supported integration path - use an API key.

Note

POST /api/auth/login exists for the console. It expects the password encrypted with a platform secret that is not distributed, so it is not an authentication path for API clients.

Refresh a session token#

POST/api/auth/refresh

Exchanges a refresh token for a new pair. The presented token is revoked in the same call, so the value changes every time - which is why a refresh token cannot live in a CI secret, and why API keys exist.

Parameters

refreshTokenstringrequired
The current refresh token. Browsers may omit it and send the httpOnly cookie instead
curl -X POST https://api.light-cloud.com/api/auth/refresh \
  -H "Content-Type: application/json" \
  -H "X-Client-Type: cli" \
  -d "{\"refreshToken\": \"$REFRESH_TOKEN\"}"
200ResponseA new pair
{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "9c1f4e2a7b..."
}

Important

X-Client-Type: cli is not optional outside a browser. Without it the response carries only a new access token, and your refresh token stays the old, now-revoked one.

Retrieve the current user#

GET/api/auth/profile

The signed-in user and every organisation they belong to, with their role in each. This is where targetOrganisationId comes from. Session tokens only - a key has no user behind it.

curl https://api.light-cloud.com/api/auth/profile \
  -H "Authorization: Bearer $SESSION_TOKEN"
200ResponseThe user and their organisations
{
  "id": "usr_5Kd",
  "email": "you@example.com",
  "first_name": "Ada",
  "organisations": [
    {
      "id": "org_1Ab",
      "name": "Acme Labs",
      "slug": "acme-labs",
      "role": "owner"
    }
  ]
}

Sign out#

POST/api/auth/logout

Revokes the refresh token and clears the cookie. Access tokens already issued stay valid until they expire, up to 15 minutes later.

curl -X POST https://api.light-cloud.com/api/auth/logout \
  -H "Authorization: Bearer $SESSION_TOKEN"
200ResponseSigned out
{ "message": "Successfully logged out" }

Two-factor accounts#

When an account has two-factor authentication enabled, signing in returns a short-lived pending token instead of a session, and a TOTP or recovery code exchanges it for the real one at POST /api/auth/login/mfa. The console and MCP server handle this.

API keys are unaffected - they are workspace credentials, not account credentials, and carry no second factor of their own. That is another reason they cannot touch billing or membership.