> ## Documentation Index
> Fetch the complete documentation index at: https://docs.emergence.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Service Accounts

> Service accounts enable background workers and automated processes to authenticate with CRAFT without user interaction.

# Service Accounts

Service accounts allow non-interactive processes, background workers, schedulers, cleanup jobs, and automated pipelines, to authenticate with CRAFT using machine identity.

Service accounts authenticate against the **master realm** in Keycloak (not the organization realm), which gives them platform-level access scoped by explicit organization and project headers.

## Detection

The platform identifies a request as coming from a service account when:

1. The client ID in the JWT begins with the `svc-` prefix (e.g., `svc-data-pipeline`)
2. The JWT contains the `serviceAccount` realm role
3. The authentication was performed against the Keycloak master realm

Non-interactive processes using regular user tokens are rejected at service account-protected endpoints.

## Authentication Flow

<Steps>
  <Step title="Obtain a Client Credentials Token">
    Service accounts use the OAuth 2.0 Client Credentials grant, no user interaction required.

    ```bash theme={null}
    curl -X POST \
      "https://api.example.com/keycloak/realms/master/protocol/openid-connect/token" \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -d "grant_type=client_credentials" \
      -d "client_id=svc-my-worker" \
      -d "client_secret=<client-secret>"
    ```

    Response:

    ```json theme={null}
    {
      "access_token": "<jwt-token>",
      "token_type": "Bearer",
      "expires_in": 3600
    }
    ```
  </Step>

  <Step title="Pass Service Account Context Headers">
    Service account JWTs do not carry tenant context. Pass these required headers:

    ```http theme={null}
    Authorization: Bearer <jwt-token>
    X-Org-Id: org_abc123
    X-Project-ID: proj_xyz789
    ```

    * `X-Org-Id`, the organization context (required; replaces the realm-derived org in user tokens)
    * `X-Project-ID`, the project scope for project-scoped endpoints (required when applicable)

    To attribute the request to a specific user in audit logs, also include `X-On-Behalf-Of: <user-id>` (optional).
  </Step>

  <Step title="Acting on Behalf of a User (optional)">
    The `X-On-Behalf-Of` header is recorded in audit logs but does not grant additional permissions. Include it whenever the service account is operating on a user's behalf. The `get_headers()` helper accepts it as an optional parameter.
  </Step>
</Steps>

## Creating a Service Account

<Steps>
  <Step title="Create a Confidential Client in Keycloak">
    In the Keycloak master realm admin console:

    1. Navigate to **Clients → Create client**
    2. Set **Client ID** with the `svc-` prefix (e.g., `svc-nightly-cleanup`)
    3. Set **Client Protocol**: `openid-connect`
    4. Set **Access Type**: `confidential`
    5. Enable **Service Accounts Enabled**
    6. Save and note the generated client secret
  </Step>

  <Step title="Assign the serviceAccount Role">
    In the client's **Service Account Roles** tab:

    1. Select **Realm Roles**
    2. Assign the `serviceAccount` role

    This role is required for the platform to recognize the client as a service account.
  </Step>

  <Step title="Grant Platform Permissions">
    Service accounts access the platform APIs with the same permission model as users. Grant the service account access to specific organizations and projects via the platform admin API or the Runtime UI.
  </Step>
</Steps>

## Use Cases

| Use Case                        | Example                                                             |
| ------------------------------- | ------------------------------------------------------------------- |
| **Scheduled data pipelines**    | Nightly data ingestion jobs that create agents and data connections |
| **Background cleanup**          | Purging expired sessions, rotating secrets, archiving old artifacts |
| **Workflow orchestration**      | Prefect workflows that call platform APIs to register results       |
| **Automated testing**           | CI/CD pipelines that create isolated test resources per run         |
| **Inter-service communication** | Solution services calling platform APIs                             |

## Token Expiry and Rotation

Service account tokens are short-lived (configurable, default 1 hour). Background workers should implement automatic token refresh:

```python theme={null}
import httpx
import time

class ServiceAccountClient:
    def __init__(self, client_id: str, client_secret: str, keycloak_url: str):
        self.client_id = client_id
        self.client_secret = client_secret
        self.keycloak_url = keycloak_url
        self._token = None
        self._expires_at = 0

    def _refresh_token(self):
        response = httpx.post(
            f"{self.keycloak_url}/realms/master/protocol/openid-connect/token",
            data={
                "grant_type": "client_credentials",
                "client_id": self.client_id,
                "client_secret": self.client_secret,
            }
        )
        data = response.json()
        self._token = data["access_token"]
        self._expires_at = time.time() + data["expires_in"] - 60  # 60s buffer

    @property
    def token(self) -> str:
        if not self._token or time.time() >= self._expires_at:
            self._refresh_token()
        return self._token

    def get_headers(self, org_id: str, project_id: str, on_behalf_of: str | None = None) -> dict:
        headers = {
            "Authorization": f"Bearer {self.token}",
            "X-Org-Id": org_id,
            "X-Project-ID": project_id,
        }
        if on_behalf_of:
            headers["X-On-Behalf-Of"] = on_behalf_of
        return headers
```

## Related

<CardGroup cols={2}>
  <Card title="Authentication" icon="lock" href="/platform/authentication">
    Overview of all authentication methods including user tokens and OIDC.
  </Card>

  <Card title="Authorization" icon="shield" href="/platform/authorization">
    How permissions are checked for service account requests.
  </Card>

  <Card title="Projects" icon="folder" href="/platform/projects">
    Project-level isolation and the X-Project-ID header.
  </Card>
</CardGroup>
