Ensemble Docs

Authentication

Authenticate external integrations using API Keys or signed JWT tokens.

Ensemble provides two authentication methods for external integrations. Both are designed for server-side use — the difference is whether your integration is purely backend or needs to pass credentials to a frontend.

When to Use Which

MethodUse CaseSecret Location
API KeyServer-to-server integrationsYour backend only
Signed JWTEmbedded frontends (Chat Widget, custom UIs)Your backend signs, frontend uses token

API Key Authentication

Use API keys for direct backend-to-backend integrations where no browser/frontend is involved.

Creating an API Key

  1. Go to SettingsAPI Keys in Ensemble
  2. Click Create API Key
  3. Give it a description (e.g., "Production Backend")
  4. Copy the key — it won't be shown again

Using the API Key

Include the key and tenant ID in your request headers:

curl -X POST https://service.ensembleapp.ai/api/agents/{agentId}/invoke \
  -H "Content-Type: application/json" \
  -H "x-api-key: sk_your_api_key" \
  -H "x-tenant-id: your_tenant_id" \
  -d '{"message": "Hello"}'
HeaderDescription
x-api-keyYour API key (starts with sk_)
x-tenant-idYour tenant ID

Security Notes

  • Never expose API keys to browsers or frontend code
  • Store keys in environment variables or a secrets manager
  • Rotate keys periodically
  • Each key can be disabled independently if compromised

Signed JWT Authentication

Use signed JWTs when you need to pass credentials to a frontend (like the Chat Widget) while keeping your secret secure on the server.

How It Works

  1. Create a secret in Ensemble (Settings → Secrets) — one-time setup
  2. Your frontend requests a token from your backend
  3. Your backend signs a JWT using the secret
  4. Your backend returns the short-lived JWT to the frontend
  5. Your frontend calls Ensemble APIs with the JWT
  6. Ensemble verifies the signature matches your secret
  7. Ensemble returns the response

Creating a Secret

  1. Go to SettingsSecrets in Ensemble
  2. Click Create Secret
  3. Give it a description (e.g., "JWT Signing")
  4. Copy the Secret ID (Key ID) and Secret Value — the secret value won't be shown again Create JWT secret via Ensemble settings

Signing JWTs

Use the secret to sign JWT tokens on your backend. The token must include:

  • sub - A unique user identifier for tracking purposes
  • exp - Expiration in seconds from now
  • aud - Must be ensembleapp.ai
  • keyid - Ensemble's generated secret ID.
  • <JWT secret> - Ensemble's generated JWT secret - should be an environment variable.
// Example Backend using jsonwebtoken (Node.js)
const jwt = require('jsonwebtoken');

const token = jwt.sign(
  {
    // a unique user id for tracking purposes (e.g. user id on your system)
    sub: 'user-123',
    // expire in 3600 seconds (1 hour)
    exp: nowInSeconds + 3600,
    iat: nowInSeconds,
    aud: 'ensembleapp.ai',
  },
  process.env.ENSEMBLE_JWT_SECRET,
  {
    algorithm: 'HS256',
    // The Ensemble ID associated with the secret
    keyid: 'ensemble-secret-id',
  }
);

Passing Secure Context

You can embed trusted data context in the JWT that will be available to the invoked agents and tools. This context cannot be tampered with by the client or other unsecured data context.

const token = jwt.sign(
  {
    sub: 'rjohnson-123',
    ...
    context: {
      name: "Ron Johnson",
      email: "ron@ensembleapps.ai",
    }
  },
  ...
);

Using the JWT

Pass the token in the Authorization header:

curl -X POST https://service.ensembleapp.ai/api/agents/{agentId}/invoke \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your_jwt_token" \
  -d '{"message": "Hello"}'

Or with the Chat Widget:

import { ChatWidget } from '@ensembleapp/client-sdk';

function Chat() {
  const [token, setToken] = useState<string>();

  useEffect(() => {
    // Fetch token from your backend
    fetch('/api/ensemble-token')
      .then(res => res.json())
      .then(data => setToken(data.token));
  }, []);

  if (!token) return <div>Loading...</div>;

  return (
    <ChatWidget
      api={{
        baseUrl: 'https://service.ensembleapp.ai',
        token: token,
      }}
      agentId="your-agent-id"
      threadId={`user-${userId}`}
    />
  );
}

Token Expiration

  • Keep tokens short-lived (1 hour or less recommended)
  • Your frontend should handle token refresh before expiration
  • If a token expires, fetch a new one from your backend

Comparison

AspectAPI KeySigned JWT
ComplexitySimpleRequires signing logic
Frontend-safeNoYes (token only, not secret)
Secured contextNoYes (claims in JWT)
ExpirationNever (until rotated)Built-in (exp claim)
Best forCron jobs, webhooks, CI/CDChat widgets, user-facing apps

On this page