---
title: "add_test_end_users"
description: "Generated reference for the management action add_test_end_users: its description, access metadata, payload contract, and registration status."
slug: /reference/actions/add-test-end-users
order: 61
visibility: public
redirects_from: ["/reference/actions/create-test-end-users","/reference/actions/delete-test-end-users","/reference/actions/withdraw-test-end-users","/reference/actions/test-end-users"]
---

# add\_test\_end\_users

Generated automatically from the [published contract sources](/cloud/reference/about/#contract-sources).

A script calls this action over HTTPS at `POST https://turnzero.ai/api/v1/actions/add_test_end_users`, with a bearer credential and the action's payload as the JSON body. An MCP connection lists this action as the tool `tzcloud_add_test_end_users`.

**Build metadata:** Registered in this build. Registration describes the default dispatcher in this build. It does not guarantee that a caller has the required credential or grant, that a tool is listed for that connection, or that the required service is configured.

## Contract description

Create test end users on an application's sign\-in realm with `labels`, or remove them with `remove: true` and `end_users` or `all: true`\. Pass `application` and `environment`\. This tests sign\-in without a person or mailbox\. Each user has `<label>@synthetic.turnzero.ai`, receives no mail, and is removed after 48 hours\. Open the returned test sign\-in page, enter its address, get its code with `read_test_signin_code`, and enter the code; the session is real and your code runs unchanged\.

On production, and under `workforce` on development too, a minted token needs the `test_end_users` grant; a session needs none\. Removal needs no browser approval, removes held codes, and ends sessions on every router within sixty seconds\. Removing a real end user is refused; use `delete_end_user` for that\.

## More about this action

Your session creates test end users on every realm\. A minted token needs the `test_end_users` grant on production, and under the workforce audience on either realm\. A creation is refused `test_end_users_not_admitted` where the realm or your credential is not admitted, and `test_end_users_ceiling_reached` past twenty on the realm or fifty created within the hour\.

The `environment` argument is required: pass `development` or `production`, the environment whose realm you address\. The development realm exists only after `create_environment` has turned development on\.

The development realm accepts every credential from its creation, except under the workforce audience\. `create_environment` removes no test end user\. Mint a token with the `test_end_users` grant with `mint_token`; the person claims it with `token claim`\.

The call returns `test_signin_page`, the realm's test sign\-in page\. For each label it returns the user's `id`, `label`, `email`, `expires_at`, and `created`\. Where an unexpired test end user already has a label, the call returns that user with `created` false and creates nothing, so a repeat is safe\. Where the test end user with a label has expired, the call removes it and creates it afresh\.

Test end users count toward no user count or limit\. Where the call is refused `test_end_users_not_admitted`, the refusal's detail gives the remedy\. The call is also refused `not_test_end_user` for a label whose address belongs to a real end user, and `test_end_users_disabled` while the platform's `TEST_END_USERS` setting is off\.

The `labels` argument takes one to twenty labels\. Each label is the local part of a test end user's address at the fixture domain `synthetic.turnzero.ai`\. A label is three to forty characters: a lowercase letter, then lowercase letters, digits, and hyphens\. A label whose address another end user of the realm has is refused 409 `not_test_end_user`\.

`remove: true` removes test end users at once, passing `end_users`, their ids, up to twenty, or `all: true`\. The `environment` argument is required: `development` or `production`, the realm you address\. The call creates no pending action and needs no approval, because a test end user has no person's data\. Each removal also deletes the user's held codes and ends the sign\-ins they could still confirm\. The call is refused whole with `not_test_end_user`, removing nothing, where any listed user is not a test end user of that realm; remove a real end user with `delete_end_user`\.

`remove: true` works while the platform's `TEST_END_USERS` setting is off, so you can always clean up\. A member the other form takes is refused `invalid_request` by name: `labels` is a creation's alone, and `end_users` and `all` a removal's\.

To create, pass `application`, `environment`, and one to twenty `labels`\. To remove, pass `application`, `environment`, `remove: true`, and `end_users` or `all: true`\.

## Access and action metadata

```json
{
  "name": "add_test_end_users",
  "resource": "realm",
  "tier": "reversible",
  "surface": "tzcloud",
  "product": "cloud",
  "summary": "Create or remove the test end users of one realm of your account's own application. A call with `labels` makes up to twenty, `application` and `environment` both required. Each is a user row holding one verified `email` identity `<label>@synthetic.turnzero.ai`, expiring 48 hours after its creation, and counted as no user. It signs in through the realm's own emailed-code route, its code held for `read_test_signin_code` and never sent. For a label an unexpired test end user of the realm holds, the call returns that user unchanged with `created` false, so a retry creates nothing. A label whose row has expired is removed and created afresh. It admits the account's session and a token carrying `test_end_users` on every realm. On a production realm, and on a workforce application's realm, it refuses any other minted token `test_end_users_not_admitted`. Each creation there writes one info line with the application, the environment, the count, and the credential, and no address. Refused `test_end_users_ceiling_reached` past twenty unexpired test end users on the realm or fifty created by the application's realms within the hour. Refused `not_test_end_user` for a label whose address another end user of the realm holds, and `test_end_users_disabled` while the control plane's `TEST_END_USERS` setting reads `off`. `remove: true` removes test end users at once, with no pending action, `application` and `environment` both required and either `end_users` or `all` true: each removal ends the user's rows, identities, and sessions, writes the user revocation row the routers sync, deletes the user's held codes, and takes the live tickets they were started under. Refused whole `not_test_end_user` where any named user is not a test end user of the realm. A removal is admitted while `TEST_END_USERS` reads `off`, so an operator's stop strands no test end user.",
  "annotations": {
    "readOnlyHint": false,
    "destructiveHint": true,
    "openWorldHint": false
  }
}
```

## MCP catalog entry

```json
{
  "name": "add_test_end_users",
  "title": "Create or remove test app users",
  "tier": "reversible",
  "summary": "Create test end users on an application's sign-in realm with `labels`, or remove them with `remove: true` and `end_users` or `all: true`. Pass `application` and `environment`. This tests sign-in without a person or mailbox. Each user has `<label>@synthetic.turnzero.ai`, receives no mail, and is removed after 48 hours. Open the returned test sign-in page, enter its address, get its code with `read_test_signin_code`, and enter the code; the session is real and your code runs unchanged.\n\nOn production, and under `workforce` on development too, a minted token needs the `test_end_users` grant; a session needs none. Removal needs no browser approval, removes held codes, and ends sessions on every router within sixty seconds. Removing a real end user is refused; use `delete_end_user` for that.",
  "owners": [
    "ACS-L0-21",
    "ACS-L0-20",
    "PLD-L0-40",
    "ACS-L0-06"
  ],
  "scenario": "ACS-L0-21"
}
```

## request

| JSON pointer | Description and constraints |
|---|---|
| "" \(root\) | Type: object<br>Required fields: \["application"\]<br>Additional properties: false<br>Retired members: expires\_in\_days: Each test end user lasts 48 hours from its creation, one fixed lifetime: call again without expires\_in\_days\. act: Leave act out: pass labels to add, or remove: true with end\_users or all\. |
| /<wbr>properties/<wbr>application | The application id, from `list_applications`; one of your account's own\.<br><br>Type: string |
| /<wbr>properties/<wbr>environment | Required by both acts, and refused `invalid_request` without it: the environment whose realm you address\.<br><br>Type: string<br>Pattern: ^\(development\|production\)$ |
| /<wbr>properties/<wbr>labels | Required to create: each the local part of a test end user's address at the fixture domain `synthetic.turnzero.ai`; one whose address another end user holds is refused 409 `not_test_end_user`\.<br><br>Type: array<br>Minimum items: 1<br>Maximum items: 20 |
| /<wbr>properties/<wbr>labels/<wbr>items | Type: string<br>Pattern: ^\[a\-z\]\[a\-z0\-9\-\]\{2,39\}$ |
| /<wbr>properties/<wbr>end\_users | To delete: the test end users' ids, one of `end_users` and `all` required and never both; an id that is not a test end user of the realm refuses the whole request 409 `not_test_end_user`, and nothing is removed\.<br><br>Type: array<br>Minimum items: 1<br>Maximum items: 20 |
| /<wbr>properties/<wbr>end\_users/<wbr>items | Type: string |
| /<wbr>properties/<wbr>all | To delete every test end user of the realm; one of `end_users` and `all` is required, and never both\.<br><br>Required value: true |
| /<wbr>properties/<wbr>remove | `true` removes test end users at once, naming `end_users` or `all`\.<br><br>Type: boolean |

## response

| JSON pointer | Description and constraints |
|---|---|
| "" \(root\) | Type: object<br>Required fields: \["contract\_version","realm","application"\]<br>Additional properties: false |
| /<wbr>properties/<wbr>contract\_version | Required value: 1 |
| /<wbr>properties/<wbr>realm | The realm identifier: the application id for production, `<id>:development` for development\.<br><br>Type: string |
| /<wbr>properties/<wbr>application | Type: string |
| /<wbr>properties/<wbr>environment | The environment whose realm the call addressed\.<br><br>Type: string<br>Pattern: ^\(development\|production\)$ |
| /<wbr>properties/<wbr>test\_signin\_page | On a creation: The realm's test sign\-in page, `/__account/signin/test` on the environment's hostname, which offers the emailed\-code form to a test end user on every admitting realm with a non\-empty route set\.<br><br>Type: string |
| /<wbr>properties/<wbr>test\_end\_users | On a creation: one entry per label, in the request's order\.<br><br>Type: array<br>Minimum items: 1<br>Maximum items: 20 |
| /<wbr>properties/<wbr>test\_end\_users/<wbr>items | Type: object<br>Required fields: \["id","label","email","expires\_at","created"\]<br>Additional properties: false |
| /<wbr>properties/<wbr>test\_end\_users/<wbr>items/<wbr>properties/<wbr>id | The test end user's opaque identifier, the one `list_end_users` returns\.<br><br>Type: string |
| /<wbr>properties/<wbr>test\_end\_users/<wbr>items/<wbr>properties/<wbr>label | Type: string |
| /<wbr>properties/<wbr>test\_end\_users/<wbr>items/<wbr>properties/<wbr>email | `<label>@synthetic.turnzero.ai`, the verified address the user signs in with\.<br><br>Type: string |
| /<wbr>properties/<wbr>test\_end\_users/<wbr>items/<wbr>properties/<wbr>expires\_at | The instant from which the platform's expiry pass removes the user, within ten minutes\.<br><br>Type: string |
| /<wbr>properties/<wbr>test\_end\_users/<wbr>items/<wbr>properties/<wbr>created | True where this call created the row, false where an unexpired test end user already held the label and was returned unchanged\.<br><br>Type: boolean |
| /<wbr>properties/<wbr>removed | On a removal: the identifiers of the test end users removed, each with its identities, sessions, held codes, and live tickets; empty where none was found\.<br><br>Type: array |
| /<wbr>properties/<wbr>removed/<wbr>items | Type: string |
| /<wbr>properties/<wbr>detail | Type: string |

## Complete payload contract

```json
{
  "request": {
    "type": "object",
    "required": [
      "application"
    ],
    "properties": {
      "application": {
        "type": "string",
        "description": "The application id, from `list_applications`; one of your account's own."
      },
      "environment": {
        "type": "string",
        "pattern": "^(development|production)$",
        "description": "Required by both acts, and refused `invalid_request` without it: the environment whose realm you address."
      },
      "labels": {
        "type": "array",
        "minItems": 1,
        "maxItems": 20,
        "items": {
          "type": "string",
          "pattern": "^[a-z][a-z0-9-]{2,39}$"
        },
        "description": "Required to create: each the local part of a test end user's address at the fixture domain `synthetic.turnzero.ai`; one whose address another end user holds is refused 409 `not_test_end_user`."
      },
      "end_users": {
        "type": "array",
        "minItems": 1,
        "maxItems": 20,
        "items": {
          "type": "string"
        },
        "description": "To delete: the test end users' ids, one of `end_users` and `all` required and never both; an id that is not a test end user of the realm refuses the whole request 409 `not_test_end_user`, and nothing is removed."
      },
      "all": {
        "const": true,
        "description": "To delete every test end user of the realm; one of `end_users` and `all` is required, and never both."
      },
      "remove": {
        "type": "boolean",
        "description": "`true` removes test end users at once, naming `end_users` or `all`."
      }
    },
    "additionalProperties": false,
    "x-retired": {
      "expires_in_days": "Each test end user lasts 48 hours from its creation, one fixed lifetime: call again without expires_in_days.",
      "act": "Leave act out: pass labels to add, or remove: true with end_users or all."
    }
  },
  "response": {
    "type": "object",
    "required": [
      "contract_version",
      "realm",
      "application"
    ],
    "properties": {
      "contract_version": {
        "const": 1
      },
      "realm": {
        "type": "string",
        "description": "The realm identifier: the application id for production, `<id>:development` for development."
      },
      "application": {
        "type": "string"
      },
      "environment": {
        "type": "string",
        "pattern": "^(development|production)$",
        "description": "The environment whose realm the call addressed."
      },
      "test_signin_page": {
        "type": "string",
        "description": "On a creation: The realm's test sign-in page, `/__account/signin/test` on the environment's hostname, which offers the emailed-code form to a test end user on every admitting realm with a non-empty route set."
      },
      "test_end_users": {
        "type": "array",
        "minItems": 1,
        "maxItems": 20,
        "description": "On a creation: one entry per label, in the request's order.",
        "items": {
          "type": "object",
          "required": [
            "id",
            "label",
            "email",
            "expires_at",
            "created"
          ],
          "properties": {
            "id": {
              "type": "string",
              "description": "The test end user's opaque identifier, the one `list_end_users` returns."
            },
            "label": {
              "type": "string"
            },
            "email": {
              "type": "string",
              "description": "`<label>@synthetic.turnzero.ai`, the verified address the user signs in with."
            },
            "expires_at": {
              "type": "string",
              "description": "The instant from which the platform's expiry pass removes the user, within ten minutes."
            },
            "created": {
              "type": "boolean",
              "description": "True where this call created the row, false where an unexpired test end user already held the label and was returned unchanged."
            }
          },
          "additionalProperties": false
        }
      },
      "removed": {
        "type": "array",
        "items": {
          "type": "string"
        },
        "description": "On a removal: the identifiers of the test end users removed, each with its identities, sessions, held codes, and live tickets; empty where none was found."
      },
      "detail": {
        "type": "string"
      }
    },
    "additionalProperties": false
  }
}
```

## Shared contracts

- [Refusals](/cloud/reference/refusals/): every refusal, by surface, with its cause and its remedy
- [schemas/wire\_error\.schema\.json](/cloud/reference/sources/schemas/wire-error-schema/)
- [schemas/wire\_errors\.json](/cloud/reference/sources/schemas/wire-errors/)
- [schemas/action\_payloads\.json](/cloud/reference/sources/schemas/action-payloads/) (includes shared shapes)
- [management\_api\_contract\.md](/cloud/reference/sources/management-api-contract/)
