Authentication

Learn how to authenticate with the Transactional API.

Authentication

The Transactional API uses API keys to authenticate requests. All API requests must include a valid API key.


Getting Your API Key

  1. Log in to the Transactional Dashboard
  2. Navigate to Settings > API Keys
  3. Click Create API Key
  4. Give your key a name and select the appropriate type
  5. Copy the key - it will only be shown once!

API Key Types

Transactional provides different API key types for different use cases:

TypePrefixUse Case
Servertr_srv_Full API access from your backend
Sendingtr_snd_Send-only access for email/SMS
Publictr_pub_Client-side form submissions

Server Keys

Server keys have full API access and should only be used in secure server environments:

// Server-side only
const client = new Transactional({
  apiKey: process.env.TRANSACTIONAL_SERVER_KEY, // tr_srv_...
});
 
// Full access to all endpoints
await client.emails.send(...);
await client.templates.list();
await client.suppressions.add(...);

Sending Keys

Sending keys can only send messages and cannot read data:

const client = new Transactional({
  apiKey: process.env.TRANSACTIONAL_SENDING_KEY, // tr_snd_...
});
 
// Can send
await client.emails.send(...);
await client.sms.send(...);
 
// Cannot read - will throw error
await client.emails.messages.list(); // 403 Forbidden

Public Keys

Public keys are safe to use in client-side code for form submissions:

// Safe for client-side use
const client = new Transactional({
  apiKey: 'tr_pub_...', // Can be exposed in browser
});
 
// Only form submission endpoints
await client.forms.submit(formId, data);

Using Your API Key

Authorization Header

Include your API key in the Authorization header:

curl https://api.usetransactional.com/v1/emails \
  -H "Authorization: Bearer tr_srv_your_api_key"

X-API-Key Header

Alternatively, use the X-API-Key header:

curl https://api.usetransactional.com/v1/emails \
  -H "X-API-Key: tr_srv_your_api_key"

Both methods are equivalent. Choose whichever fits your workflow.


SDK Authentication

TypeScript SDK

import { Transactional } from 'transactional-sdk';
 
// Basic authentication
const client = new Transactional({
  apiKey: process.env.TRANSACTIONAL_API_KEY,
});
 
// With additional options
const client = new Transactional({
  apiKey: process.env.TRANSACTIONAL_API_KEY,
  baseUrl: 'https://api.usetransactional.com/v1', // Default
  timeout: 30000, // 30 seconds
  retries: 3, // Retry failed requests
});

Python SDK

from transactional import Transactional
import os
 
# Basic authentication
client = Transactional(api_key=os.environ["TRANSACTIONAL_API_KEY"])
 
# With additional options
client = Transactional(
    api_key=os.environ["TRANSACTIONAL_API_KEY"],
    base_url="https://api.usetransactional.com/v1",  # Default
    timeout=30.0,  # 30 seconds
    retries=3,  # Retry failed requests
)
 
# Async client
from transactional import AsyncTransactional
 
async_client = AsyncTransactional(api_key=os.environ["TRANSACTIONAL_API_KEY"])

Environment Variables

We recommend storing API keys in environment variables:

Node.js (.env)

# .env
TRANSACTIONAL_API_KEY=tr_srv_your_api_key
import { Transactional } from 'transactional-sdk';
import 'dotenv/config';
 
const client = new Transactional({
  apiKey: process.env.TRANSACTIONAL_API_KEY!,
});

Python

# .env
TRANSACTIONAL_API_KEY=tr_srv_your_api_key
from transactional import Transactional
from dotenv import load_dotenv
import os
 
load_dotenv()
 
client = Transactional(api_key=os.environ["TRANSACTIONAL_API_KEY"])

Framework-Specific

Next.js:

# .env.local
TRANSACTIONAL_API_KEY=tr_srv_your_api_key

Vercel:

vercel env add TRANSACTIONAL_API_KEY

Docker:

# docker-compose.yml
services:
  app:
    environment:
      - TRANSACTIONAL_API_KEY=tr_srv_your_api_key

API Key Security

Follow these best practices to keep your API keys secure:

