Skip to content

Authorization

The Octeth API supports two authentication methods and two permission scopes.

Authentication Methods

Permanent authentication for automated integrations.

bash
curl https://your-domain.com/api.php \
  -F "ResponseFormat=JSON" \
  -F "Command=List.Create" \
  -F "APIKey=your-api-key" \
  -F "SubscriberListName=Newsletter"

Get your API key:

  • User API Key: User Dashboard → Settings → API Keys
  • Admin API Key: Admin Area → Settings → Account → API tab

Session ID

Temporary authentication for interactive applications.

bash
# User login
curl https://your-domain.com/api.php \
  -F "ResponseFormat=JSON" \
  -F "Command=User.Login" \
  -F "Username=user@example.com" \
  -F "Password=password"

# Returns: {"SessionID": "abc123..."}

# Use session in subsequent calls
curl https://your-domain.com/api.php \
  -F "ResponseFormat=JSON" \
  -F "Command=List.Create" \
  -F "SessionID=abc123..." \
  -F "SubscriberListName=Newsletter"

Permission Scopes

User Scope

Access to marketing operations.

ParameterMethodAccess
APIKeyAPI KeyLists, campaigns, subscribers, emails
SessionIDSessionSame as API Key

Login options:

bash
# With username/password
curl https://your-domain.com/api.php \
  -F "ResponseFormat=JSON" \
  -F "Command=User.Login" \
  -F "Username=user@example.com" \
  -F "Password=password"

# With API key (skip password)
curl https://your-domain.com/api.php \
  -F "ResponseFormat=JSON" \
  -F "Command=User.Login" \
  -F "apikey=your-api-key"

# With 2FA
curl https://your-domain.com/api.php \
  -F "ResponseFormat=JSON" \
  -F "Command=User.Login" \
  -F "Username=user@example.com" \
  -F "Password=password" \
  -F "tfacode=123456"

Admin Scope

Access to system administration.

ParameterMethodAccess
AdminAPIKeyAPI KeyUsers, system settings, all user data
SessionIDSessionSame as API Key

Login options:

bash
# With username/password
curl https://your-domain.com/api.php \
  -F "ResponseFormat=JSON" \
  -F "Command=Admin.Login" \
  -F "Username=admin" \
  -F "Password=admin-password"

# With admin API key (if ADMIN_API_KEY configured)
curl https://your-domain.com/api.php \
  -F "ResponseFormat=JSON" \
  -F "Command=Admin.Login" \
  -F "adminapikey=admin-api-key"

Sub-admin API keys and privilege enforcement

Sub-admin accounts (Settings, Sub Admin Accounts) carry a privilege list, the same one that decides which screens they can open in the admin area. Two things follow for the API:

  • Each sub-admin can hold its own API key. It is issued, regenerated or revoked on the sub-admin edit screen and is passed as AdminAPIKey exactly like the master key. The master ADMIN_API_KEY from .oempro_env keeps its meaning: it always authenticates as the unrestricted master administrator. A wrong key of either kind returns 99998.
  • Every admin command declares the privilege it needs. When ADMIN_API_ENFORCE_PRIVILEGES=true in .oempro_env, a call made with a sub-admin key, a sub-admin SessionID, or a sub-admin username and password is checked against that list and answers 99999 (Not enough privileges) when the account lacks it. The master key and any admin account without restricted access are never affected. When the setting is off or absent, sub-admins are not restricted over the API (the behaviour of every release before v5.9.6). Fresh installs ship with it on; upgraded installs keep it off until an operator turns it on.

The privilege a command needs mirrors the screen that owns it: for example Settings.Update needs Settings, DeliveryServer.Update needs DeliveryServers, UserGroup.Create needs UserGroups, Users.Delete needs User.Delete and User.Switch needs User.Impersonate. Commands contributed by plugins need PluginAccess.

The response of Admin.Login never includes the sub-admin API key.

Choosing the credential with Access

Most commands accept exactly one kind of credential, and Access is ignored on them. Some commands are registered for two kinds, and for those the registry lists the kinds in order: the first one is the default, and Access selects the other.

  • Access=admin authenticates with AdminAPIKey (or an admin SessionID) and runs the command's admin path.
  • Access=user authenticates with APIKey (or a user SessionID) and runs the command's user path.
  • The value is compared exactly. Admin, USER or any other spelling makes authentication fail with 99998 even when the credential is valid.
  • When a request carries both a user credential and AdminAPIKey without Access, the default kind is used and the other credential is ignored. Pass Access whenever you send both.
  • Access=subscriber exists only for subscriber.get, subscriber.get.activity and subscriber.update when called from a logged-in subscriber area session; it is not a third API credential.

Commands that default to admin (pass Access=user to run them as a user):

user.update, user.snapshot, suppression.import, smssuppression.add, smssuppression.patterns.add, email.template.create, email.templates.get, email.template.get, email.template.update, email.template.delete, email.template.thumbnail.upload, email.smtp.render, campaign.recipients.get, campaign.linkclicks.get, campaign.recipients.activity.get, campaign.abtest.get, media.delete

