HeadlessOps
Integrations

Credentials Management

Store and use API keys and OAuth2 tokens in your integrations

Credentials Management

Credentials are named secrets stored securely in the workspace vault. They are referenced in step code via ctx.credentials.CREDENTIAL_NAME.

Credential Types

TypeDescription
api_keyA single secret string (API token, bearer key, etc.)
oauth2An OAuth2 connection with client ID/secret, auth URL, token URL, and scopes

Create a Credential

curl -X POST https://app.headlessops.ai/api/credentials \
  -H "Authorization: Bearer iter_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "api_key",
    "name": "STRIPE_KEY",
    "description": "Stripe production API key (Bearer auth, scopes: charges:read)",
    "value": "sk_live_...",
    "metadata": { "account_id": "acct_123", "mode": "live" }
  }'

Naming convention: Use UPPER_SNAKE_CASE for credential names.

Credential Apps (Connection Templates)

Credential Apps are reusable templates that pre-fill all non-secret OAuth2 fields. This is the recommended way to create OAuth2 credentials:

# 1. List available apps
curl https://app.headlessops.ai/api/credentials/apps \
  -H "Authorization: Bearer iter_YOUR_KEY"

# 2. Create a credential from the template
curl -X POST https://app.headlessops.ai/api/credentials/apps/slack/create-credential \
  -H "Authorization: Bearer iter_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "SLACK_PROD", "description": "Slack production workspace" }'

Invite links let external users supply their own credentials without accessing the platform:

Link TypeUse Case
api_key_fillExternal user pastes their API key
oauth2_fillExternal user enters client ID + secret, then authorizes
oauth2_authorizeAll OAuth2 fields pre-configured, link just triggers authorization
curl -X POST https://app.headlessops.ai/api/credentials/invites \
  -H "Authorization: Bearer iter_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "credentialId": "cred_abc123",
    "linkType": "api_key_fill",
    "ttl": "48h"
  }'

Response includes a public url to send to the user.

Using Credentials in Steps

export async function run(input: Record<string, any>, ctx: StepContext) {
    // Access the secret value
    const apiKey = ctx.credentials.STRIPE_KEY;

    // Access metadata
    const meta = ctx.credentials.meta.STRIPE_KEY;
    const accountId = meta.metadata?.account_id;

    const response = await ctx.http.get(
        "https://api.stripe.com/v1/charges",
        {
            headers: { Authorization: `Bearer ${apiKey}` },
            timeout: 25000,
        }
    );
    if (!response.ok) throw new Error(`Stripe API returned ${response.status}`);
    const data = await response.json();
    return data;
}