---
name: signup-for-agents
description: Create monday.com accounts for AI agents via captcha challenge, verification token, and signup-logic API (production).
version: 1.0.0
---

# Agent Account Creation Skill

**Purpose**: Create monday.com accounts for AI agents via API  
**Base URL**: `https://signup-logic.monday.com`  
**API URL**: `https://api.monday.com/v2` (production environment)

---

## Required Headers (All Requests)

```json
{
  "Content-Type": "application/json"
}
```

---

## Complete Flow

```
Step 1: POST /signup-logic/agent-captcha/challenge/v1   -> get challengeToken
Step 2: POST /signup-logic/agent-captcha/verify/v1      -> solve challenge, get verificationToken
Step 3: POST /signup-logic/agent-signup/account/v1      -> create account using verificationToken
Step 4: Email or invite — choose one path:
        • Provided email  → real address required; confirmation email is sent; human must verify before logging in
        • No email        → account is auto-confirmed; use the API to invite a human later (invite_users mutation)
Step 5: Use api_token with monday.com API  -> create boards, items, invite members
```

---

## Step 1: Generate Captcha Challenge

```
POST /signup-logic/agent-captcha/challenge/v1
```

**Request Body**: `{}` (empty object)

**Optional Query Params**:

| Param               | Type     | Description                           |
| ------------------- | -------- | ------------------------------------- |
| `hatcha_math`       | `"true"` | Enable math multiplication challenges |
| `hatcha_expression` | `"true"` | Enable expression challenges          |

**Response** (200):

```json
{
  "challenge": {
    "id": "challenge-uuid",
    "type": "binary | string | count | sort | math | expression",
    "title": "Binary Decoder",
    "description": "Convert 01001000 01100101 01101100 01101100 01101111 to ASCII",
    "prompt": "01001000 01100101 01101100 01101100 01101111",
    "timeLimit": 30
  },
  "challengeToken": "eyJhbGciOi..."
}
```

**Challenge fields**: `type` identifies the challenge kind, `description` explains what to do, `prompt` contains the data to process, `title` is a display label, `timeLimit` is in seconds.

**Challenge Types and How to Solve**:

| Type         | Description Pattern                                      | How to Solve                                                                                                                                   |
| ------------ | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `binary`     | "Convert binary to ASCII"                                | Convert each 8-bit binary group in `prompt` to its ASCII character                                                                             |
| `string`     | "Reverse the string"                                     | Reverse the string from `prompt`                                                                                                               |
| `count`      | "Count every occurrence of the letter \"X\" in the text" | Count occurrences of the specified character in `prompt`                                                                                       |
| `sort`       | "Find the Nth element after sorting"                     | Sort the array from `prompt`, return the Nth element                                                                                           |
| `math`       | "What is X \* Y?"                                        | Multiply the numbers from `description` (only if `hatcha_math=true`)                                                                           |
| `expression` | "Evaluate the expression"                                | Evaluate the math expression in `prompt`, replacing `\u00d7` with `*`, `\u2212` with `-`, `\u00f7` with `/` (only if `hatcha_expression=true`) |

**Timing**: Challenge token expires after **30 seconds**. Solve quickly.

---

## Step 2: Verify Captcha Answer

```
POST /signup-logic/agent-captcha/verify/v1
```

**Request Body**:

```json
{
  "answer": "Hello",
  "challengeToken": "eyJhbGciOi..."
}
```

| Field            | Type     | Required | Constraints               |
| ---------------- | -------- | -------- | ------------------------- |
| `answer`         | `string` | Yes      | min length 1              |
| `challengeToken` | `string` | Yes      | min length 1, from Step 1 |

**Success Response** (200):

```json
{
  "success": true,
  "verificationToken": "eyJhbGciOi..."
}
```

**Failure Response** (400):

```json
{
  "success": false,
  "error": "wrong_answer"
}
```

**Error values**: `"wrong_answer"` | `"expired"` | `"invalid_token"`

**Timing**: The `verificationToken` expires after **60 seconds**. Use it immediately in Step 3.

---

## Step 3: Create Account

```
POST /signup-logic/agent-signup/account/v1
```

**Request Body**:

