Skip to content

API Keys

Programmatically manage API keys for authentication and access control.

Why Manage Keys Programmatically?

  • Automation: Create keys for new environments automatically
  • Security: Rotate keys regularly without manual intervention
  • Scope Control: Create limited-scope keys for specific services
  • Monitoring: Track key usage and detect anomalies

Create an API Key

typescript
const key = await client.apiKeys.create({
  name: 'Production Key',
  scopes: ['inboxes:write', 'messages:write']
})

console.log(key.id)
// → key_abc123

console.log(key.key)
// → mx_def456ghi789 (save this!)

console.log(key.scopes)
// → ['inboxes:write', 'messages:write']

Save the Key

The actual API key (key.key) is only shown once during creation. Save it securely - you won't be able to retrieve it again!

Parameters

ParameterTypeRequiredDescription
namestringYesHuman-readable name
scopesstring[]YesPermissions for this key
descriptionstringNoOptional description
usage_limit_per_daynumberNoDaily usage limit

Available Scopes

ScopePermissions
inboxes:readList and view inboxes
inboxes:writeCreate, update, and delete inboxes
messages:readList and view messages
messages:writeSend and delete messages
webhooks:readList and view webhooks
webhooks:writeCreate, update, and delete webhooks
api_keys:readList and view API keys
api_keys:writeCreate and revoke API keys

Read-Only Key

Create a key with only read permissions:

typescript
const monitoringKey = await client.apiKeys.create({
  name: 'Monitoring Dashboard',
  scopes: ['inboxes:read', 'messages:read'],
  description: 'Read-only access for monitoring'
})

Principle of Least Privilege

Always create keys with the minimum scopes required. This limits damage if a key is compromised.

Send-Only Key

Create a key that can only send emails:

typescript
const senderKey = await client.apiKeys.create({
  name: 'Email Sender Service',
  scopes: ['messages:write'],
  description: 'Can only send emails, not read them'
})

Key with Usage Limit

Prevent runaway costs with daily usage limits:

typescript
const testKey = await client.apiKeys.create({
  name: 'Test Environment',
  scopes: ['inboxes:write', 'messages:write'],
  usage_limit_per_day: 100,  // Max 100 API calls per day
  description: 'Limited key for testing'
})

List API Keys

typescript
const response = await client.apiKeys.list({
  limit: 20,
  offset: 0
})

for (const key of response.data) {
  console.log(`${key.name} (${key.id})`)
  console.log(`Scopes: ${key.scopes.join(', ')}`)
  console.log(`Created: ${key.created_at}`)
  console.log('---')
}

Get a Single Key

typescript
const key = await client.apiKeys.get('key_abc123')

console.log(key.name)
console.log(key.scopes)
console.log(key.usage_limit_per_day)

Can't Retrieve Key Value

You can view key metadata (name, scopes, etc.) but not the actual key value (mx_...). The key value is only shown during creation.

Check Key Usage

Monitor how much an API key is being used:

typescript
const usage = await client.apiKeys.getUsage('key_abc123')

console.log(`Today: ${usage.today} calls`)
console.log(`This week: ${usage.this_week} calls`)
console.log(`This month: ${usage.this_month} calls`)
console.log(`All time: ${usage.all_time} calls`)

Usage Monitoring Example

typescript
// Alert on high usage
const keys = await client.apiKeys.list()

for (const key of keys.data) {
  const usage = await client.apiKeys.getUsage(key.id)

  if (usage.today > 5000) {
    console.warn(`⚠️ High usage on "${key.name}": ${usage.today} calls today`)
  }

  if (key.usage_limit_per_day) {
    const percentUsed = (usage.today / key.usage_limit_per_day) * 100
    console.log(`"${key.name}": ${percentUsed.toFixed(1)}% of daily limit`)
  }
}

Revoke an API Key

Permanently disable a key:

typescript
await client.apiKeys.revoke('key_abc123')

This is Permanent

Revoking a key:

  • Immediately stops all requests using that key
  • Cannot be undone
  • Requires creating a new key to restore access

Make sure you update any services using the old key first!

Rotate Keys

Best practice: regularly rotate your API keys.

typescript
async function rotateKey(oldKeyId: string) {
  // 1. Create new key with same scopes
  const oldKey = await client.apiKeys.get(oldKeyId)

  const newKey = await client.apiKeys.create({
    name: `${oldKey.name} (rotated ${new Date().toISOString().split('T')[0]})`,
    scopes: oldKey.scopes,
    description: oldKey.description,
    usage_limit_per_day: oldKey.usage_limit_per_day
  })

  console.log('New key created:', newKey.key)
  console.log('Update your environment variables with this key!')
  console.log('Then revoke the old key:')
  console.log(`  client.apiKeys.revoke('${oldKeyId}')`)

  return newKey
}

// Usage
await rotateKey('key_abc123')

Key Rotation Strategy

Manual Rotation

typescript
// 1. Create new key
const newKey = await client.apiKeys.create({
  name: 'Production Key (2025-01)',
  scopes: ['inboxes:write', 'messages:write']
})

// 2. Update your .env file with newKey.key
// 3. Deploy updated environment variable
// 4. Revoke old key after verification
await client.apiKeys.revoke('old_key_id')

