Overview
Sub-accounts let you create isolated child accounts under your parent account. Each sub-account gets its own API keys, domains, and email sending quota drawn from a shared pool. This is useful for SaaS platforms that send email on behalf of their customers, agencies managing multiple clients, or any multi-tenant setup where you need separation between senders.
Sub-accounts inherit the parent's plan tier and cannot create their own sub-accounts (maximum depth is one level).
Requirements
- Plan: Business or Enterprise (sub-accounts are not available on the Free plan)
- API key scope:
sub_accounts:manage - Limits: Business plan allows up to 5 sub-accounts. Enterprise plan has no limit.
API Reference
All sub-account endpoints require authentication via the X-EuroMail-Api-Key header with the sub_accounts:manage scope.
Create Sub-Account
curl -X POST https://api.euromail.dev/v1/accounts \
-H "X-EuroMail-Api-Key: em_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Client A",
"email": "[email protected]",
"password": "securepassword123",
"monthly_quota": 5000
}'| Field | Required | Description |
|---|---|---|
name | Yes | Display name, 1-255 characters |
email | Yes | Unique email address for the sub-account |
password | Yes | Minimum 8 characters |
monthly_quota | Yes | Monthly sending quota, must be > 0 |
Response (201 Created):
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Client A",
"email": "[email protected]",
"plan": "business",
"monthly_quota": 5000,
"emails_sent_this_month": 0,
"is_active": true,
"parent_account_id": "123e4567-e89b-12d3-a456-426614174000",
"created_at": "2026-03-21T10:00:00Z"
}
}List Sub-Accounts
curl https://api.euromail.dev/v1/accounts?page=1&per_page=25 \
-H "X-EuroMail-Api-Key: em_live_..."| Parameter | Default | Description |
|---|---|---|
page | 1 | Page number (minimum 1) |
per_page | 25 | Results per page (1-100) |
Response (200 OK):
{
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Client A",
"email": "[email protected]",
"plan": "business",
"monthly_quota": 5000,
"emails_sent_this_month": 1243,
"is_active": true,
"parent_account_id": "123e4567-e89b-12d3-a456-426614174000",
"created_at": "2026-03-21T10:00:00Z"
}
],
"pagination": {
"page": 1,
"per_page": 25,
"total": 3,
"total_pages": 1
}
}
Results are ordered by created_at descending. Parent accounts can only see their own sub-accounts.
Get Sub-Account
curl https://api.euromail.dev/v1/accounts/{id} \
-H "X-EuroMail-Api-Key: em_live_..."
Returns the full sub-account details including updated_at. Returns 404 if the sub-account does not exist or belongs to a different parent.
Update Sub-Account
curl -X PATCH https://api.euromail.dev/v1/accounts/{id} \
-H "X-EuroMail-Api-Key: em_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Client A (Updated)",
"monthly_quota": 10000,
"is_active": false
}'| Field | Description |
|---|---|
name | New display name (1-255 characters) |
monthly_quota | New quota (must be > 0, validated against available pool) |
is_active | Enable or disable the sub-account |
All fields are optional. Email and plan cannot be changed.
Delete Sub-Account
curl -X DELETE https://api.euromail.dev/v1/accounts/{id} \
-H "X-EuroMail-Api-Key: em_live_..."
Returns 204 No Content. Deleting a sub-account cascades to its API keys, domains, and emails. The sub-account's allocated quota is returned to the parent's pool.
Sub-Account Analytics
curl "https://api.euromail.dev/v1/accounts/{id}/analytics?period=30d" \
-H "X-EuroMail-Api-Key: em_live_..."| Parameter | Values | Default | Description |
|---|---|---|---|
period | 7d, 30d, 90d | 30d | Time window |
Response (200 OK):
{
"data": {
"total_sent": 4521,
"total_delivered": 4480,
"total_bounced": 41,
"total_opens": 2105,
"total_clicks": 312,
"total_unsubscribes": 8,
"delivery_rate_pct": 99.09,
"bounce_rate_pct": 0.91,
"open_rate_pct": 46.56,
"click_rate_pct": 6.9
},
"period": {
"days": 30
}
}Create API Key for Sub-Account
curl -X POST https://api.euromail.dev/v1/accounts/{id}/api-keys \
-H "X-EuroMail-Api-Key: em_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Client A Production Key",
"scopes": ["emails:send", "emails:read"]
}'| Field | Required | Description |
|---|---|---|
name | Yes | Key name, 1-255 characters |
scopes | No | Array of scopes (empty = full access) |
Available scopes: emails:send, emails:read, emails:validate, domains:manage, templates:manage, webhooks:manage, contacts:manage, account:admin, sub_accounts:manage.
Response (201 Created):
{
"data": {
"id": "660e8400-e29b-41d4-a716-446655440000",
"name": "Client A Production Key",
"key": "em_prod_abc123...",
"key_prefix": "em_prod_abc1",
"scopes": ["emails:send", "emails:read"],
"is_active": true,
"created_at": "2026-03-21T10:05:00Z"
}
}
The full key value is only returned once at creation time. Store it securely.
Aggregate Analytics
View combined analytics across the parent account and all sub-accounts:
curl "https://api.euromail.dev/v1/analytics/aggregate?period=30d" \
-H "X-EuroMail-Api-Key: em_live_..."
Only root accounts can access this endpoint. Sub-accounts receive 403 Forbidden.
Quota Management
Sub-accounts draw from a shared quota pool. The pool is the parent account's monthly_quota minus the total allocated to all sub-accounts.
Example: If your account has a monthly quota of 50,000 and you create two sub-accounts with 10,000 each, you have 30,000 remaining in the pool for new sub-accounts or your own sending.
When you create or update a sub-account, EuroMail validates that the requested quota does not exceed the available pool. Quota changes use row-level locking to prevent race conditions during concurrent updates.
When a sub-account is deleted, its allocated quota is automatically returned to the pool.
Error Responses
| Status | Cause |
|---|---|
400 Bad Request | Invalid name, email, password, or quota value |
403 Forbidden | Plan does not support sub-accounts, or a sub-account is trying to manage other sub-accounts |
404 Not Found | Sub-account does not exist or belongs to a different parent |
409 Conflict | Email address already in use |
422 Unprocessable Entity | Insufficient quota pool |