Tenants represent your customers in Ark’s white-label architecture. Each tenant gets isolated domains, credentials, webhooks, suppressions, and usage tracking.
Architecture Overview Understand the Platform → Tenant → Domain → Email hierarchy before diving into management operations
Create a Tenant
Python
Node.js
Ruby
Go
cURL
from ark import Ark
client = Ark()
tenant = client.tenants.create(
name = "Acme Corp" ,
metadata = { "plan" : "enterprise" , "customer_id" : "cust_123" }
)
print ( f "Tenant ID: { tenant.data.id } " )
print ( f "Name: { tenant.data.name } " )
import Ark from 'ark-email' ;
const client = new Ark ();
const tenant = await client . tenants . create ({
name: 'Acme Corp' ,
metadata: { plan: 'enterprise' , customer_id: 'cust_123' },
});
console . log ( `Tenant ID: ${ tenant . data . id } ` );
require "ark_email"
client = ArkEmail :: Client . new
tenant = client. tenants . create (
name: "Acme Corp" ,
metadata: { plan: "enterprise" , customer_id: "cust_123" }
)
puts "Tenant ID: #{ tenant. data . id } "
client := ark . NewClient ()
tenant , err := client . Tenants . Create ( ctx , ark . TenantCreateParams {
Name : ark . String ( "Acme Corp" ),
Metadata : map [ string ] string { "plan" : "enterprise" , "customer_id" : "cust_123" },
})
fmt . Printf ( "Tenant ID: %s \n " , tenant . Data . ID )
curl -X POST https://api.arkhq.io/v1/tenants \
-H "Authorization: Bearer $ARK_API_KEY " \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corp",
"metadata": {"plan": "enterprise", "customer_id": "cust_123"}
}'
Use metadata for correlation. Store your internal customer ID, plan level, or other identifiers in the metadata field. This makes it easy to map Ark tenants back to your own database.
List Tenants
Python
Node.js
Ruby
Go
cURL
tenants = client.tenants.list()
for tenant in tenants.data:
print ( f " { tenant.id } : { tenant.name } " )
const tenants = await client . tenants . list ();
for ( const tenant of tenants . data ) {
console . log ( ` ${ tenant . id } : ${ tenant . name } ` );
}
tenants = client. tenants . list
tenants. data . each do | tenant |
puts " #{ tenant. id } : #{ tenant. name } "
end
tenants , err := client . Tenants . List ( ctx )
for _ , tenant := range tenants . Data {
fmt . Printf ( " %s : %s \n " , tenant . ID , tenant . Name )
}
curl https://api.arkhq.io/v1/tenants \
-H "Authorization: Bearer $ARK_API_KEY "
Update a Tenant
Python
Node.js
Ruby
Go
cURL
tenant = client.tenants.update(
"tenant_abc123" ,
name = "Acme Corp (Renamed)" ,
metadata = { "plan" : "scale" , "customer_id" : "cust_123" }
)
const tenant = await client . tenants . update ( 'tenant_abc123' , {
name: 'Acme Corp (Renamed)' ,
metadata: { plan: 'scale' , customer_id: 'cust_123' },
});
tenant = client. tenants . update (
"tenant_abc123" ,
name: "Acme Corp (Renamed)" ,
metadata: { plan: "scale" , customer_id: "cust_123" }
)
tenant , err := client . Tenants . Update ( ctx , "tenant_abc123" , ark . TenantUpdateParams {
Name : ark . String ( "Acme Corp (Renamed)" ),
Metadata : map [ string ] string { "plan" : "scale" , "customer_id" : "cust_123" },
})
curl -X PATCH https://api.arkhq.io/v1/tenants/tenant_abc123 \
-H "Authorization: Bearer $ARK_API_KEY " \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corp (Renamed)",
"metadata": {"plan": "scale", "customer_id": "cust_123"}
}'
Delete a Tenant
Deleting a tenant removes all associated domains, credentials, webhooks, and suppressions. This action cannot be undone.
Python
Node.js
Ruby
Go
cURL
client.tenants.delete( "tenant_abc123" )
await client . tenants . delete ( 'tenant_abc123' );
client. tenants . delete ( "tenant_abc123" )
err := client . Tenants . Delete ( ctx , "tenant_abc123" )
curl -X DELETE https://api.arkhq.io/v1/tenants/tenant_abc123 \
-H "Authorization: Bearer $ARK_API_KEY "
Per-Tenant Credentials
Each tenant can have its own API keys and SMTP credentials. This lets your customers authenticate directly, or lets you scope access per-tenant in your backend.
Create Credentials
credential = client.tenants.credentials.create(
"tenant_abc123" ,
name = "Production API Key"
)
print ( f "API Key: { credential.data.key } " )
print ( f "SMTP Username: { credential.data.smtp_username } " )
const credential = await client . tenants . credentials . create ( 'tenant_abc123' , {
name: 'Production API Key' ,
});
console . log ( `API Key: ${ credential . data . key } ` );
console . log ( `SMTP Username: ${ credential . data . smtpUsername } ` );
curl -X POST https://api.arkhq.io/v1/tenants/tenant_abc123/credentials \
-H "Authorization: Bearer $ARK_API_KEY " \
-H "Content-Type: application/json" \
-d '{"name": "Production API Key"}'
The credential key is only returned once at creation time. Store it securely — you cannot retrieve it again.
Per-Tenant Webhooks
Configure webhook endpoints scoped to a specific tenant. Only events for that tenant will be delivered.
webhook = client.tenants.webhooks.create(
"tenant_abc123" ,
name = "Acme Webhook" ,
url = "https://acme.com/webhooks/email" ,
events = [ "MessageSent" , "MessageBounced" , "MessageDeliveryFailed" ]
)
const webhook = await client . tenants . webhooks . create ( 'tenant_abc123' , {
name: 'Acme Webhook' ,
url: 'https://acme.com/webhooks/email' ,
events: [ 'MessageSent' , 'MessageBounced' , 'MessageDeliveryFailed' ],
});
curl -X POST https://api.arkhq.io/v1/tenants/tenant_abc123/webhooks \
-H "Authorization: Bearer $ARK_API_KEY " \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Webhook",
"url": "https://acme.com/webhooks/email",
"events": ["MessageSent", "MessageBounced", "MessageDeliveryFailed"]
}'
Per-Tenant Suppressions
Suppressions are tenant-scoped. A bounce on Tenant A’s domain doesn’t affect Tenant B’s sending.
# List suppressions for a tenant
suppressions = client.tenants.suppressions.list( "tenant_abc123" )
for suppression in suppressions.data:
print ( f " { suppression.email } : { suppression.reason } " )
# Manually suppress an address
client.tenants.suppressions.create(
"tenant_abc123" ,
email = "bounced@example.com"
)
# Remove a suppression
client.tenants.suppressions.delete( "tenant_abc123" , "bounced@example.com" )
// List suppressions for a tenant
const suppressions = await client . tenants . suppressions . list ( 'tenant_abc123' );
for ( const s of suppressions . data ) {
console . log ( ` ${ s . email } : ${ s . reason } ` );
}
// Manually suppress an address
await client . tenants . suppressions . create ( 'tenant_abc123' , {
email: 'bounced@example.com' ,
});
// Remove a suppression
await client . tenants . suppressions . delete ( 'tenant_abc123' , 'bounced@example.com' );
# List suppressions
curl https://api.arkhq.io/v1/tenants/tenant_abc123/suppressions \
-H "Authorization: Bearer $ARK_API_KEY "
# Add suppression
curl -X POST https://api.arkhq.io/v1/tenants/tenant_abc123/suppressions \
-H "Authorization: Bearer $ARK_API_KEY " \
-H "Content-Type: application/json" \
-d '{"email": "bounced@example.com"}'
# Remove suppression
curl -X DELETE https://api.arkhq.io/v1/tenants/tenant_abc123/suppressions/bounced@example.com \
-H "Authorization: Bearer $ARK_API_KEY "
Per-Tenant Usage
Track sending volume, bounces, and engagement per tenant:
usage = client.tenants.usage.retrieve( "tenant_abc123" )
print ( f "Emails sent: { usage.data.emails_sent } " )
print ( f "Bounces: { usage.data.bounces } " )
# Timeseries data
timeseries = client.tenants.usage.timeseries( "tenant_abc123" )
for point in timeseries.data:
print ( f " { point.date } : { point.emails_sent } sent" )
const usage = await client . tenants . usage . retrieve ( 'tenant_abc123' );
console . log ( `Emails sent: ${ usage . data . emailsSent } ` );
// Timeseries data
const timeseries = await client . tenants . usage . timeseries ( 'tenant_abc123' );
for ( const point of timeseries . data ) {
console . log ( ` ${ point . date } : ${ point . emailsSent } sent` );
}
# Usage summary
curl https://api.arkhq.io/v1/tenants/tenant_abc123/usage \
-H "Authorization: Bearer $ARK_API_KEY "
# Usage timeseries
curl https://api.arkhq.io/v1/tenants/tenant_abc123/usage/timeseries \
-H "Authorization: Bearer $ARK_API_KEY "
Common Patterns
Provisioning on Customer Signup
When a new customer signs up for your platform, create a tenant and add their domain:
from ark import Ark
client = Ark()
def onboard_customer ( customer_name , customer_domain , customer_id ):
# 1. Create tenant
tenant = client.tenants.create(
name = customer_name,
metadata = { "customer_id" : customer_id}
)
# 2. Add their domain
domain = client.tenants.domains.create(
tenant.data.id,
name = customer_domain
)
# 3. Create credentials for them
credential = client.tenants.credentials.create(
tenant.data.id,
name = f " { customer_name } API Key"
)
return {
"tenant_id" : tenant.data.id,
"domain_id" : domain.data.id,
"dns_records" : domain.data.dns_records,
"api_key" : credential.data.key
}
Domain Onboarding Build complete domain setup flows for your customers, including DNS verification and error handling
Multi-Tenant Usage Export
Export usage across all tenants for billing reconciliation:
# Export all tenant usage as CSV
curl "https://api.arkhq.io/v1/usage/export?format=csv" \
-H "Authorization: Bearer $ARK_API_KEY " \
-o usage-export.csv
# Compare tenant usage
curl https://api.arkhq.io/v1/usage/tenants \
-H "Authorization: Bearer $ARK_API_KEY "
Next Steps