Commands that default to user (pass Access=admin together with UserID to run them as an admin, see the next section):

campaigns.get, lists.get, lists.stats, list.getactivityseries, segments.get, segments.rulevocabulary.get, subscribers.get, media.upload, emailgateway.getdomains, user.senderdomain.list, smssuppression.browse, smssuppression.stats, smssuppression.delete, smssuppression.patterns.browse, smssuppression.patterns.delete

What the admin path does differs per command and is described on each command's page: suppression.import writes install-wide rows under admin auth, user.update unlocks the admin-only fields, the campaign.* reports and email.template.* commands read or write across accounts, and the user-default commands above resolve UserID and then run as that user.

Admin access to user-owned objects

Some user commands also accept admin authentication so an admin UI can read another account's data without impersonating it: lists.get, campaigns.get, segments.get, emailgateway.getdomains, user.senderdomain.list, lists.stats, list.getactivityseries, subscribers.get and media.upload (v5.9.6, #2775). Pass Access=admin together with UserID; the handler then runs exactly as it would for that user's own key. Without Access=admin the call is a user call, so nothing changes for existing integrations. These calls need the User.Edit privilege when privilege enforcement is on, and a restricted sub-admin can only name accounts inside its allowed user groups. Error codes reserved for this path: 5001 (UserID missing or invalid), 5002 (user not found), 5003 (user outside the admin's allowed groups).

Two-Factor Authentication

When 2FA is enabled, include the verification code:

bash
# User login with 2FA
curl https://your-domain.com/api.php \
  -F "ResponseFormat=JSON" \
  -F "Command=User.Login" \
  -F "Username=user@example.com" \
  -F "Password=password" \
  -F "tfacode=123456"

# Using recovery code (disables 2FA)
curl https://your-domain.com/api.php \
  -F "ResponseFormat=JSON" \
  -F "Command=User.Login" \
  -F "Username=user@example.com" \
  -F "Password=password" \
  -F "tfarecoverycode=XXXX-XXXX-XXXX"

Response Format

Successful login

json
{
  "Success": true,
  "ErrorCode": 0,
  "SessionID": "abc123...",
  "UserInfo": {
    "UserID": 42,
    "Username": "user@example.com",
    "EmailAddress": "user@example.com",
    "FirstName": "John",
    "LastName": "Doe",
    "GroupInformation": {...}
  }
}

Failed login

json
{
  "Success": false,
  "ErrorCode": 3,
  "ErrorText": "Invalid credentials"
}

Common Error Codes

CodeDescription
1Missing username
2Missing password
3Invalid credentials
6Invalid 2FA code
1012FA required

Parameter Reference

Login Parameters

All login parameters are lowercase:

ParameterDescriptionExample
usernameAccount username or emailuser@example.com
passwordAccount passwordmypassword
apikeyUser API key (skip password)XXXX-XXXX-XXXX
adminapikeyAdmin API keyXXXX-XXXX-XXXX
tfacode2FA verification code123456
tfarecoverycode2FA recovery codeXXXX-XXXX-XXXX

API Call Parameters

Authentication parameters for API calls are PascalCase:

ParameterScopeDescription
APIKeyUserUser API key
AdminAPIKeyAdminAdmin API key: the master ADMIN_API_KEY or a sub-admin's own key
SessionIDBothSession from login

Best Practices

Use API Keys for automation

php
// Good: API key for automated tasks
$api->call('Campaign.Send', [
    'APIKey' => getenv('OCTETH_API_KEY'),
    'CampaignID' => 123
]);

Use Sessions for user interfaces

javascript
// Good: Session for web apps
const session = await login(username, password);
localStorage.setItem('sessionId', session.SessionID);

// Use session for subsequent calls
await api.call('Lists.Get', {
    SessionID: localStorage.getItem('sessionId')
});

Handle session expiration

python
def api_call(command, data):
    response = make_request(command, data)
    
    if response['ErrorCode'] == 401:  # Session expired
        # Re-authenticate
        session = login()
        data['SessionID'] = session['SessionID']
        response = make_request(command, data)
    
    return response

Secure your credentials

bash
# Store API keys in environment variables
export OCTETH_API_KEY="your-api-key"
export OCTETH_ADMIN_KEY="your-admin-key"

# Use in scripts
curl https://your-domain.com/api.php \
  -F "APIKey=$OCTETH_API_KEY" \
  ...

Testing Your Setup

Quick test to verify authentication:

bash
# Test with API key
curl https://your-domain.com/api.php \
  -F "ResponseFormat=JSON" \
  -F "Command=Lists.Get" \
  -F "APIKey=your-api-key"

# Test with session
curl https://your-domain.com/api.php \
  -F "ResponseFormat=JSON" \
  -F "Command=Lists.Get" \
  -F "SessionID=your-session-id"

Success response confirms authentication is working:

json
{
  "Success": true,
  "ErrorCode": 0,
  "TotalLists": 5,
  "Lists": [...]
}

Next Steps

Any questions? Contact us.