{
  "info": {
    "_postman_id": "cfd1a286-2dc1-4d62-97f7-2b8863edaa2d",
    "name": "CalmBoard Integrations API",
    "description": "API-key management, per-user OAuth redirect settings, versioned integration API examples, and MCP server examples for CalmBoard. Use a session token for settings calls and an integration API key or OAuth bearer token for /api/v1 and /mcp calls.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "event": [
    {
      "listen": "test",
      "script": {
        "type": "text/javascript",
        "exec": [
          "pm.test(\"Response does not return a server error\", function () {",
          "  pm.expect(pm.response.code).to.be.below(500);",
          "});",
          "",
          "const contentType = pm.response.headers.get(\"Content-Type\") || \"\";",
          "if (contentType.includes(\"application/json\")) {",
          "  pm.test(\"Response body is valid JSON\", function () {",
          "    pm.response.json();",
          "  });",
          "}"
        ]
      }
    }
  ],
  "variable": [
    { "key": "baseUrl", "value": "https://calmboard.ai" },
    { "key": "sessionToken", "value": "" },
    { "key": "loginEmail", "value": "user@example.com" },
    { "key": "loginPassword", "value": "Password1!" },
    { "key": "recaptchaToken", "value": "" },
    { "key": "apiKey", "value": "cbk_your_api_key" },
    { "key": "oauthAccessToken", "value": "cbo_your_oauth_access_token" },
    { "key": "oauthClientId", "value": "cb_oauth_your_client_id" },
    { "key": "oauthRedirectUri", "value": "http://localhost:6274/oauth/callback" },
    { "key": "oauthResource", "value": "https://calmboard.ai/api/v1" },
    { "key": "mcpOAuthResource", "value": "https://calmboard.ai/mcp" },
    { "key": "oauthCode", "value": "" },
    { "key": "oauthCodeChallenge", "value": "BASE64URL_SHA256_VERIFIER" },
    { "key": "oauthCodeVerifier", "value": "replace_with_a_43_to_128_character_pkce_verifier_value" },
    { "key": "keyId", "value": "key_123" },
    { "key": "projectId", "value": "prj_123" },
    { "key": "boardId", "value": "brd_123" },
    { "key": "columnId", "value": "col_backlog" },
    { "key": "targetColumnId", "value": "col_review" },
    { "key": "invalidColumnId", "value": "col_does_not_exist" },
    { "key": "taskId", "value": "tsk_123" },
    { "key": "checklistId", "value": "chk_123" },
    { "key": "sprintId", "value": "spr_123" }
  ],
  "item": [
    {
      "name": "Session auth helper",
      "description": "Use this folder when you need a CalmBoard session token for session-authenticated requests. In production, reCAPTCHA may block direct Postman login; in that case sign in through the browser and copy localStorage.calmboard_token into the sessionToken collection variable.",
      "item": [
        {
          "name": "Sign in and set sessionToken",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{loginEmail}}\",\n  \"password\": \"{{loginPassword}}\",\n  \"recaptcha_token\": \"{{recaptchaToken}}\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/api/auth/login",
              "host": ["{{baseUrl}}"],
              "path": ["api", "auth", "login"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Sign in returns a CalmBoard session token\", function () {",
                  "  pm.response.to.have.status(200);",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.token).to.be.a(\"string\").and.not.empty;",
                  "  pm.collectionVariables.set(\"sessionToken\", json.token);",
                  "});"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "API key management (session auth)",
      "item": [
        {
          "name": "List API keys",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{sessionToken}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/integrations/api-keys",
              "host": ["{{baseUrl}}"],
              "path": ["api", "integrations", "api-keys"]
            }
          }
        },
        {
          "name": "Create API key",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{sessionToken}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Slack sync\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/api/integrations/api-keys",
              "host": ["{{baseUrl}}"],
              "path": ["api", "integrations", "api-keys"]
            }
          }
        },
        {
          "name": "Revoke API key",
          "request": {
            "method": "DELETE",
            "header": [
              { "key": "Authorization", "value": "Bearer {{sessionToken}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/integrations/api-keys/{{keyId}}",
              "host": ["{{baseUrl}}"],
              "path": ["api", "integrations", "api-keys", "{{keyId}}"]
            }
          }
        }
      ]
    },
    {
      "name": "OAuth bearer token flow",
      "item": [
        {
          "name": "OAuth protected resource metadata",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/.well-known/oauth-protected-resource",
              "host": ["{{baseUrl}}"],
              "path": [".well-known", "oauth-protected-resource"]
            }
          }
        },
        {
          "name": "MCP OAuth protected resource metadata",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/.well-known/oauth-protected-resource/mcp",
              "host": ["{{baseUrl}}"],
              "path": [".well-known", "oauth-protected-resource", "mcp"]
            }
          }
        },
        {
          "name": "OAuth authorization server metadata",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/.well-known/oauth-authorization-server",
              "host": ["{{baseUrl}}"],
              "path": [".well-known", "oauth-authorization-server"]
            }
          }
        },
        {
          "name": "Register OAuth client dynamically",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"client_name\": \"Postman MCP client\",\n  \"redirect_uris\": [\n    \"{{oauthRedirectUri}}\"\n  ],\n  \"grant_types\": [\"authorization_code\"],\n  \"response_types\": [\"code\"],\n  \"token_endpoint_auth_method\": \"none\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/api/oauth/register",
              "host": ["{{baseUrl}}"],
              "path": ["api", "oauth", "register"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Dynamic client registration returns a client ID\", function () {",
                  "  pm.response.to.have.status(201);",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.client_id).to.be.a(\"string\").and.not.empty;",
                  "  pm.collectionVariables.set(\"oauthClientId\", json.client_id);",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Save OAuth redirect URIs (session auth)",
          "request": {
            "method": "PUT",
            "header": [
              { "key": "Authorization", "value": "Bearer {{sessionToken}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"redirect_uris\": [\n    \"{{oauthRedirectUri}}\"\n  ]\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/api/integrations/oauth-settings",
              "host": ["{{baseUrl}}"],
              "path": ["api", "integrations", "oauth-settings"]
            }
          }
        },
        {
          "name": "Issue OAuth authorization code (session auth)",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const token = String(pm.collectionVariables.get(\"sessionToken\") || pm.variables.get(\"sessionToken\") || \"\").trim();",
                  "if (!token) {",
                  "  throw new Error(\"Set sessionToken before issuing an OAuth authorization code. Run 'Session auth helper / Sign in and set sessionToken', or copy localStorage.calmboard_token from a signed-in CalmBoard browser session into the sessionToken collection variable.\");",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"OAuth authorization code request returns redirect_to\", function () {",
                  "  pm.response.to.have.status(200);",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.redirect_to).to.be.a(\"string\").and.not.empty;",
                  "  const redirectUrl = new URL(json.redirect_to);",
                  "  const code = redirectUrl.searchParams.get(\"code\");",
                  "  pm.expect(code).to.be.a(\"string\").and.not.empty;",
                  "  pm.collectionVariables.set(\"oauthCode\", code);",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{sessionToken}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/oauth/authorize?response_type=code&client_id={{oauthClientId}}&redirect_uri={{oauthRedirectUri}}&scope=integrations%3Aread%20integrations%3Awrite&state=postman-state&code_challenge={{oauthCodeChallenge}}&code_challenge_method=S256&resource={{oauthResource}}",
              "host": ["{{baseUrl}}"],
              "path": ["api", "oauth", "authorize"],
              "query": [
                { "key": "response_type", "value": "code" },
                { "key": "client_id", "value": "{{oauthClientId}}" },
                { "key": "redirect_uri", "value": "{{oauthRedirectUri}}" },
                { "key": "scope", "value": "integrations:read integrations:write" },
                { "key": "state", "value": "postman-state" },
                { "key": "code_challenge", "value": "{{oauthCodeChallenge}}" },
                { "key": "code_challenge_method", "value": "S256" },
                { "key": "resource", "value": "{{oauthResource}}" }
              ]
            }
          }
        },
        {
          "name": "Exchange OAuth authorization code for bearer token",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/x-www-form-urlencoded" }
            ],
            "body": {
              "mode": "urlencoded",
              "urlencoded": [
                { "key": "grant_type", "value": "authorization_code", "type": "text" },
                { "key": "client_id", "value": "{{oauthClientId}}", "type": "text" },
                { "key": "redirect_uri", "value": "{{oauthRedirectUri}}", "type": "text" },
                { "key": "code", "value": "{{oauthCode}}", "type": "text" },
                { "key": "code_verifier", "value": "{{oauthCodeVerifier}}", "type": "text" },
                { "key": "resource", "value": "{{oauthResource}}", "type": "text" }
              ]
            },
            "url": {
              "raw": "{{baseUrl}}/api/oauth/token",
              "host": ["{{baseUrl}}"],
              "path": ["api", "oauth", "token"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"OAuth token exchange returns a bearer token\", function () {",
                  "  pm.response.to.have.status(200);",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.access_token).to.be.a(\"string\").and.not.empty;",
                  "  pm.expect(json.token_type).to.eql(\"Bearer\");",
                  "  pm.collectionVariables.set(\"oauthAccessToken\", json.access_token);",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "List projects with OAuth bearer token",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{oauthAccessToken}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects"]
            }
          }
        }
      ]
    },
    {
      "name": "MCP server",
      "description": "Paid-plan MCP examples. Use either Authorization: Bearer {{oauthAccessToken}} or Authorization: Bearer {{apiKey}}.",
      "item": [
        {
          "name": "MCP server info",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/mcp",
              "host": ["{{baseUrl}}"],
              "path": ["mcp"]
            }
          }
        },
        {
          "name": "MCP initialize",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{oauthAccessToken}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"initialize\",\n  \"params\": {\n    \"protocolVersion\": \"2025-06-18\",\n    \"capabilities\": {},\n    \"clientInfo\": {\n      \"name\": \"Postman MCP test\",\n      \"version\": \"1.0.0\"\n    }\n  }\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/mcp",
              "host": ["{{baseUrl}}"],
              "path": ["mcp"]
            }
          }
        },
        {
          "name": "MCP list tools",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{oauthAccessToken}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 2,\n  \"method\": \"tools/list\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/mcp",
              "host": ["{{baseUrl}}"],
              "path": ["mcp"]
            }
          }
        },
        {
          "name": "MCP call list tasks",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{oauthAccessToken}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 3,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"calmboard.list_tasks\",\n    \"arguments\": {\n      \"project_id\": \"{{projectId}}\",\n      \"board_id\": \"{{boardId}}\",\n      \"limit\": 20\n    }\n  }\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/mcp",
              "host": ["{{baseUrl}}"],
              "path": ["mcp"]
            }
          }
        },
        {
          "name": "MCP create task",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{oauthAccessToken}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 4,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"calmboard.create_task\",\n    \"arguments\": {\n      \"project_id\": \"{{projectId}}\",\n      \"column_id\": \"{{columnId}}\",\n      \"title\": \"Write integration follow-up\",\n      \"description\": \"Track the natural-language task flow rollout.\",\n      \"tags\": [\"integration\", \"ai\"]\n    }\n  }\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/mcp",
              "host": ["{{baseUrl}}"],
              "path": ["mcp"]
            }
          }
        },
        {
          "name": "MCP list sprints",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{oauthAccessToken}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 5,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"calmboard.list_sprints\",\n    \"arguments\": {\n      \"project_id\": \"{{projectId}}\",\n      \"board_id\": \"{{boardId}}\",\n      \"status\": \"active\"\n    }\n  }\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/mcp",
              "host": ["{{baseUrl}}"],
              "path": ["mcp"]
            }
          }
        },
        {
          "name": "MCP create sprint",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{oauthAccessToken}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 6,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"calmboard.create_sprint\",\n    \"arguments\": {\n      \"project_id\": \"{{projectId}}\",\n      \"board_id\": \"{{boardId}}\",\n      \"title\": \"Sprint 10 - Launch prep\",\n      \"start_date\": \"2026-04-20\",\n      \"end_date\": \"2026-05-01\",\n      \"status\": \"planned\"\n    }\n  }\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/mcp",
              "host": ["{{baseUrl}}"],
              "path": ["mcp"]
            }
          }
        },
        {
          "name": "MCP add project member",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{oauthAccessToken}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 7,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"calmboard.add_project_member\",\n    \"arguments\": {\n      \"project_id\": \"{{projectId}}\",\n      \"user_id\": \"maria@example.com\",\n      \"role\": \"editor\"\n    }\n  }\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/mcp",
              "host": ["{{baseUrl}}"],
              "path": ["mcp"]
            }
          }
        },
        {
          "name": "MCP call calculated report",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{oauthAccessToken}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 8,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"calmboard.get_calculated_report\",\n    \"arguments\": {\n      \"project_id\": \"{{projectId}}\",\n      \"board_id\": \"{{boardId}}\",\n      \"report_type\": \"quality_rework\"\n    }\n  }\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/mcp",
              "host": ["{{baseUrl}}"],
              "path": ["mcp"]
            }
          }
        },
        {
          "name": "MCP list tools with API key",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 4,\n  \"method\": \"tools/list\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/mcp",
              "host": ["{{baseUrl}}"],
              "path": ["mcp"]
            }
          }
        }
      ]
    },
    {
      "name": "Versioned API (API key auth)",
      "item": [
        {
          "name": "List projects",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"List projects returns 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "pm.test(\"Projects payload is an array\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.projects).to.be.an(\"array\");",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "List project boards",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/boards",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "boards"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"List boards returns 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "pm.test(\"Boards payload is an array\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.boards).to.be.an(\"array\");",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "List board columns",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/boards/{{boardId}}/columns",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "boards", "{{boardId}}", "columns"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"List columns returns 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "pm.test(\"Columns payload is an array\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.columns).to.be.an(\"array\");",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "List project members",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/members",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "members"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"List project members returns 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "pm.test(\"Members payload is an array\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.members).to.be.an(\"array\");",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Add project member",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"user_id\": \"maria@example.com\",\n  \"role\": \"editor\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/members",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "members"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Add project member returns 201\", function () {",
                  "  pm.response.to.have.status(201);",
                  "});",
                  "pm.test(\"Add project member returns the user id\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.user_id).to.eql(\"maria@example.com\");",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Update project member role",
          "request": {
            "method": "PATCH",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"role\": \"executive\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/members/maria@example.com",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "members", "maria@example.com"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Update project member role returns 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "pm.test(\"Update project member role returns the new role\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.role).to.eql(\"executive\");",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Remove project member",
          "request": {
            "method": "DELETE",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/members/maria@example.com",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "members", "maria@example.com"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Remove project member returns 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "List tasks",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/tasks?board_id={{boardId}}",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "tasks"],
              "query": [
                { "key": "board_id", "value": "{{boardId}}" }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"List tasks returns 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "pm.test(\"Tasks payload is an array\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.tasks).to.be.an(\"array\");",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Get task detail",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/tasks/{{taskId}}",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "tasks", "{{taskId}}"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Get task detail returns 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "pm.test(\"Get task detail returns the requested task\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.task_id).to.eql(pm.variables.get(\"taskId\"));",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Create task",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"board_id\": \"{{boardId}}\",\n  \"column_id\": \"{{columnId}}\",\n  \"title\": \"Sync Slack release alerts\",\n  \"description\": \"Post CalmBoard task updates to Slack.\",\n  \"position\": 1,\n  \"tags\": [\"integration\"]\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/tasks",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "tasks"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Create task returns 201\", function () {",
                  "  pm.response.to.have.status(201);",
                  "});",
                  "pm.test(\"Create task returns a task id\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.task_id).to.be.a(\"string\").and.not.empty;",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Update task",
          "request": {
            "method": "PATCH",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"title\": \"Sync Slack deployment alerts\",\n  \"description\": \"Post CalmBoard deployment task updates to Slack.\",\n  \"blocked\": false,\n  \"complexity_points\": 5\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/tasks/{{taskId}}",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "tasks", "{{taskId}}"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Update task returns 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "pm.test(\"Update task returns the task id\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.task_id).to.be.a(\"string\").and.not.empty;",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Archive task",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{}"
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/tasks/{{taskId}}/archive",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "tasks", "{{taskId}}", "archive"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Archive task returns 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Add task comment",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"body\": \"Slack webhook is configured and ready for review.\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/tasks/{{taskId}}/comments",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "tasks", "{{taskId}}", "comments"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Add comment returns 201\", function () {",
                  "  pm.response.to.have.status(201);",
                  "});",
                  "pm.test(\"Add comment returns a comment id\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.comment_id).to.be.a(\"string\").and.not.empty;",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Create task checklist",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Launch checklist\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/tasks/{{taskId}}/checklists",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "tasks", "{{taskId}}", "checklists"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Create checklist returns 201\", function () {",
                  "  pm.response.to.have.status(201);",
                  "});",
                  "pm.test(\"Create checklist returns a checklist id\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.checklist_id).to.be.a(\"string\").and.not.empty;",
                  "  pm.collectionVariables.set(\"checklistId\", json.checklist_id);",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Add checklist item",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"content\": \"Review assistant task mutation flow\",\n  \"done\": false\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/tasks/{{taskId}}/checklists/{{checklistId}}/items",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "tasks", "{{taskId}}", "checklists", "{{checklistId}}", "items"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Add checklist item returns 201\", function () {",
                  "  pm.response.to.have.status(201);",
                  "});",
                  "pm.test(\"Add checklist item returns an item id\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.item_id).to.be.a(\"string\").and.not.empty;",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Move task",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"to_column_id\": \"{{targetColumnId}}\",\n  \"position\": 2\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/tasks/{{taskId}}/move",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "tasks", "{{taskId}}", "move"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Move task returns 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "pm.test(\"Move task returns the target column\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.to_column_id).to.eql(pm.variables.get(\"targetColumnId\"));",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Delete task",
          "request": {
            "method": "DELETE",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/tasks/{{taskId}}",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "tasks", "{{taskId}}"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Delete task returns 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Move task with invalid column (should fail)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"to_column_id\": \"{{invalidColumnId}}\",\n  \"position\": 1\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/tasks/{{taskId}}/move",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "tasks", "{{taskId}}", "move"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Move task rejects a non-existent column\", function () {",
                  "  pm.response.to.have.status(400);",
                  "});",
                  "pm.test(\"Invalid column response explains the validation failure\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.error).to.eql(\"invalid to_column_id\");",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "List sprints",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/sprints?board_id={{boardId}}",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "sprints"],
              "query": [
                { "key": "board_id", "value": "{{boardId}}" }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"List sprints returns 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "pm.test(\"Sprints payload is an array\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.sprints).to.be.an(\"array\");",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Search sprints",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/sprints?board_id={{boardId}}&q=release&status=active",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "sprints"],
              "query": [
                { "key": "board_id", "value": "{{boardId}}" },
                { "key": "q", "value": "release" },
                { "key": "status", "value": "active" }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Search sprints returns 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "pm.test(\"Search sprints payload is an array\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.sprints).to.be.an(\"array\");",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Create sprint",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"board_id\": \"{{boardId}}\",\n  \"title\": \"Sprint 9\",\n  \"goal\": \"Finish release automation\",\n  \"start_date\": \"2026-04-20\",\n  \"end_date\": \"2026-05-01\",\n  \"status\": \"planned\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/sprints",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "sprints"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Create sprint returns 201\", function () {",
                  "  pm.response.to.have.status(201);",
                  "});",
                  "pm.test(\"Create sprint returns a sprint id\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.sprint_id).to.be.a(\"string\").and.not.empty;",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Update sprint",
          "request": {
            "method": "PATCH",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"title\": \"Sprint 9 - Release automation\",\n  \"status\": \"active\"\n}"
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/sprints/{{sprintId}}",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "sprints", "{{sprintId}}"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Update sprint returns 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "pm.test(\"Update sprint returns the sprint id\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.sprint_id).to.be.a(\"string\").and.not.empty;",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Delete sprint",
          "request": {
            "method": "DELETE",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/sprints/{{sprintId}}",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "sprints", "{{sprintId}}"]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Delete sprint returns 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "List insights",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/insights?board_id={{boardId}}",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "insights"],
              "query": [
                { "key": "board_id", "value": "{{boardId}}" }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"List insights returns 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "pm.test(\"Insights payload is an array\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.insights).to.be.an(\"array\");",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Get calculated board reports",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/reports/calculated?board_id={{boardId}}&type=all",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "reports", "calculated"],
              "query": [
                { "key": "board_id", "value": "{{boardId}}" },
                { "key": "type", "value": "all" }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Calculated reports return 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "pm.test(\"Calculated reports include report payload\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.report).to.be.an(\"object\");",
                  "  pm.expect(json.report.reports).to.be.an(\"object\");",
                  "  pm.expect(json.report.reports.quality_rework).to.be.an(\"object\");",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Get Scope & Change Report only",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/reports/calculated/scope_change?board_id={{boardId}}",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "reports", "calculated", "scope_change"],
              "query": [
                { "key": "board_id", "value": "{{boardId}}" }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Scope report returns 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "pm.test(\"Scope report is structured\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.report.type).to.eql(\"scope_change\");",
                  "  pm.expect(json.report.data.summary).to.be.an(\"object\");",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Get Quality & Rework Report only",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{apiKey}}" }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/projects/{{projectId}}/reports/calculated/quality_rework?board_id={{boardId}}",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "projects", "{{projectId}}", "reports", "calculated", "quality_rework"],
              "query": [
                { "key": "board_id", "value": "{{boardId}}" }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Quality & Rework report returns 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "pm.test(\"Quality & Rework report is structured\", function () {",
                  "  const json = pm.response.json();",
                  "  pm.expect(json.report.type).to.eql(\"quality_rework\");",
                  "  pm.expect(json.report.data.reopened_tasks).to.be.an(\"object\");",
                  "  pm.expect(json.report.data.column_bounces).to.be.an(\"object\");",
                  "});"
                ]
              }
            }
          ]
        }
      ]
    }
  ]
}
