Skip to main content
Credentials allow your plugin to securely store and manage authentication information such as API keys, OAuth tokens, and other sensitive data.

What are Credentials?

Credentials in Atomemo plugins are:
  • User-provided authentication data (API keys, tokens, passwords)
  • Validated and stored securely by the Hub Server
  • Passed to tools that require authentication
  • Optional authentication logic via the authenticate method

Basic Credential Definition

1

Define the credential

Create a credential definition with required fields:
plugin.addCredential({
  name: "api-key",
  display_name: { en_US: "API Key" },
  description: { 
    en_US: "Enter your API key for authentication" 
  },
  icon: "🔑",
  parameters: [
    {
      name: "key",
      display_name: { en_US: "API Key" },
      description: { en_US: "Your secret API key" },
      type: "string",
      required: true,
      secret: true,
    },
  ],
})
2

Use in tools

Tools automatically receive validated credentials:
plugin.addTool({
  name: "fetch-data",
  display_name: { en_US: "Fetch Data" },
  credentials: ["api-key"], // Link credential
  invoke: async ({ args }) => {
    const apiKey = args.credentials?.["api-key"]?.key
    
    // Use the API key
    const response = await fetch("https://api.example.com/data", {
      headers: { "Authorization": `Bearer ${apiKey}` },
    })
    
    return await response.json()
  },
})

Credential Structure

Required Fields

FieldTypeDescription
namestringUnique identifier (kebab-case)
display_nameRecord<Locale, string>Human-readable name
descriptionRecord<Locale, string>What this credential is for
iconstringVisual identifier (emoji/icon)
parametersParameter[]Input fields for credential data

Optional Fields

FieldTypeDescription
authenticateFunctionCustom validation logic
help_urlstringLink to credential setup docs
extraRecord<string, any>Additional metadata

Parameter Types

Credentials can have multiple parameters with different types:

String Parameter

{
  name: "username",
  display_name: { en_US: "Username" },
  description: { en_US: "Your account username" },
  type: "string",
  required: true,
  secret: false, // Not sensitive
}

Secret Parameter

{
  name: "password",
  display_name: { en_US: "Password" },
  description: { en_US: "Your account password" },
  type: "string",
  required: true,
  secret: true, // Masked in UI
}

Number Parameter

{
  name: "port",
  display_name: { en_US: "Port" },
  description: { en_US: "Server port number" },
  type: "number",
  required: false,
  default: 443,
}

Boolean Parameter

{
  name: "use_ssl",
  display_name: { en_US: "Use SSL" },
  description: { en_US: "Enable SSL connection" },
  type: "boolean",
  required: false,
  default: true,
}

Custom Authentication

Implement the authenticate method to validate credentials:
plugin.addCredential({
  name: "oauth-token",
  display_name: { en_US: "OAuth Token" },
  description: { en_US: "Authenticate with OAuth 2.0" },
  icon: "🔐",
  parameters: [
    {
      name: "access_token",
      display_name: { en_US: "Access Token" },
      type: "string",
      required: true,
      secret: true,
    },
  ],
  authenticate: async ({ args }) => {
    const { credential, extra } = args
    const token = credential.access_token

    // Validate token with API
    const response = await fetch("https://api.example.com/verify", {
      headers: { "Authorization": `Bearer ${token}` },
    })

    if (!response.ok) {
      throw new Error("Invalid access token")
    }

    const data = await response.json()

    // Return validated credential data
    return {
      valid: true,
      user_id: data.user_id,
      expires_at: data.expires_at,
    }
  },
})

Authentication Flow

When a user provides credentials:
1

User submits credential data

The user enters credential information in the Atomemo UI:
{
  "access_token": "abc123xyz"
}
2

Hub Server sends auth request

The Hub Server sends a credential_auth_spec message:
{
  request_id: "req_123",
  credential_name: "oauth-token",
  credential: { access_token: "abc123xyz" },
  extra: { user_id: "user_456" }
}
3

Plugin validates credentials

Your authenticate function is called:
authenticate: async ({ args }) => {
  // Validate and return data
  return { valid: true, user_id: "user_456" }
}
4

Result returned to Hub

The plugin sends a credential_auth_spec_response:
{
  request_id: "req_123",
  data: { valid: true, user_id: "user_456" }
}

Real-World Examples

Example 1: Simple API Key

plugin.addCredential({
  name: "openai-api-key",
  display_name: { en_US: "OpenAI API Key" },
  description: { 
    en_US: "Your OpenAI API key from platform.openai.com" 
  },
  icon: "🤖",
  help_url: "https://platform.openai.com/api-keys",
  parameters: [
    {
      name: "api_key",
      display_name: { en_US: "API Key" },
      description: { en_US: "Starts with sk-" },
      type: "string",
      required: true,
      secret: true,
      placeholder: "sk-...",
    },
  ],
  authenticate: async ({ args }) => {
    const { credential } = args
    const apiKey = credential.api_key

    // Validate format
    if (!apiKey.startsWith("sk-")) {
      throw new Error("Invalid API key format")
    }

    // Test API key
    const response = await fetch("https://api.openai.com/v1/models", {
      headers: { "Authorization": `Bearer ${apiKey}` },
    })

    if (!response.ok) {
      throw new Error("Invalid API key")
    }

    return { valid: true }
  },
})

