Skip to main content
Mesa uses API keys for authentication. Each key is scoped to an organization, prefixed with mesa_, and granted specific permission scopes to control what it can access.

Your First API Key

Your first API key is created in the Mesa dashboard. If you haven’t done this yet, follow the Quickstart to create this initial key with admin scope. See Permission Scopes for details on what each scope allows.
API keys are only displayed once when created. Store them securely in your secrets manager or environment variables.

Creating API Keys

You can create API keys through the SDK or API. This is useful if you want to generate scoped keys to put in a sandbox or within CI:
import { Mesa } from "@mesadev/rest";

const mesa = new Mesa({ apiKey: process.env.MESA_API_KEY });

const key = await mesa.admin.createApiKey({
  org: "my-org",
  body: {
    name: "CI/CD Pipeline",
    scopes: ["read", "write"],
    expires_in_seconds: 2592000, // 30 days (optional)
    tags: {
      env: "production",
      team: "platform",
    },
  },
});

// Store this securely - the full key is only shown once!
console.log(key.key); // mesa_abc123...
You can also create keys from the Mesa dashboard.

Tags

Tags are optional key-value labels you can attach to API keys to organize them by purpose, environment, team, or any other dimension. Tag keys are lowercased and limited to 64 characters, values are limited to 256 characters. You can update tags on an existing key. Set a value to null to remove a tag:
await mesa.admin.updateApiKeyTags({
  org: "my-org",
  id: key.id,
  body: {
    tags: {
      env: "staging",   // set or overwrite
      team: null,        // remove
    },
  },
});
Tags are returned on every key when listing.

Listing Keys

const { apiKeys } = await mesa.admin.listApiKeys({ org: "my-org" });

for (const key of apiKeys) {
  console.log(`${key.name}: ${key.id} (${key.scopes.join(", ")})`);
}
The full API key value is never returned when listing keys. Only the key ID, name, and metadata are returned.

Revoking Keys

await mesa.admin.revokeApiKey({
  org: "my-org",
  id: "mesa_abc123",
});
Revoked keys are immediately invalidated and cannot be used for authentication.

Using API Keys

SDK Authentication

import { Mesa } from "@mesadev/rest";

const mesa = new Mesa({
  apiKey: "<API_KEY>",
});
Or using environment variables:
export MESA_API_KEY=<API_KEY>
const mesa = new Mesa({
  apiKey: process.env.MESA_API_KEY,
});

HTTP API Authentication

Include the API key in the Authorization header:
curl -H "Authorization: Bearer <API_KEY>" \
  https://api.mesa.dev/my-org/repo

Git Authentication

Use the API key as the password when cloning or pushing:
git clone https://t:<API_KEY>@api.mesa.dev/my-org/my-repo.git

Permission Scopes

Scopes are hierarchical. Each level includes all permissions from the levels below it.
ScopeDescription
readClone, fetch, and view repositories, branches, commits, and content
writeEverything read can do, plus push, create/update/delete repos, branches, and bookmarks
adminEverything write can do, plus API key management and webhook management

Scope Requirements by Operation

OperationRequired Scope
List/get repos, branches, commits, content, diffs, changesread
Clone (git fetch)read
Create/update/delete reposwrite
Push (git push)write
Create/move/delete bookmarks and changeswrite
Manage webhooksadmin
Manage API keysadmin

Default Scopes

When creating a key without specifying scopes, the default is:
["read", "write"]
This covers common development workflows without granting admin capabilities.

Repository Scoping

By default, API keys have access to all repositories in the organization. You can restrict a key to specific repositories by passing repo_ids when creating it:
const key = await mesa.admin.createApiKey({
  org: "my-org",
  body: {
    name: "Agent - frontend repo",
    scopes: ["read", "write"],
    repo_ids: ["repo-id-1", "repo-id-2"],
  },
});
A repo-scoped key can only access the repositories it was created for. Any attempt to access a repository outside its scope will return a FORBIDDEN error. This applies to all operations including API calls, git push/pull, and webhook management. Repository scoping is independent of permission scopes. Both must be satisfied. For example, a key with read scope and repo_ids: ["repo-id-1"] can only read repo-id-1.
Use repo-scoped keys when giving agents access to a single repository or a subset of repos. This limits the blast radius if a key is leaked.

Key Expiration

API keys can optionally be set to expire by passing expires_in_seconds when creating a key. The value must be between 100 seconds and 1 year (31,536,000 seconds). Omit the field to create a key that never expires. Expired keys are automatically invalidated and will return UNAUTHORIZED on use. You can also set expiration from the dashboard when creating a key.

Error Handling

Authentication Errors

try {
  await mesa.repos.list({ org: "my-org" });
} catch (error) {
  if (error.code === "UNAUTHORIZED") {
    // Invalid or missing API key
    console.error("Check your API key");
  }
}

Permission Errors

try {
  await mesa.repos.delete({ org: "my-org", repo: "important-repo" });
} catch (error) {
  if (error.code === "FORBIDDEN") {
    // API key lacks required scope
    console.error("API key needs write scope");
  }
}

Organization Access

API keys are scoped to a single organization. Attempting to access a different organization returns a forbidden error:
// Key belongs to "org-a"
const mesa = new Mesa({ apiKey: orgAKey });

// This will fail - wrong organization
await mesa.repos.list({ org: "org-b" }); // Error: FORBIDDEN

Best Practices

Use Scoped Keys

Create separate API keys for different purposes with minimal required scopes:
// Read-only key for monitoring or analytics
await mesa.admin.createApiKey({
  org: "my-org",
  body: {
    name: "Analytics Service",
    scopes: ["read"],
  },
});

// CI/CD key with 90-day expiration
await mesa.admin.createApiKey({
  org: "my-org",
  body: {
    name: "GitHub Actions",
    scopes: ["read", "write"],
    expires_in_seconds: 7776000,
  },
});
  • Set expirations on keys used in CI or sandboxes. Long-lived keys should be limited to read or write scope.
  • Rotate keys regularly. If a key is compromised, revoke it immediately from the dashboard or SDK.
  • Keys are always bound to a single organization. Even an admin key with no repo restrictions cannot access another organization’s resources.