Service accounts

Updated

Service accounts let you authenticate with the Customer.io CLI and other programmatic tools without using your personal credentials. They provide long-lived tokens with fine-grained access controls—ideal for AI coding assistants, CI/CD pipelines, and automation scripts.

You can set up multiple service accounts and multiple tokens per account. You might have different service accounts for different projects or departments in your account, and different tokens within each account to represent different tools or users.

There are two types of service accounts:

  • User service account: tied to the person who creates it. Tokens inherit the creator’s permissions and stay in sync—if your permissions change, so do the token’s. The token only grants access to the workspaces the creator has access to. Use these for personal automation or tools you own.
  • System service account: not tied to a person. Tokens get their permissions from a role you select at creation time, and those permissions apply to all workspaces in the account. Use these for shared integrations so team changes don’t break the workflow. Only admins can create system service accounts.

Create a service account

  1. Go to Account Settings > API Credentials and click the Service Accounts tab.
  2. Click Create Service Account.
  3. Enter a name and optional description.
  4. Choose the service account type:
    • User service account: tokens inherit your permissions. Anyone can create one.
    • System service account: tokens get permissions from a role. Only available to admins.
  5. Click Create.

After creating the service account, you’re prompted to create your first token.

Create a token

A service account can have multiple tokens. Use separate tokens for different integrations so you can rotate or revoke them independently.

The token creation flow depends on the service account type.

User service account tokens

  1. In the Service Accounts tab, expand the service account.
  2. Click Create Token.
  3. Enter a Name for the token.
  4. Set Expiration: No expiration, 30, 60, 90 days, or 1 year.
  5. (Optional) Check Read-only to permanently restrict the token to GET requests.
  6. Click Create.

The token inherits your current permissions and only has access to the workspaces you have access to. If your permissions or access to workspaces change later, the token’s access changes to reflect this.

System service account tokens

  1. In the Service Accounts tab, expand the service account.
  2. Click Create Token.
  3. Enter a Name for the token.
  4. Set Expiration: No expiration, 30, 60, 90 days, or 1 year.
  5. Select a Role from the dropdown. The token gets that role’s permissions for all workspaces in the account.
  6. Click Create.

System tokens don’t have a read-only checkbox—their access is controlled by the role you assign.

Store tokens securely

After you create your token, copy it and store it in a secure location.

Service account tokens are long-lived and can grant full write access to your account. Treat them like passwords, and pick a storage method that matches how you’ll use the token.

Use caseRecommendationWhy
Local interactive developmentRun cio auth login to store your token in ~/.cio/config.jsonBuilt in; scoped to your user account
Local scripts or non-interactive runsA project-scoped .env file (git ignored), loaded with a tool like direnv or dotenvScoped to one project; rotate independently per project
CI/CD pipelinesYour CI provider’s secrets store (for example, GitHub Actions secrets), exposed as CIO_TOKENEncrypted at rest, masked in logs, auditable
Shared machinesYour OS keychain (macOS Keychain, secret-tool on Linux)Encrypted, per-user, no plaintext on disk

Best practices:

  • Don’t put sa_live_ tokens in shell config files like ~/.zshrc or ~/.bashrc. You don’t want to leak the token to these files, that might be synced somewhere or backed up to cloud storage.
  • Don’t commit .env files. If you use one, add it to your .gitignore and ship a .env.example with placeholder values for teammates.
  • Use one token per integration. Separate tokens let you rotate or revoke access for one tool without disrupting the others.

Revoke a token

Revoking a token immediately invalidates it and any active sessions. Other tokens on the same service account aren’t affected.

  1. Expand the service account in the Service Accounts tab.
  2. Find the token and click Revoke.

Delete a service account

Deleting a service account revokes all of its tokens. This can’t be undone.

Token permissions

Token permissions depend on the service account type.

User service account tokens

When you create a token on a user service account, it inherits your permissions. The token stays in sync with your permissions: if your role changes, the token’s access changes with it. The same is true for workspaces; the token only grants access to the workspaces you currently have access to.

You can further restrict a user token by checking Read-only when you create it. A read-only token only permits GET requests, regardless of your role. This restriction is permanent—you can’t remove it later.

You can also request a read-only session at token exchange time by passing scope=read_only. This creates a temporary read-only session without changing the underlying token.

The CLI exposes this as --read-only:

cio --read-only api /v1/environments/{environment_id}/campaigns \
  --params '{"environment_id":"123"}'

System service account tokens

When you create a token on a system service account, you choose a role. The token gets that role’s permissions for all workspaces in the account.

System tokens don’t have a read-only checkbox—the role controls what the token can do. You can still request a read-only session at token exchange time by passing scope=read_only.

If you sign up for a new account through the CLI using the cio auth signup verify command, the CLI generates an “admin” token with full permissions for the account to help you get set up. This is the only time you’ll create a token through the CLI. You’ll create subsequent tokens in the UI.

How service account token authentication works

Your sa_live_ token isn’t sent on every API call. The CLI exchanges it for a short-lived (1 hour) JSON Web Token (JWT) and sends the JWT as the bearer credential instead. The sa_live_ token only ever appears on the call to the token endpoint.

The exchange exists so the long-lived credential touches as few systems as possible. A JWT that leaks expires on its own; a sa_live_ token that leaks has to be rotated by hand. Exchanging also lets the CLI request a scoped session (for example, scope=read_only) without altering the underlying token.

JWT exchange without the CLI

If you want to integrate directly with the APIs exposed by our CLI, you’ll need to exchange your service account token for a JWT yourself.

curl -X POST https://us.fly.customer.io/v1/service_accounts/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_secret=sa_live_xxxxx"

For accounts in our EU region, use https://eu.fly.customer.io.

Form fieldRequiredDescription
grant_typeyesMust be client_credentials.
client_secretyesYour sa_live_ service account token.
scopenoSet to read_only for a session that only permits GET requests.

The exchange follows the OAuth 2.0 client credentials grant.

Response

{
  "access_token": "eyJhbGciOi...",
  "token_type": "Bearer",
  "expires_in": 3600
}

Send access_token as Authorization: Bearer <access_token> on subsequent API calls. When it’s close to expiry, repeat the exchange to get a new one. Cache the JWT between calls—the token endpoint is rate-limited per IP, so don’t exchange on every request.

Copied to clipboard!
  Contents