Example 2: Username and Password

plugin.addCredential({
  name: "database-credentials",
  display_name: { en_US: "Database Credentials" },
  description: { en_US: "PostgreSQL database connection" },
  icon: "🗄️",
  parameters: [
    {
      name: "host",
      display_name: { en_US: "Host" },
      type: "string",
      required: true,
      default: "localhost",
    },
    {
      name: "port",
      display_name: { en_US: "Port" },
      type: "number",
      required: false,
      default: 5432,
    },
    {
      name: "database",
      display_name: { en_US: "Database Name" },
      type: "string",
      required: true,
    },
    {
      name: "username",
      display_name: { en_US: "Username" },
      type: "string",
      required: true,
    },
    {
      name: "password",
      display_name: { en_US: "Password" },
      type: "string",
      required: true,
      secret: true,
    },
  ],
  authenticate: async ({ args }) => {
    const { credential } = args
    
    // Test database connection
    // (Example - actual implementation depends on your DB client)
    try {
      const client = new DatabaseClient({
        host: credential.host,
        port: credential.port,
        database: credential.database,
        username: credential.username,
        password: credential.password,
      })
      
      await client.connect()
      await client.query("SELECT 1")
      await client.disconnect()
      
      return { valid: true }
    } catch (error) {
      throw new Error(`Database connection failed: ${error.message}`)
    }
  },
})

Example 3: OAuth with Refresh Token

plugin.addCredential({
  name: "google-oauth",
  display_name: { en_US: "Google OAuth" },
  description: { en_US: "Authenticate with your Google account" },
  icon: "🔐",
  parameters: [
    {
      name: "access_token",
      display_name: { en_US: "Access Token" },
      type: "string",
      required: true,
      secret: true,
    },
    {
      name: "refresh_token",
      display_name: { en_US: "Refresh Token" },
      type: "string",
      required: true,
      secret: true,
    },
  ],
  authenticate: async ({ args }) => {
    const { credential } = args
    
    // Verify token
    const response = await fetch(
      "https://www.googleapis.com/oauth2/v1/tokeninfo",
      {
        headers: { "Authorization": `Bearer ${credential.access_token}` },
      }
    )

    if (!response.ok) {
      throw new Error("Invalid OAuth token")
    }

    const tokenInfo = await response.json()

    return {
      valid: true,
      email: tokenInfo.email,
      expires_at: tokenInfo.expires_in,
    }
  },
})

Error Handling

Handle authentication errors gracefully:
authenticate: async ({ args }) => {
  try {
    const { credential } = args
    
    const response = await fetch("https://api.example.com/verify", {
      headers: { "Authorization": `Bearer ${credential.token}` },
    })

    if (response.status === 401) {
      throw new Error("Invalid or expired token")
    }

    if (response.status === 403) {
      throw new Error("Insufficient permissions")
    }

    if (!response.ok) {
      throw new Error(`Authentication failed: ${response.statusText}`)
    }

    return { valid: true }
  } catch (error) {
    if (error instanceof Error) {
      throw error
    }
    throw new Error("Unknown authentication error")
  }
}
When an error is thrown, the SDK automatically sends an error response to the Hub Server:
// Automatic error handling by SDK
channel.push("credential_auth_spec_error", {
  request_id: message.request_id,
  message: error.message,
  stack: error.stack,
})

Best Practices

{
  name: "api_key",
  secret: true, // Masked in UI
}
description: {
  en_US: "Find your API key at example.com/settings/api"
},
help_url: "https://example.com/docs/api-keys"
authenticate: async ({ args }) => {
  const { credential } = args
  
  if (!credential.api_key.match(/^sk-[a-zA-Z0-9]{48}$/)) {
    throw new Error("Invalid API key format")
  }
  
  // Continue validation...
}
Make a simple API call to verify credentials work:
authenticate: async ({ args }) => {
  // Make test request
  const response = await fetch("https://api.example.com/test", {
    headers: { "Authorization": `Bearer ${args.credential.token}` },
  })
  
  if (!response.ok) {
    throw new Error("Credential verification failed")
  }
  
  return { valid: true }
}
authenticate: async ({ args }) => {
  const data = await verifyToken(args.credential.token)
  
  return {
    valid: true,
    user_id: data.user_id,
    email: data.email,
    expires_at: data.expires_at,
    scopes: data.scopes,
  }
}

Next Steps

Add Tools

Create tools that use your credentials

Tool Definition

API reference for tools

Build docs developers (and LLMs) love