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

StepAPI callWhat happens
1POST /api/domains/:code/aliasesCreate a domain alias for the active domain.
2Manual DNS changePublish the ownership DNS record for the alias domain.
3PUT /api/domains/:code/aliases/:alias_domain_code/dns_ownership_checkVerify alias-domain ownership.
4GET /api/domains/:code/aliases/:alias_domain_codePoll until the alias domain is active.
5POST /api/domains/:code/alias_email_accountsCreate an email alias that forwards to one or more destinations.
6GET /api/domains/:code/alias_email_accounts/:alias_email_account_codePoll until the email alias is active.

Before you start

  • Use a production Management API token in the X-Api-Token header.
  • 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

On this page