Automated Rotation (90 days)

typescript
async function autoRotateKeys() {
  const keys = await client.apiKeys.list()
  const ninetyDaysAgo = new Date()
  ninetyDaysAgo.setDate(ninetyDaysAgo.getDate() - 90)

  for (const key of keys.data) {
    const createdAt = new Date(key.created_at)

    if (createdAt < ninetyDaysAgo) {
      console.log(`Key "${key.name}" is ${Math.floor((Date.now() - createdAt.getTime()) / (1000 * 60 * 60 * 24))} days old`)

      // Create replacement
      const newKey = await client.apiKeys.create({
        name: `${key.name} (rotated)`,
        scopes: key.scopes,
        description: key.description
      })

      console.log(`Created replacement: ${newKey.id}`)
      console.log(`New key: ${newKey.key}`)
      console.log(`Update your environment, then revoke: ${key.id}`)
    }
  }
}

// Run monthly via cron
autoRotateKeys().catch(console.error)

Complete Example

typescript
import { MyxaraClient } from '@myxara/sdk-js'

const client = new MyxaraClient({
  apiKey: process.env.MYXARA_API_KEY!
})

async function setupAPIKeys() {
  // Production key - full access
  const prodKey = await client.apiKeys.create({
    name: 'Production Service',
    scopes: [
      'inboxes:write',
      'messages:write',
      'webhooks:write'
    ],
    description: 'Main production key',
    usage_limit_per_day: 50000
  })

  console.log('Production key:', prodKey.key)

  // Analytics key - read only
  const analyticsKey = await client.apiKeys.create({
    name: 'Analytics Dashboard',
    scopes: [
      'inboxes:read',
      'messages:read'
    ],
    description: 'Read-only for analytics',
    usage_limit_per_day: 10000
  })

  console.log('Analytics key:', analyticsKey.key)

  // Email sender key - send only
  const senderKey = await client.apiKeys.create({
    name: 'Email Sender Worker',
    scopes: ['messages:write'],
    description: 'Send emails only',
    usage_limit_per_day: 30000
  })

  console.log('Sender key:', senderKey.key)

  // Test key - limited usage
  const testKey = await client.apiKeys.create({
    name: 'Test Environment',
    scopes: ['inboxes:write', 'messages:write'],
    description: 'For testing',
    usage_limit_per_day: 100
  })

  console.log('Test key:', testKey.key)

  // Monitor usage
  console.log('\nCurrent usage:')
  const keys = await client.apiKeys.list()

  for (const key of keys.data) {
    const usage = await client.apiKeys.getUsage(key.id)
    console.log(`${key.name}: ${usage.today} calls today`)
  }
}

setupAPIKeys().catch(console.error)

Security Best Practices

1. Never Commit Keys to Git

bash
# .gitignore
.env
.env.local
*.key
secrets/

2. Use Environment Variables

typescript
// ✅ Good
const client = new MyxaraClient({
  apiKey: process.env.MYXARA_API_KEY!
})

// ❌ Bad
const client = new MyxaraClient({
  apiKey: 'mx_hardcoded_key'
})

3. Scope Keys Appropriately

typescript
// ✅ Good - minimal scopes
const webhookKey = await client.apiKeys.create({
  name: 'Webhook Processor',
  scopes: ['messages:read']  // Only what's needed
})

// ❌ Bad - excessive scopes
const webhookKey = await client.apiKeys.create({
  name: 'Webhook Processor',
  scopes: ['*']  // Too broad!
})

4. Set Usage Limits

typescript
// ✅ Good - protected against abuse
const key = await client.apiKeys.create({
  name: 'Public API',
  scopes: ['messages:write'],
  usage_limit_per_day: 1000  // Fail-safe
})

5. Monitor and Alert

typescript
// Check usage daily
async function checkUsage() {
  const keys = await client.apiKeys.list()

  for (const key of keys.data) {
    const usage = await client.apiKeys.getUsage(key.id)

    if (key.usage_limit_per_day) {
      const percent = (usage.today / key.usage_limit_per_day) * 100

      if (percent > 80) {
        // Send alert
        console.warn(`⚠️ Key "${key.name}" at ${percent.toFixed(1)}% of limit`)
      }
    }
  }
}

Error Handling

typescript
import { AuthenticationError, APIError } from '@myxara/sdk-js'

try {
  const key = await client.apiKeys.create({
    name: 'New Key',
    scopes: ['inboxes:write']
  })
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error('Not authorized to create API keys')
  } else if (error instanceof APIError) {
    if (error.status === 429) {
      console.error('Rate limited - too many keys created')
    } else {
      console.error('API error:', error.message)
    }
  }
}

TypeScript Types

typescript
import type {
  APIKey,
  CreateAPIKeyParams,
  APIKeyUsage,
  ListResponse
} from '@myxara/sdk-js'

const params: CreateAPIKeyParams = {
  name: 'My Key',
  scopes: ['messages:write'],
  usage_limit_per_day: 1000
}

const key: APIKey = await client.apiKeys.create(params)

const usage: APIKeyUsage = await client.apiKeys.getUsage(key.id)

const response: ListResponse<APIKey> = await client.apiKeys.list()

Next Steps

Released under the MIT License (SDK) & Elastic License 2.0 (Server)