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

# Service Accounts

> Create and manage non-human identities for programmatic access to the ContraForce v2 API, with per-credential API scopes and cross-workspace authorization.

Service accounts are non-human identities that your applications, scripts, and integrations use to call the ContraForce v2 API. Each service account authenticates with a Client ID and Client Secret, and every credential carries its own set of granular API scopes that determine exactly what it can do across the organization.

<Info>
  **Who is this for?** Organization Admins and security engineers who need to connect external tools, SIEM pipelines, ticketing systems, or custom automations to ContraForce without using a human user account.
</Info>

## Prerequisites

* **Organization Admin** role, or the `org:service-accounts:manage` API scope on an existing credential
* A clear understanding of the API scopes the integration needs — start with the minimum and expand as required

<Warning>
  Service accounts can only call the **v2 API** (`https://portal.contraforce.com/api/v2/...`). Requests to the v1 API are rejected with `403 Forbidden`.
</Warning>

## How Authorization Works

Authorization for service accounts is driven by **API scopes on each credential**. Scopes are the single source of truth for what a credential is allowed to do.

* **Scopes decide *what*** — each scope (for example, `incidents:read`) authorizes a specific class of API action.
* **Cross-workspace by default** — v2 endpoints that span workspaces (such as `POST /api/v2/incidents/across-workspaces`) run as scope-authorized calls and return data from every workspace the service account can see in one request. There is no need to target a single workspace per call.
* **Multiple credentials per account** — a single service account can carry several credentials, each with a different scope set and expiration. This enables zero-downtime rotation and lets one integration hold read-only scopes while another carries write scopes.

## Where to Find Service Accounts

Navigate to **Settings → Developer Integrations** in the ContraForce Portal. Service accounts appear in the **Integrations** table alongside webhooks, marked with a key icon and the type **Service Account**.

## Creating a Service Account

<Steps>
  <Step title="Open Developer Integrations">
    In the Portal, go to **Settings → Developer Integrations** and click **Add Integration → Service Account**.
  </Step>

  <Step title="Enter basic details">
    Provide a **Name** (up to 200 characters) and an optional **Description** (up to 1000 characters). Use a name that identifies the calling system — for example, `SIEM-ingest-prod` or `Ticketing-bridge`.
  </Step>

  <Step title="Select API scopes for the initial credential">
    Pick the scopes the first credential should carry. Scopes follow the `{resource}:{action}` pattern and are grouped into **Workspace Scopes** (operate on workspace-owned resources such as incidents and Gamebooks) and **Organization Scopes** (operate on org-level resources such as users and webhooks).

    <Tip>
      Follow least-privilege. Grant only the scopes the integration actually calls. You can always issue a second credential with broader scopes later.
    </Tip>
  </Step>

  <Step title="Optional — set credential expiration">
    Choose an expiration date if the credential should automatically stop working after a certain point. Leave blank for a credential that never expires.
  </Step>

  <Step title="Create and capture the secret">
    Click **Create**. ContraForce returns the **Client ID** and a **Client Secret**.

    <Warning>
      The Client Secret is shown **once** and cannot be retrieved again. Copy it immediately into your secret store (Azure Key Vault, AWS Secrets Manager, HashiCorp Vault, etc.). If you lose it, revoke the credential and issue a new one.
    </Warning>
  </Step>
</Steps>

## Authenticating to the v2 API

Service accounts use **HTTP Basic authentication** (RFC 7617). Encode `ClientId:ClientSecret` in Base64 and send it in the `Authorization` header.

```bash theme={null}
curl https://portal.contraforce.com/api/v2/incidents/across-workspaces \
  -H "Authorization: Basic $(echo -n 'CLIENT_ID:CLIENT_SECRET' | base64)" \
  -H "Content-Type: application/json" \
  -d '{"isFirstCall": true}'
```

On success, ContraForce records usage metadata on the credential (`LastUsedAt`) and writes an entry to the service account's **Request Logs**.

### Authentication failures

| Response           | Likely cause                                                                          |
| ------------------ | ------------------------------------------------------------------------------------- |
| `401 Unauthorized` | Invalid Client ID, invalid Client Secret, or malformed `Authorization` header         |
| `403 Forbidden`    | Credential is revoked, expired, or the service account is disabled                    |
| `403 Forbidden`    | Request targets a v1 route (`/api/...` without `/v2/`) — service accounts are v2-only |
| `403 Forbidden`    | Authenticated but the credential is missing the scope the endpoint requires           |

## API Scopes Reference

Scopes are granted **per credential**. A service account can hold multiple credentials with different scope sets.

### Workspace scopes

Authorize actions against workspace-owned resources. Cross-workspace endpoints use these scopes and automatically return only data from workspaces the service account is allowed to see.

| Scope                | Grants                                                        |
| -------------------- | ------------------------------------------------------------- |
| `incidents:read`     | View incidents, entities, evidence, alert rules, and comments |
| `incidents:write`    | Assign incidents, update status, and perform bulk updates     |
| `incidents:comments` | Create, update, and delete incident comments                  |
| `gamebooks:read`     | View Gamebooks, Gamebook history, and incident Gamebooks      |
| `datasources:read`   | View connected data sources                                   |
| `tickets:read`       | Search service tickets                                        |
| `tickets:manage`     | Link and unlink tickets to incidents                          |
| `investigation:read` | View user sign-in logs and directory logs                     |

### Organization scopes

Authorize actions on org-level resources.

