Skip to main content

Overview

Service accounts are machine identities designed for automated systems, CI/CD pipelines, and applications that need programmatic access to secrets.

List Service Accounts

Retrieve all service accounts in an organization.
GET /organizations/{organizationId}/service-accounts?includeAccessToSecrets={includeAccessToSecrets}
organizationId
string
required
Organization ID
includeAccessToSecrets
boolean
default:"false"
Include detailed secret access information

Response

id
string
required
Service account unique identifier
organizationId
string
required
Parent organization ID
name
string
required
Service account name
creationDate
string
required
When service account was created
revisionDate
string
required
Last modification date
accessToSecrets
number
Number of secrets this service account can access

Get Service Account

Retrieve a specific service account.
GET /service-accounts/{id}
id
string
required
Service account ID

Create Service Account

Create a new service account.
curl -X POST "https://api.bitwarden.com/organizations/{organizationId}/service-accounts" \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CI/CD Pipeline"
  }'

Request Body

name
string
required
Service account name (e.g., “Production Deployment”, “Terraform”)
Creating a service account may require additional seats on your Secrets Manager subscription.

Update Service Account

Update a service account’s name.
PUT /service-accounts/{id}
id
string
required
Service account ID

Request Body

name
string
required
New service account name

Delete Service Accounts

Delete one or more service accounts.
POST /service-accounts/delete
ids
array
required
Array of service account IDs to delete

Response

Returns results for each deletion:
{
  "data": [
    {
      "id": "sa-guid-1",
      "error": ""
    },
    {
      "id": "sa-guid-2",
      "error": "access denied"
    }
  ]
}
Deleting a service account revokes all its access tokens. Active integrations will fail.

Access Tokens

List Access Tokens

Retrieve all access tokens for a service account.
GET /service-accounts/{id}/access-tokens
id
string
required
Service account ID

Response

id
string
required
Access token ID
name
string
required
Token name
creationDate
string
required
When token was created
expireAt
string
Token expiration date (null = no expiration)
revisionDate
string
required
Last modification date

Create Access Token

Generate a new access token for a service account.
POST /service-accounts/{id}/access-tokens
id
string
required
Service account ID

Request Body

name
string
required
Token name (e.g., “Production Token”, “Staging Deploy”)
encryptedPayload
string
required
Encrypted payload for the token
key
string
required
Encryption key
expireAt
string
Optional expiration date (ISO 8601 format)

Response

id
string
required
Token ID
clientSecret
string
required
The actual access token value (only returned once!)
name
string
required
Token name
creationDate
string
required
When token was created
The clientSecret is only returned when creating the token. Store it securely - you cannot retrieve it again!

Revoke Access Tokens

Revoke one or more access tokens.
POST /service-accounts/{id}/access-tokens/revoke
id
string
required
Service account ID
ids
array
required
Array of access token IDs to revoke

Access Management

Service accounts access secrets through projects. Grant access using access policies.

Grant Project Access

Allow a service account to access a project:
{
  "serviceAccountAccessPolicies": [
    {
      "serviceAccountId": "sa-guid",
      "read": true,
      "write": false
    }
  ]
}

Access Levels

  • Read: Can fetch secrets
  • Write: Can create/update secrets (typically not granted)
Most service accounts only need read access to fetch secrets for deployment.

Best Practices

Naming Conventions

Use descriptive names that indicate purpose:
[System/Tool] - [Environment]

Examples:
GitHub Actions - Production
Terraform - Staging
Kubernetes - Development
Jenkins - CI Pipeline

Security

  1. Least Privilege: Only grant access to required projects
  2. Rotate Tokens: Regenerate tokens periodically
  3. Set Expiration: Use token expiration dates
  4. Monitor Usage: Check event logs for suspicious activity
  5. Revoke Unused: Delete service accounts and tokens no longer needed

Token Management

  1. Name Descriptively: Indicate token purpose and location
  2. Use Expiration: Set expiration dates for production tokens
  3. Store Securely: Use secret management in your CI/CD system
  4. One Token per System: Don’t share tokens across systems
  5. Revoke Immediately: Remove compromised tokens right away

Usage Examples

CI/CD Integration (GitHub Actions)

name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      
      - name: Fetch Secrets
        env:
          BW_SERVICE_TOKEN: ${{ secrets.BW_SERVICE_TOKEN }}
        run: |
          # Fetch secrets from Bitwarden
          SECRETS=$(curl -s "https://api.bitwarden.com/projects/${PROJECT_ID}/secrets" \
            -H "Authorization: Bearer ${BW_SERVICE_TOKEN}")
          
          # Export as environment variables
          echo "API_KEY=$(echo $SECRETS | jq -r '.data[] | select(.key=="API_KEY") | .value')" >> $GITHUB_ENV
      
      - name: Deploy
        run: ./deploy.sh

Terraform Provider

provider "bitwarden" {
  access_token = var.bw_service_token
}

data "bitwarden_secret" "database_url" {
  key = "DATABASE_URL"
  project_id = var.project_id
}

resource "aws_db_instance" "main" {
  # Use secret in resource
  password = data.bitwarden_secret.database_url.value
}

Docker Container

FROM node:18

WORKDIR /app

# Install Bitwarden CLI
RUN npm install -g @bitwarden/cli

# Copy startup script
COPY entrypoint.sh /
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]
#!/bin/bash
# entrypoint.sh

# Fetch secrets
export SECRETS=$(curl -s "https://api.bitwarden.com/projects/${PROJECT_ID}/secrets" \
  -H "Authorization: Bearer ${BW_SERVICE_TOKEN}")

# Set environment variables
export DATABASE_URL=$(echo $SECRETS | jq -r '.data[] | select(.key=="DATABASE_URL") | .value')

# Start application
npm start

Kubernetes Secret Sync

import requests
import base64
from kubernetes import client, config

# Load k8s config
config.load_incluster_config()
v1 = client.CoreV1Api()

# Fetch secrets from Bitwarden
response = requests.get(
    f"https://api.bitwarden.com/projects/{project_id}/secrets",
    headers={"Authorization": f"Bearer {service_token}"}
)

secrets = response.json()['data']

# Create/update Kubernetes secret
secret_data = {}
for secret in secrets:
    secret_data[secret['key']] = base64.b64encode(
        secret['value'].encode()
    ).decode()

k8s_secret = client.V1Secret(
    metadata=client.V1ObjectMeta(name="app-secrets"),
    data=secret_data
)

v1.replace_namespaced_secret(
    name="app-secrets",
    namespace="default",
    body=k8s_secret
)

Service Account Limits

Service account limits vary by plan:
PlanService Accounts
Free (trial)0
TeamsStarts at 20, can purchase more
EnterpriseCustom
Service accounts count toward your Secrets Manager seat limit. Contact sales to add capacity.

Build docs developers (and LLMs) love