Examples
Create domain and email aliases
Complete Management API workflow for creating a domain alias and an email alias.
This example uses the Management API to add aliases to an existing active Hosting domain.
Workflow and API calls
| Step | API call | What happens |
|---|---|---|
| 1 | POST /api/domains/:code/aliases | Create a domain alias for the active domain. |
| 2 | Manual DNS change | Publish the ownership DNS record for the alias domain. |
| 3 | PUT /api/domains/:code/aliases/:alias_domain_code/dns_ownership_check | Verify alias-domain ownership. |
| 4 | GET /api/domains/:code/aliases/:alias_domain_code | Poll until the alias domain is active. |
| 5 | POST /api/domains/:code/alias_email_accounts | Create an email alias that forwards to one or more destinations. |
| 6 | GET /api/domains/:code/alias_email_accounts/:alias_email_account_code | Poll until the email alias is active. |
Before you start
- Use a production Management API token in the
X-Api-Tokenheader. - The main domain must already be active.
- You need access to the alias domain DNS zone.
- Email alias destinations should be real addresses that can receive mail.
Full TypeScript example
import { stdin as input, stdout as output } from "node:process";
import { createInterface } from "node:readline/promises";
type Json = Record<string, unknown>;
type CreatedResource = {
message: string;
resource_code: string;
};
type ResourcesResponse<T> = {
resources: T[];
};
type StatusResource = {
status: string;
status_detail: string;
};
type DomainAlias = StatusResource & {
code: string;
name: string;
ownership_verification_random_code?: string;
possession_a_record?: string;
};
type EmailAlias = StatusResource & {
code: string;
email_address: string;
destinations: string[];
};
const API_BASE = (process.env.QBOXMAIL_API_BASE ?? "https://api.qboxmail.com/api").replace(/\/$/, "");
const API_TOKEN = requiredEnv("QBOXMAIL_API_TOKEN");
const DOMAIN_CODE = requiredEnv("DOMAIN_CODE");
const ALIAS_DOMAIN_NAME = requiredEnv("ALIAS_DOMAIN_NAME");
const ALIAS_EMAIL_NAME = process.env.ALIAS_EMAIL_NAME ?? "info";
const ALIAS_DESTINATIONS = requiredEnv("ALIAS_DESTINATIONS").split(",").map((value) => value.trim());
function requiredEnv(name: string): string {
const value = process.env[name];
if (!value) throw new Error(`Missing env var: ${name}`);
return value;
}
function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function api<T>(path: string, options: { method?: string; body?: Json } = {}): Promise<T> {
const method = options.method ?? "GET";
const response = await fetch(`${API_BASE}${path}`, {
method,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"X-Api-Token": API_TOKEN,
},
body: options.body === undefined ? undefined : JSON.stringify(options.body),
});
if (response.status === 204) return undefined as T;
const payload = await response.json();
if (!response.ok) {
throw new Error(`${method} ${path} failed: ${response.status} ${JSON.stringify(payload)}`);
}
return payload as T;
}
function firstResource<T>(response: ResourcesResponse<T>): T {
const resource = response.resources[0];
if (!resource) throw new Error("API returned no resources");
return resource;
}
async function waitFor<T extends StatusResource>(label: string, load: () => Promise<T>) {
const deadline = Date.now() + 30 * 60_000;
while (Date.now() < deadline) {
const resource = await load();
if (resource.status === "enabled" && resource.status_detail === "ok") return resource;
console.log(`${label} not ready yet: ${resource.status}/${resource.status_detail}`);
await sleep(15_000);
}
throw new Error(`Timed out waiting for ${label}`);
}
async function waitForEnter(message: string) {
const rl = createInterface({ input, output });
await rl.question(message);
rl.close();
}
async function createDomainAlias(): Promise<string> {
const created = await api<CreatedResource>(`/domains/${encodeURIComponent(DOMAIN_CODE)}/aliases`, {
method: "POST",
body: { name: ALIAS_DOMAIN_NAME },
});
console.log(`Domain alias created: ${ALIAS_DOMAIN_NAME} (${created.resource_code})`);
return created.resource_code;
}
async function getDomainAlias(aliasDomainCode: string): Promise<DomainAlias> {
return firstResource(
await api<ResourcesResponse<DomainAlias>>(
`/domains/${encodeURIComponent(DOMAIN_CODE)}/aliases/${encodeURIComponent(aliasDomainCode)}`,
),
);
}
async function verifyDomainAlias(aliasDomainCode: string): Promise<void> {
await api<void>(
`/domains/${encodeURIComponent(DOMAIN_CODE)}/aliases/${encodeURIComponent(aliasDomainCode)}/dns_ownership_check`,
{ method: "PUT" },
);
}
async function createEmailAlias(): Promise<string> {
const created = await api<CreatedResource>(`/domains/${encodeURIComponent(DOMAIN_CODE)}/alias_email_accounts`, {
method: "POST",
body: {
name: ALIAS_EMAIL_NAME,
destinations: ALIAS_DESTINATIONS,
},
});
console.log(`Email alias created: ${created.resource_code}`);
return created.resource_code;
}
async function getEmailAlias(aliasEmailCode: string): Promise<EmailAlias> {
return firstResource(
await api<ResourcesResponse<EmailAlias>>(
`/domains/${encodeURIComponent(DOMAIN_CODE)}/alias_email_accounts/${encodeURIComponent(aliasEmailCode)}`,
),
);
}
async function main() {
const aliasDomainCode = await createDomainAlias();
const aliasDomain = await getDomainAlias(aliasDomainCode);
console.log("Publish the DNS ownership record requested by Qboxmail before continuing.");
console.log({
aliasDomain: aliasDomain.name,
ownershipCode: aliasDomain.ownership_verification_random_code,
expectedARecord: aliasDomain.possession_a_record,
});
await waitForEnter("Press Enter after the DNS ownership record resolves...");
await verifyDomainAlias(aliasDomainCode);
await waitFor("domain alias activation", () => getDomainAlias(aliasDomainCode));
const aliasEmailCode = await createEmailAlias();
const emailAlias = await waitFor("email alias activation", () => getEmailAlias(aliasEmailCode));
console.log({
aliasDomainCode,
aliasEmailCode,
emailAddress: emailAlias.email_address,
destinations: emailAlias.destinations,
});
}
main().catch((error) => {
console.error(error);
process.exit(1);
});Run it
Save the code above as create-domain-and-email-aliases.ts, then run:
export QBOXMAIL_API_TOKEN="<MANAGEMENT_API_TOKEN>"
export DOMAIN_CODE="D123456789"
export ALIAS_DOMAIN_NAME="example.net"
export ALIAS_EMAIL_NAME="info"
export ALIAS_DESTINATIONS="user@example.com,external@example.net"
npx tsx create-domain-and-email-aliases.ts