| Scope                         | Grants                                                   |
| ----------------------------- | -------------------------------------------------------- |
| `webhooks:read`               | View webhook configurations and delivery logs            |
| `webhooks:manage`             | Create, update, delete, pause, resume, and test webhooks |
| `org:users:read`              | View organization users and profiles                     |
| `org:users:manage`            | Add, update, and remove users                            |
| `org:users:roles`             | Assign and modify organizational roles                   |
| `org:service-accounts:read`   | View service accounts and credentials                    |
| `org:service-accounts:manage` | Create, update, disable, and delete service accounts     |

<Info>
  Write scopes imply their matching read scope. Granting `incidents:write` automatically authorizes `incidents:read` on the same endpoint set.
</Info>

<CardGroup cols={2}>
  <Card title="Endpoint Reference" icon="route" href="/api-reference/endpoints">
    Every v2 endpoint grouped by scope with full route paths
  </Card>

  <Card title="Object Models" icon="code" href="/api-reference/models">
    Copy-pasteable JSON request and response examples
  </Card>
</CardGroup>

## Managing the Service Account

Open a service account's detail page from **Settings → Developer Integrations** to manage it. The page has three tabs: **General**, **Request Logs**, and **Audit**.

### General tab

* **Disable** — Blocks all credentials on the service account from authenticating. Use this for a temporary freeze (for example, during an incident).
* **Enable** — Re-activates a disabled service account.
* **Delete** — Permanently removes the service account and all its credentials. Cannot be undone.
* **Edit** — Update name and description.

<Warning>
  Deleting a service account breaks every integration using its credentials. Disable first and monitor **Request Logs** for any stragglers before deleting.
</Warning>

### Credential management

Each credential shows its prefix (first 6 characters), status, expiration, scopes, last-used timestamp, and creation date.

| Action             | What it does                                                                                                                  |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------- |
| **New Credential** | Issues an additional credential with its own scopes and expiration. Use for rotation or to split read vs. write integrations. |
| **Revoke**         | Immediately invalidates a credential. Cannot be undone.                                                                       |

### Request Logs tab

Shows every API call made using any credential on the service account: timestamp, HTTP method, path, response status, latency, and source IP. Use this to audit integration behavior or troubleshoot `403` failures.

### Audit tab

Shows lifecycle events — creation, updates, credential issuance, revocation, disable/enable, and deletion — attributed to the Portal user who performed each action.

## Rotating a Credential (Zero Downtime)

Because a service account can hold multiple active credentials, you can rotate secrets without an outage.

<Steps>
  <Step title="Issue a new credential">
    On the service account's **General** tab, click **New Credential**. Select the same scopes as the credential you're rotating and copy the new Client Secret immediately.
  </Step>

  <Step title="Deploy the new secret">
    Update your integration's secret store with the new Client Secret. Deploy the configuration change.
  </Step>

  <Step title="Verify traffic on the new credential">
    Check **Request Logs** and confirm that requests are coming in. The new credential's **Last used** timestamp updates within seconds of the first authenticated call.
  </Step>

  <Step title="Revoke the old credential">
    Once all traffic has shifted, click **Revoke** on the old credential. Any residual caller using the old secret will receive `403 Forbidden`.
  </Step>
</Steps>

## How Service Accounts Work

* **Identity** — Each service account is an organization member with `IsServiceAccount = true`. It is not visible in **Settings → User Management** and cannot sign in to the Portal.
* **Client ID** — The stable GUID of the service account. Safe to share.
* **Client Secret** — 32 cryptographically random bytes, Base64-encoded. Stored as a SHA-256 hash (for authentication lookups) and as AES-256-GCM ciphertext wrapped by an RSA key in Azure Key Vault (envelope encryption). The plaintext is shown only at creation.
* **Authorization** — Every v2 endpoint declares a required scope. On each request, the authentication middleware resolves the credential, confirms it is active and unexpired, and projects its scopes onto the request principal. The endpoint's scope filter then allows or denies. Endpoints that span workspaces filter returned data to the set of workspaces the service account is entitled to see.
* **Isolation** — Service accounts are deliberately restricted to the v2 API. The v2 design uses resource-based routing with per-request scope authorization, removing an authorization bypass that existed in v1.

## Best Practices

* **One service account per integration.** Don't share credentials across systems — it fragments audit trails and breaks rotation.
* **Split read and write.** Issue separate credentials for read-only monitoring and write automations on the same service account, so a leak of the monitoring secret can't mutate state.
* **Minimum scopes.** Grant read-only scopes first. Add write scopes only when an integration actually needs to mutate state.
* **Expiration everywhere.** Set a credential expiration for anything non-critical and renew on a schedule.
* **Name for humans.** Future-you will grep the audit log. `Jira-incident-sync-prod` beats `sa-3`.
* **Monitor the Request Logs.** A sudden drop in traffic usually means a rotation went wrong. A sudden spike may mean a runaway script.

## Troubleshooting

| Symptom                                                         | Fix                                                                                                  |
| --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `401 Unauthorized` immediately after creation                   | Verify the `Authorization` header is `Basic <base64(ClientId:ClientSecret)>` with no whitespace      |
| `403 Forbidden` with "Service accounts must use the v2 API"     | Change the URL to `/api/v2/...`                                                                      |
| `403 Forbidden` on a specific endpoint only                     | The credential is missing the required scope. Issue a new credential with the correct scope.         |
| `403 Forbidden` with "credential is not active"                 | The credential was revoked or expired. Issue a new credential.                                       |
| `403 Forbidden` with "service account is disabled or not found" | Re-enable the service account under **Settings → Developer Integrations**                            |
| Cross-workspace endpoint returns an empty list                  | The service account has no visible workspaces, or no data matched the filters in visible workspaces. |
| Credential works intermittently                                 | Confirm no other integration is rotating the same credential. Use one credential per integration.    |

<Note>
  If you have questions about service accounts or need help planning an integration, contact us at [support@contraforce.com](mailto:support@contraforce.com).
</Note>