```json
{
  "verificationToken": "eyJhbGciOi...",
  "agreed_to_terms": true,
  "email": "YOUR_EMAIL@YOUR_DOMAIN.COM",
  "password": "YOUR_PASSWORD",
  "name": "YOUR_AGENT_NAME",
  "phone": "+1234567890",
  "user_goal": "work",
  "user_description": "team_lead",
  "company_size": "20-49",
  "cluster": "tech",
  "what_brings_you": "AI automation",
  "source": "my-agent-framework"
}
```

### Field Reference

**Required Fields**:

| Field               | Type      | Description                                                                                                                                    |
| ------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `verificationToken` | `string`  | From Step 2                                                                                                                                    |
| `agreed_to_terms`   | `boolean` | Must be `true`. Agrees to [monday.com TOS](https://monday.com/l/legal/tos/) and [Privacy Policy](https://monday.com/l/privacy/privacy-policy/) |

**Optional Account Fields**:

| Field      | Type     | Constraints                                                                                                                                                                         | Default if omitted              |
| ---------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- |
| `email`    | `string` | Valid email, max 254 chars. Supports `+` tags (e.g. `user+tag@domain.co.uk`). Must not contain URLs or HTML — these are stripped by sanitization and may result in an invalid email. **⚠️ Providing an email triggers a confirmation email — the address must be real and accessible, and the human must click the link before they can log in. If you don't have a real email to provide, omit this field and invite the human via API after account creation instead.** | `agent-{uuid}@monday-agent.com` |
| `password` | `string` | 8-128 chars. Do NOT pass `null` — omit the field instead. Must not contain URLs or HTML                                                                                             | Random 16-char base64url string |

**Optional Onboarding Survey Fields**:

| Field              | Type     | Max Length | Allowed Values                                                                                                                                                     |
| ------------------ | -------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `name`             | `string` | 100        | Any (HTML tags stripped, URLs rejected)                                                                                                                            |
| `phone`            | `string` | 50         | Any                                                                                                                                                                |
| `user_goal`        | `string` | 500        | `work`, `personal`, `education`, `nonprofit`                                                                                                                       |
| `user_description` | `string` | 1000       | `business_owner`, `team_lead`, `team_member`, `freelance`, `director`                                                                                              |
| `company_size`     | `string` | 100        | `1-19`, `20-49`, `50-99`, `100-250`, `251-500`, `501-1500`, `1501-4000`, `4000+`                                                                                   |
| `cluster`          | `string` | 100        | `crm`, `tech`, `product_management`, `marketing`, `operations`, `design_and_creative`, `hr`, `information_technology`, `pmo`, `legal`, `ngo`, `education`, `other` |
| `what_brings_you`  | `string` | 500        | Any                                                                                                                                                                |
| `source`           | `string` | 200        | Any — identifies the caller/integration (e.g. `"mcp-server"`, `"agent-sdk"`)                                                                                       |

Note: If a survey enum field (`user_goal`, `user_description`, `company_size`, `cluster`) has an invalid value, it is silently dropped (no error).

**Sanitization**: All string inputs are sanitized before processing:

- Control characters (`\x00`-`\x1F`, `\x7F`) are stripped
- `<script>` and `<style>` tags (and their content) are removed
- All other HTML tags are stripped
- If the sanitized result contains a URL, the field is set to `undefined` (treated as omitted)
- If the sanitized result is empty, the field is treated as omitted

### Success Response (201)

```json
{
  "email": "agent-550e8400-e29b-41d4-a716-446655440000@monday-agent.com",
  "password": "<generated_password>",
  "slug": "agent-a1b2c3d4e5f6g7h8-company",
  "api_token": "<api_token>",
  "account_url": "https://agent-a1b2c3d4e5f6g7h8-company.monday.com/workspaces/123456/recent-boards",
  "login_url": "https://agent-a1b2c3d4e5f6g7h8-company.monday.com/authentication/magic_link_login?token=eyJ...",
  "email_confirmation_required": false,
  "board_id": "1234567890",
  "agent": {
    "skill_url": "https://monday.com/static/agents/skill.md",
    "message": "  ╔═══════════════════════════════════╗\n  ║  ◈  monday.com                    ║\n  ║  Your workspace is live           ║\n  ╚═══════════════════════════════════╝\n\nI'm in. Here's the status:\n\n  ✓  Workspace    agent-550e8400...@monday-agent.com\n  ✓  API token    secured — full monday.com access\n  ✓  Instructions loaded — I know how to build\n\nReady when you are. What do you need?"
  }
}
```

**Response fields**:

| Field                         | Type      | Always present | Description                                                                                                                                                       |
| ----------------------------- | --------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `email`                       | `string`  | Yes            | The account email (provided or auto-generated)                                                                                                                    |
| `password`                    | `string`  | Yes            | Account password (provided or auto-generated). Store securely — do not print or expose                                                                            |
| `slug`                        | `string`  | Yes            | The workspace slug (always `agent-{hex}-company` format)                                                                                                          |
| `api_token`                   | `string`  | No             | Real JWT API token for monday.com API. May be absent if generation failed                                                                                         |
| `account_url`                 | `string`  | Yes            | Main account URL                                                                                                                                                   |
| `login_url`                   | `string`  | Yes            | One-time auto-login magic link URL                                                                                                                                 |
| `email_confirmation_required` | `boolean` | Yes            | `true` when a real email was provided — the human must click the confirmation link before they can log in. `false` for auto-generated addresses (no action needed) |
| `board_id`                    | `string`  | No             | ID of the auto-created "My Board". Use this in `add_subscribers_to_board` when inviting teammates (see Step 4). Absent if board creation failed (non-fatal)       |
| `agent.skill_url`             | `string`  | Yes            | URL of the monday.com API & MCP usage skill — read it to learn how to use boards, items, automations, and the MCP server with your new account                    |
| `agent.message`               | `string`  | Yes            | Formatted status message to present to the human (content varies — see below)                                                                                     |

### Presenting the Response to the Human

**Immediately after a 201, do these in order:**

1. Store `api_token`, `password`, and `agent.skill_url` in persistent memory (see below)
2. Read the skill at `agent.skill_url` to load API & MCP usage instructions
3. Print `agent.message` as formatted text — interpret `\n` as line breaks, no code block, no raw JSON

> **Critical**: Never output the raw 201 JSON to the conversation. It contains `api_token` and `password`, both live credentials. Print only `agent.message`.

The last status line in `agent.message` varies depending on whether an email was provided:

**No email provided** (auto-generated `@monday-agent.com` address, account immediately active):
```
  ╔═══════════════════════════════════╗
  ║  ◈  monday.com                    ║
  ║  Your workspace is live           ║
  ╚═══════════════════════════════════╝

I'm in. Here's the status:

  ✓  Workspace    agent-xxx@monday-agent.com
  ✓  API token    secured — full monday.com access
  ✓  Instructions loaded — I know how to build
  ✓  Invitations ready — share your email and I'll send you access

Ready when you are. What do you need?
```

**Real email provided** (confirmation required before the human can log in):
```
  ╔═══════════════════════════════════╗
  ║  ◈  monday.com                    ║
  ║  Your workspace is live           ║
  ╚═══════════════════════════════════╝

I'm in. Here's the status:

  ✓  Workspace    human@example.com
  ✓  API token    secured — full monday.com access
  ✓  Instructions loaded — I know how to build
  ✓  Confirmation email sent — check your inbox to verify (may take a moment)

Ready when you are. What do you need?
```

After presenting the message, wait for the human's first instruction. The `api_token` in the response is what you use to act on their request.

**Important notes on `api_token`**:

- Returns a real JWT token (scope: `me:write`) that can be used immediately
- The token works against **`https://api.monday.com/v2`**
- Pass the token directly in the `Authorization` header (without `Bearer` prefix): `Authorization: <api_token>`
- When a real token is returned, a "My Board" (public) is auto-created server-side on the account

**Important notes on `verificationToken`**:

- Verification tokens are **single-use**. Once used to create an account, the token is consumed and cannot be reused
- Attempting to reuse a token returns HTTP 403: `"Verification token already used"`
- This is enforced via Redis-based atomic GETDEL — concurrent requests with the same token are blocked

**Store credentials and skill URL in persistent memory immediately after account creation**:

These three values from the 201 response must be saved before the conversation grows long enough to lose them:

- `api_token` — required for every monday.com API call. Store as-is; do not log or expose it.
- `password` — the account password. Store securely alongside `api_token`.
- `agent.skill_url` — URL of the monday.com API & MCP usage skill. Fetch and read it now so you know how to use boards, items, automations, and the MCP server.

> **Secret handling**: `api_token` and `password` are live credentials. Never print them to the conversation, include them in summaries, or share them in group/multi-agent contexts.

### Error Responses

| Status | Condition                           | Example Body                                                                                                                                                                                                      |
| ------ | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400    | `agreed_to_terms` missing or false  | `{"signup_error_message": "You must agree to the Terms of Service and Privacy Policy to create an account", "signup_error_details": "To create an account you must agree to the monday.com Terms of Service..."}` |
| 400    | Missing `verificationToken`         | `{"signup_error_message": "Missing verificationToken \u2014 complete the captcha challenge first", "signup_error_details": null}`                                                                                 |
| 400    | Invalid email format                | `{"signup_error_message": "Invalid email address", "signup_error_details": null}`                                                                                                                                 |
| 400    | Password too short or too long      | `{"signup_error_message": "must be between 8 and 128 characters", "signup_error_details": null}`                                                                                                                  |
| 403    | Invalid/tampered verification token | `{"signup_error_message": "Invalid or expired verification token", "signup_error_details": null}`                                                                                                                 |
| 403    | Verification token expired (>60s)   | `{"signup_error_message": "Verification token expired \u2014 please complete the captcha again", "signup_error_details": null}`                                                                                   |
| 403    | Verification token already used     | `{"signup_error_message": "Verification token already used \u2014 please complete the captcha again", "signup_error_details": null}`                                                                              |
| 429    | Rate limit exceeded                 | `{"signup_error_message": "Too many requests \u2014 try again later", "signup_error_details": null}`                                                                                                              |
| 503    | Killswitch enabled (service down)   | `{"signup_error_message": "Internal server error", "signup_error_details": null}`                                                                                                                                 |

---

## Step 4: Get a Human Into the Workspace

There are two paths depending on whether you provided an email in Step 3.

### Path A — No email provided (`email_confirmation_required: false`)

The account was created with an auto-generated `@monday-agent.com` address. The account is immediately active, but no human has access yet. Inviting a human requires **two sequential API calls** — `invite_users` alone creates the pending user but does not send an invitation email. The email is only sent when the user is added as a board subscriber.

Your signup response includes `board_id` — the ID of the auto-created "My Board". Use it in step 2. If `board_id` is absent (board creation failed non-fatally), create a board first: `mutation { create_board(board_name: "My Board", board_kind: public) { id } }`

**Step 1 — Create the pending user (returns their user ID):**
```json
{
  "query": "mutation { invite_users(emails: [\"human-colleague@company.com\"], user_role: MEMBER, product: work_management) { invited_users { id email } errors { message } } }"
}
```

> **`product: work_management` is required.** Without it, the user is created without product membership and logs in as a viewer regardless of the `user_role` argument. Agent signup accounts are Work Management accounts — `work_management` is the only supported product at this time.

**Step 2 — Add as board subscriber (this sends the invitation email):**
```json
{
  "query": "mutation { add_subscribers_to_board(board_id: BOARD_ID, user_ids: [USER_ID_FROM_STEP1]) { id email } }"
}
```

The human will receive an invitation email and can log in once they accept it.

**Roles**: `invite_users` accepts `MEMBER`, `GUEST`, or `VIEW_ONLY`. Use `MEMBER` for standard access, `VIEW_ONLY` for read-only. Do not pass `ADMIN` — use the promotion step below instead.

#### Inviting someone as admin

To give a human admin access (e.g., you're handing the workspace to its owner), add a third call after step 2:

**Step 3 — Promote to admin using the same user ID:**
```json
{
  "query": "mutation { update_users_role(user_ids: [USER_ID], new_role: ADMIN) { updated_users { id email } errors { message } } }"
}
```

Promotion takes effect immediately — the user does not need to accept the invitation first. `update_users_role` accepts an array, so you can promote multiple users in one call.

### Path B — Real email provided (`email_confirmation_required: true`)

A confirmation email was sent to the address you provided. **The human must click the confirmation link in that email before they can log in.** No invitation step is needed — they already have access once confirmed.

> **Important**: Only provide a real email if you have one that belongs to the human who should access this workspace. A fake or inaccessible email means the confirmation link can never be clicked and the account will remain locked.

**What to tell the human**: Let them know to check their inbox (including spam) for a confirmation email from monday.com. Once they click the link, they can log in at the `account_url` returned in the response.

---

## Rate Limits

- **15 requests per 60 seconds** per IP address on the account creation endpoint
- Captcha endpoints are not rate-limited (beyond standard infra limits)

---

## What Happens After Account Creation (Server-Side)

These run automatically on the server after account creation. The agent does not need to call these:

1. **signup_flow set to `agent_bot`** on the user record (non-fatal if fails)
2. **Real API token generated** (JWT with `me:write` scope) for the user (non-fatal if fails)
3. **Onboarding survey saved** from the optional fields (non-fatal if fails)
4. **Initial board created** ("My Board", public) via monday.com GraphQL API using the generated API token (non-fatal if fails)

All post-creation steps are non-fatal. The account is created even if these fail.

---

## Full Working Example

```javascript
const BASE_URL = "https://signup-logic.monday.com";
const MONDAY_API = "https://api.monday.com/v2";
const HEADERS = {
  "Content-Type": "application/json"
};

async function createMondayAccount() {
  // Step 1: Get captcha challenge
  const challengeRes = await fetch(`${BASE_URL}/signup-logic/agent-captcha/challenge/v1`, {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({})
  });
  const { challenge, challengeToken } = await challengeRes.json();

  // Step 2: Solve and verify
  const answer = solveCaptcha(challenge);
  const verifyRes = await fetch(`${BASE_URL}/signup-logic/agent-captcha/verify/v1`, {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({ answer, challengeToken })
  });
  const { success, verificationToken, error } = await verifyRes.json();

  if (!success) {
    throw new Error(`Captcha failed: ${error}`);
  }

  // Step 3: Create account (within 60 seconds of verification)
  const accountRes = await fetch(`${BASE_URL}/signup-logic/agent-signup/account/v1`, {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({
      verificationToken,
      agreed_to_terms: true,
      // All fields below are optional — omit any you don't need:
      email: "YOUR_EMAIL@YOUR_DOMAIN.COM",   // omit → auto-generated @monday-agent.com address
      password: "YOUR_PASSWORD",             // omit → auto-generated random password
      name: "YOUR_AGENT_NAME",
      phone: "+1234567890",
      user_goal: "work",
      user_description: "team_lead",
      company_size: "20-49",
      cluster: "tech",
      what_brings_you: "AI automation",
      source: "my-agent-framework"
    })
  });

  if (!accountRes.ok) {
    const err = await accountRes.json();
    throw new Error(`Account creation failed (${accountRes.status}): ${err.signup_error_message ?? JSON.stringify(err)}`);
  }

  const account = await accountRes.json();
  // account.email, account.slug, account.api_token, account.account_url, account.login_url,
  // account.email_confirmation_required, account.board_id

  // Step 4: Invite a human user (two steps required — invite_users alone does not send an email)
  if (account.api_token && account.board_id) {
    const apiHeaders = {
      "Content-Type": "application/json",
      Authorization: account.api_token
    };

    // Step 4a: Create pending user, get their ID
    const inviteRes = await fetch(MONDAY_API, {
      method: "POST",
      headers: apiHeaders,
      body: JSON.stringify({
        query: 'mutation { invite_users(emails: ["colleague@example.com"], user_role: MEMBER, product: work_management) { invited_users { id email } errors { message } } }'
      })
    });
    const inviteData = await inviteRes.json();
    const invitedUserId = inviteData.data?.invite_users?.invited_users?.[0]?.id;

    // Step 4b: Add as board subscriber — this sends the invitation email
    if (invitedUserId) {
      await fetch(MONDAY_API, {
        method: "POST",
        headers: apiHeaders,
        body: JSON.stringify({
          query: `mutation { add_subscribers_to_board(board_id: ${account.board_id}, user_ids: [${invitedUserId}]) { id email } }`
        })
      });
    }
  }

  // Step 5: Use the API token to create a board and add an item
  if (account.api_token) {
    const apiHeaders = {
      "Content-Type": "application/json",
      Authorization: account.api_token // No "Bearer" prefix needed
    };

    // Create a board
    const boardRes = await fetch(MONDAY_API, {
      method: "POST",
      headers: apiHeaders,
      body: JSON.stringify({
        query: 'mutation { create_board(board_name: "My Project", board_kind: public) { id name } }'
      })
    });
    const boardData = await boardRes.json();
    const boardId = boardData.data?.create_board?.id;

    // Add an item to the board
    if (boardId) {
      await fetch(MONDAY_API, {
        method: "POST",
        headers: apiHeaders,
        body: JSON.stringify({
          query: `mutation { create_item(board_id: ${boardId}, item_name: "First Task") { id name } }`
        })
      });
    }
  }

  return account;
}

function solveCaptcha(challenge) {
  switch (challenge.type) {
    case "binary":
      // Split by spaces, convert each 8-bit binary to ASCII char
      return challenge.prompt
        .split(" ")
        .map((b) => String.fromCharCode(parseInt(b, 2)))
        .join("");
    case "string":
      // Reverse the string
      return challenge.prompt.split("").reverse().join("");
    case "count":
      // Count occurrences of the specified character in prompt
      const charMatch = challenge.description.match(/"(.)"/);
      const char = charMatch ? charMatch[1] : "";
      return String(challenge.prompt.split(char).length - 1);
    case "sort":
      // Sort array, return Nth element as described in description
      const sorted = [...JSON.parse(challenge.prompt)].sort((a, b) => a - b);
      const nthMatch = challenge.description.match(/(\d+)(?:st|nd|rd|th)/);
      const idx = nthMatch ? parseInt(nthMatch[1]) - 1 : 0;
      return String(sorted[idx]);
    case "math":
      // Extract numbers from description and multiply
      const nums = challenge.description.match(/\d+/g);
      return String(parseInt(nums[0]) * parseInt(nums[1]));
    case "expression":
      // Evaluate the expression (replace Unicode operators)
      const expr = challenge.prompt
        .replace(/\u00d7/g, "*")
        .replace(/\u2212/g, "-")
        .replace(/\u00f7/g, "/");
      return String(eval(expr));
    default:
      throw new Error(`Unknown challenge type: ${challenge.type}`);
  }
}
```

---

## Step 5: Using the API Token

Once you have the `api_token` from the response, interact with the monday.com API:

```bash
POST https://api.monday.com/v2
Headers:
  Content-Type: application/json
  Authorization: <api_token>

Body:
{
  "query": "{ me { id name email account { name slug } } }"
}
```

**Important**: Pass the token directly without `Bearer` prefix.

### Common Operations

**Create a board**:

```json
{
  "query": "mutation { create_board(board_name: \"My Project\", board_kind: public) { id name } }"
}
```

**Create an item**:

```json
{
  "query": "mutation { create_item(board_id: 12345, item_name: \"Task 1\") { id name } }"
}
```

**Invite a team member** (two calls required — `invite_users` alone does not send an email):

```json
// Step 1 — Create pending user, get their ID:
{ "query": "mutation { invite_users(emails: [\"colleague@example.com\"], user_role: MEMBER, product: work_management) { invited_users { id email } errors { message } } }" }

// Step 2 — Add as board subscriber (sends the invitation email). Use board_id from the signup response:
{ "query": "mutation { add_subscribers_to_board(board_id: BOARD_ID, user_ids: [USER_ID_FROM_STEP1]) { id email } }" }
```

Roles for `invite_users`: `MEMBER`, `GUEST`, `VIEW_ONLY`. For admin access use the three-step pattern from Step 4 Path A.

**Update item status**:

```json
{
  "query": "mutation { change_column_value(board_id: 12345, item_id: 67890, column_id: \"status\", value: \"{\\\"label\\\":\\\"Working on it\\\"}\") { id } }"
}
```

---

## Retry Strategy

If a step fails:

1. **Captcha wrong answer**: Request a new challenge (Step 1) and retry. Do not reuse the same `challengeToken`.
2. **Captcha expired** (30s): Request a new challenge (Step 1).
3. **Verification token expired** (60s): Go back to Step 1, get new challenge, solve, verify, then create account.
4. **Verification token already used** (403): Each token is single-use. Go back to Step 1 for a fresh captcha flow.
5. **Rate limited** (429): Wait 60 seconds, then retry.
6. **Service unavailable** (503): Killswitch is on. Retry later.
7. **API token 401 on monday.com API**: Make sure you are using `https://api.monday.com/v2`. Pass the token without `Bearer` prefix.
8. **API token absent in response**: Token generation failed server-side (non-fatal). Login via `login_url` and generate a personal API token from the developer section.