Do's

  • Store keys in environment variables - Never hardcode keys
  • Use separate keys for development and production
  • Rotate keys regularly - Especially after team changes
  • Limit key permissions - Use sending keys when full access isn't needed
  • Monitor key usage - Check for unexpected activity in the dashboard

Don'ts

  • Never commit keys to version control - Use .gitignore
  • Never expose server keys in client-side code
  • Never share keys via email or chat
  • Never log keys in application logs

.gitignore Example

# Environment files
.env
.env.local
.env.*.local
 
# API keys (if accidentally created)
*.key
api_key.txt

Key Permissions

API keys can be scoped to specific permissions:

PermissionDescription
emails:sendSend transactional emails
emails:readView email history and analytics
emails:manageManage templates, domains, senders
sms:sendSend SMS messages
sms:readView SMS history
sms:manageManage templates, webhooks
forms:readView forms and submissions
forms:manageCreate and edit forms
support:readView conversations
support:manageReply to and manage conversations
auth:readView users and settings
auth:manageManage users and authentication
calendar:readView bookings
calendar:manageManage event types and schedules
domains:readView domains and DNS
domains:manageRegister and manage domains
webhooks:manageConfigure webhook endpoints
billing:readView billing information

Creating Scoped Keys

curl -X POST https://api.usetransactional.com/v1/api-keys \
  -H "Authorization: Bearer tr_srv_admin_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Email Service",
    "type": "SERVER",
    "permissions": ["emails:send", "emails:read"]
  }'

Rotating API Keys

To rotate an API key:

  1. Create a new API key with the same permissions
  2. Update your application to use the new key
  3. Monitor that the new key is working
  4. Revoke the old key

Rotating via API

// Create new key
const newKey = await client.apiKeys.create({
  name: 'Production Server (Rotated)',
  type: 'SERVER',
  permissions: ['emails:send', 'emails:read'],
});
 
console.log(`New key: ${newKey.key}`); // Only shown once!
 
// After updating your application...
// Revoke old key
await client.apiKeys.revoke(oldKeyId);

Testing Authentication

Verify Your Key

Test that your key is valid:

curl https://api.usetransactional.com/v1/ping \
  -H "Authorization: Bearer YOUR_API_KEY"

Success Response:

{
  "success": true,
  "data": {
    "message": "pong",
    "organization": "your-org-name",
    "keyType": "SERVER",
    "permissions": ["emails:send", "emails:read", "..."]
  }
}

Invalid Key Response:

{
  "success": false,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid API key"
  }
}

SDK Verification

try {
  const response = await client.ping();
  console.log(`Connected as: ${response.organization}`);
  console.log(`Key type: ${response.keyType}`);
  console.log(`Permissions: ${response.permissions.join(', ')}`);
} catch (error) {
  if (error.code === 'UNAUTHORIZED') {
    console.error('Invalid API key');
  }
}

Authentication Errors

CodeHTTP StatusDescription
UNAUTHORIZED401API key is missing or invalid
INVALID_API_KEY401API key format is incorrect
EXPIRED_API_KEY401API key has expired
REVOKED_API_KEY401API key has been revoked
FORBIDDEN403API key lacks required permissions

Handling Auth Errors

import { Transactional, AuthenticationError } from 'transactional-sdk';
 
try {
  await client.emails.send(...);
} catch (error) {
  if (error instanceof AuthenticationError) {
    switch (error.code) {
      case 'UNAUTHORIZED':
        console.error('Check your API key configuration');
        break;
      case 'FORBIDDEN':
        console.error('This key lacks the required permissions');
        console.error(`Required: ${error.details?.requiredPermission}`);
        break;
      case 'REVOKED_API_KEY':
        console.error('This key has been revoked. Generate a new one.');
        break;
    }
  }
}

Multi-Organization Access

For applications managing multiple Transactional organizations, you can use organization-scoped keys or include the organization ID in requests:

curl https://api.usetransactional.com/v1/emails \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "X-Organization-Id: org_abc123"
const client = new Transactional({
  apiKey: process.env.TRANSACTIONAL_API_KEY,
  organizationId: 'org_abc123', // Override default organization
});

Next Steps