Branching Tenants and Managing Aliases

Create stable tenant aliases, copy configuration into branch tenants, and move an alias to a new branch.

Use a tenant alias when clients need a stable name, such as twisp-dev, while the tenant behind that name changes over time. Use a branch when you need a new, isolated tenant containing configuration copied from an existing tenant.

Together, branching and aliases support a workflow like this:

  1. Create an alias for the current development tenant.
  2. Branch the tenant's configuration into a new tenant.
  3. Validate the branch using its generated accountId.
  4. Repoint the alias when the branch is ready.

Aliases are unique per region. An alias can point only to a tenant in its owning organization, and only that organization can update or delete it.

Before you begin

Send these mutations to the GraphQL endpoint in the region containing the source tenant:

https://api.<region>.cloud.twisp.com/financial/v1/graphql

Authenticate the request and identify the source tenant with an explicit account ID:

authorization: Bearer <JWT>
content-type: application/json
x-twisp-account-id: <source-account-id>

Using a literal value in x-twisp-account-id for administrative requests keeps the source tenant explicit while an alias is being moved. For ordinary requests, clients can put a qualified alias in the same header, such as x-twisp-account-id: alias/twisp-dev.

Alias fields in the GraphQL API use the raw alias name, such as twisp-dev. When an alias is supplied through x-twisp-account-id, qualify it as alias/twisp-dev so the server can distinguish it from a literal account ID. The alias/ prefix is not stored and is not returned by alias queries or mutations.

InputValueBehavior
GraphQL alias fieldtwisp-devCreates, updates, or identifies the raw alias name
x-twisp-account-idtwisp-devUses twisp-dev as a literal account ID
x-twisp-account-idalias/twisp-devResolves the alias twisp-dev

The caller needs the following permissions:

  • Creating an alias requires db:Insert on financial.aliases.
  • Branching requires db:Insert on financial.tenants and permission to select and insert each configuration class being copied.

1. Create an alias

Create the alias in the same region as its target tenant. The target accountId must belong to the caller's organization.

Alias names and tenant account IDs follow the S3 bucket naming rules:

  • 3 to 63 characters.
  • Lowercase letters, numbers, periods (.), and hyphens (-) only.
  • Begins and ends with a letter or a number.
  • No two adjacent periods.
  • Not formatted as an IP address, such as 192.168.5.4.

A tenant account ID has one further restriction: it cannot begin with alias. This keeps an account ID from being read as the qualified form that x-twisp-account-id accepts. An alias name may begin with alias.

mutation CreateAlias($alias: String!, $accountId: String!, $ttlSeconds: Int = 60) {
  admin {
    createAlias(
      input: {
        alias: $alias
        accountId: $accountId
        ttlSeconds: $ttlSeconds
      }
    ) {
      alias
      accountId
      organizationId
      region
      ttlSeconds
    }
  }
}
{
  "alias": "twisp-dev",
  "accountId": "4b3aef43-e770-4a51-aa8f-f73421f7045e",
  "ttlSeconds": 60
}

The mutation returns twisp-dev in its alias field. To address the tenant, qualify that name in the account ID header:

curl 'https://api.<region>.cloud.twisp.com/financial/v1/graphql' \
  -H 'authorization: Bearer <JWT>' \
  -H 'content-type: application/json' \
  -H 'x-twisp-account-id: alias/twisp-dev' \
  --data-raw '{"query":"query CurrentOrganization { admin { organization { id name } } }"}'

2. Point an alias at a different tenant

Use updateAlias to move an existing alias to another tenant in the same organization. The alias name and owning organization do not change.

mutation RepointAlias($alias: String!, $accountId: String!, $ttlSeconds: Int = 60) {
  admin {
    updateAlias(
      input: {
        alias: $alias
        accountId: $accountId
        ttlSeconds: $ttlSeconds
      }
    ) {
      alias
      accountId
      region
      ttlSeconds
    }
  }
}
{
  "alias": "twisp-dev",
  "accountId": "066dbb42-42d9-4605-a010-428b46c766c7",
  "ttlSeconds": 60
}

Alias resolutions are cached. Updating ttlSeconds controls future cache entries; it does not invalidate an old mapping that a server has already cached. For a planned cutover, first lower the alias TTL, wait at least the previous TTL, and then repoint the alias. Raise the TTL again after the cutover is visible.

3. Branch a tenant

branch generates a new tenant accountId and copies configuration from the tenant identified by the request header.

mutation BranchTenant {
  admin {
    branch(input: {}) {
      id
      accountId
      organizationId
      name
      description
    }
  }
}

An empty BranchInput copies all supported configuration classes:

  • calculations
  • custom indexes
  • endpoints
  • non-default journals
  • tran codes
  • velocity controls and their configuration memberships
  • views and their backing table definitions
  • customer-defined clients

Accounts, balances, entries, and transactions are not copied. Tenant creation supplies the new tenant's default journal and Twisp-managed clients.

To copy only selected configuration, provide include:

mutation BranchSelectedConfiguration {
  admin {
    branch(input: { include: [Journal, TranCode, Calculation] }) {
      accountId
    }
  }
}

The available values are Calculation, CustomIndex, Endpoint, Journal, TranCode, VelocityControl, and View. Include View when copying a custom index defined on a view.

The branch operation is synchronous. Large configurations may take longer than a typical unary request deadline.

4. Branch and point an alias to the new branch

Twisp's @export directive can capture the generated branch accountId and pass it to a later field in the same mutation. Top-level mutation fields execute serially, so keep the branch field before the alias update.

mutation BranchAndRepointAlias($accountId: String = "") {
  branch: admin {
    branch(input: {}) {
      accountId @export(as: "accountId")
    }
  }

  moveAlias: admin {
    updateAlias(
      input: {
        alias: "twisp-dev"
        accountId: $accountId
        ttlSeconds: 1
      }
    ) {
      alias
      accountId
      region
      ttlSeconds
    }
  }
}

The initial value of $accountId satisfies GraphQL variable validation. @export replaces it with the generated branch account ID before moveAlias executes.

Branch creation and alias updates commit through independent administrative transactions. If the branch succeeds but updateAlias fails, the branch remains and the alias continues pointing at its previous tenant. Use separate mutations when your workflow needs to retain the generated branch ID for explicit retry and recovery.

For a planned low-downtime switch:

  1. Lower the current alias TTL and wait out its previous TTL.
  2. Branch the source tenant.
  3. Test the new tenant using its explicit accountId.
  4. Repoint the alias to the branch.
  5. After the switch is visible, restore the normal alias TTL.