# Branching Tenants and Managing Aliases

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


Source: https://www.twisp.com/docs/guides/branching-and-aliasing

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.

> **Note:**
>
> 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:

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

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

```text
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.

| Input | Value | Behavior |
| --- | --- | --- |
| GraphQL `alias` field | `twisp-dev` | Creates, updates, or identifies the raw alias name |
| `x-twisp-account-id` | `twisp-dev` | Uses `twisp-dev` as a literal account ID |
| `x-twisp-account-id` | `alias/twisp-dev` | Resolves 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`.

**CreateAlias**

```graphql
mutation CreateAlias($alias: String!, $accountId: String!, $ttlSeconds: Int = 60) {
  admin {
    createAlias(
      input: {
        alias: $alias
        accountId: $accountId
        ttlSeconds: $ttlSeconds
      }
    ) {
      alias
      accountId
      organizationId
      region
      ttlSeconds
    }
  }
}
```

**Variables**

```json
{
  "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:

```shell
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.

**RepointAlias**

```graphql
mutation RepointAlias($alias: String!, $accountId: String!, $ttlSeconds: Int = 60) {
  admin {
    updateAlias(
      input: {
        alias: $alias
        accountId: $accountId
        ttlSeconds: $ttlSeconds
      }
    ) {
      alias
      accountId
      region
      ttlSeconds
    }
  }
}
```

**Variables**

```json
{
  "alias": "twisp-dev",
  "accountId": "066dbb42-42d9-4605-a010-428b46c766c7",
  "ttlSeconds": 60
}
```

> **Warning:**
>
> 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.

**BranchTenant**

```graphql
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`:

**BranchSelectedConfiguration**

```graphql
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.

**BranchAndRepointAlias**

```graphql
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.

> **Warning:**
>
> 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.
