Get started · Authentication

Authentication

Every request carries an API key in an Authorization header. There is one mechanism, it is the same on every endpoint, and there is nothing to negotiate — no OAuth dance, no request signing.

The header

Send your key as a bearer token. Secrets start with esy_sk_.

every requestbash
curl https://api.esy.com/v1/runs \
  -H "Authorization: Bearer $ESY_API_KEY"

Miss it, or send a revoked key, and you get a 401:

401 Unauthorizedjson
HTTP 401

{ "detail": "Missing authentication token" }

Creating a key

Keys are created in os.esy.com under Settings → API keys, or through POST /v1/api-keys from a signed-in session.

The secret is shown exactly once
Esy stores only a SHA-256 hash of your key. The creation response is the only time the plaintext secret exists anywhere you can read it. If you lose it, you cannot recover it — you revoke that key and make another.

What a key can do

This is the part people get wrong. An API key acts as the user who created it. It is a credential, not a reduced-privilege role: there is no scope system that lets you mint a read-only key today. If the creating account can delete a project, so can a key it created.

The one restriction available is workspace binding. A key bound to a workspace can only touch that workspace, and a request naming any other is rejected with 403. Bind every key you create — it is the difference between a leaked key being a problem and being a catastrophe.

PropertyBehaviour
IdentityThe key acts as its creating user, with that user’s permissions.
Workspace bindingOptional but recommended. Hard-restricts the key to one workspace.
ExpiryKeys do not expire on their own. Revoke them explicitly.
StorageOnly a SHA-256 hash is kept. The plaintext is never recoverable.
Rate limitsNot currently enforced per key. Do not rely on the API to throttle you.

Checking which key you have

GET /v1/users/me tells you who a key authenticates as and which workspaces it can reach. It is the fastest way to confirm a key works and to find the workspaceId you will pass when creating runs.

GET /v1/users/mebash
curl -s https://api.esy.com/v1/users/me \
  -H "Authorization: Bearer $ESY_API_KEY"
200 OKjson
{
  "id": "6c24a056-…",
  "email": "[email protected]",
  "name": "Your Name",
  "role": "admin",
  "isActive": true,
  "workspaceMemberships": [
    {
      "workspaceId": "9a1b6d4c-…",
      "workspaceSlug": "your-org",
      "workspaceName": "Your Org",
      "workspaceKind": "organization",
      "role": "owner"
    }
  ]
}

Sessions, for browsers

The dashboard authenticates with a cookie session rather than a key — POST /v1/auth/login sets an esy_session cookie and returns an access token. You almost certainly do not want this: it exists for first-party browser clients, and cross-origin cookie mutations are rejected with 403 on purpose. For server-to-server work, use a key.

Keeping a key safe

  • Keep it in an environment variable or a secret manager. Never in source control, never in a client bundle, never in a URL — query strings end up in logs and browser history.
  • Bind it to one workspace at creation, and create separate keys per deployment so you can revoke one without taking down everything.
  • Never put a key in frontend code. A key that reaches a browser is public. Proxy Esy calls through your own backend.
  • Revoke on suspicion, not on proof: DELETE /v1/api-keys/{key_id} takes effect immediately.
If a key leaks
Revoke it first, investigate second. Revocation is instant and free; a live leaked key is not. Then check runs for work you did not start — every run records createdVia, apiKeyId and apiKeyName, so you can tell exactly which credential did what.
In short
  • One mechanism: Authorization: Bearer esy_sk_… on every request.
  • A key acts as the user who created it. Workspace binding is the only way to narrow it.
  • The secret is displayed once and stored only as a hash. Revoke and recreate rather than recover.