{
  "info": {
    "name": "Serenize API",
    "description": "The API behind the Serenize member app and the admin portal. Every endpoint the two\nfront ends use is listed here, and nothing else exists.\n\n## Base URL\n\n```\nhttps://serenize-api.217-154-115-96.sslip.io/api\n```\n\nEverything is JSON in and JSON out, including failures. There is no HTML error page to parse.\nCross-origin requests are open, and authentication is a bearer token rather than a cookie, so\nno credentials or preflight configuration are needed on your side.\n\n## Getting a token\n\nThere are two doors, and they hand out the same kind of token:\n\n| Door | Who it is for |\n| --- | --- |\n| `POST /auth/login` | Any member account |\n| `POST /auth/admin/login` | An account whose role is admin. A member is refused here even with the right password |\n\nSend the token on every other call:\n\n```\nAuthorization: Bearer <token>\n```\n\nIn this page press **Authorize** and paste the token. `memberToken` is used by the member endpoints\nand `adminToken` by the admin ones, so you can hold both at once and move between them without\nsigning in again. Both are ordinary bearer tokens, so pasting the same one into both slots is fine.\n\nCredentials for the demo accounts are not published here. They come with the handover notes.\n\nA token lasts thirty days and then expires. Blocking an account revokes every token it holds, and\nsigning out revokes the one that made the call. An expired or revoked token answers `401`, which\nboth apps treat as the end of the session: they sign out locally and put the person back at the door.\n\n## Rate limits\n\nThree ceilings, counted per minute. Going over answers `429` with a `Retry-After` header.\n\n| Where | Limit |\n| --- | --- |\n| `POST /auth/login`, `/auth/admin/login`, `/auth/register` | 10 per address being signed in to, and 60 per caller |\n| Everything behind a member token | 120 per account |\n| Every endpoint, as an overall ceiling | 300 per account, or per caller when signed out |\n\nThe address limit is the tight one, because guessing one account’s password is the attack worth\nstopping, and it holds even if the caller moves address between attempts. The caller limit is loose\non purpose: an office or a mobile network can put a great many honest people behind one address.\n\n## What a refusal means\n\n| Status | Meaning |\n| --- | --- |\n| `401` | No token, or a token that has expired or been revoked. Sign in again |\n| `403` | A real token, but this account is not allowed here. Admin endpoints answer this to a member token, and so does premium content to an account that is not premium |\n| `404` | No such record, or on the publishing routes an unknown entity kind |\n| `422` | The request was understood and refused. Either a field failed validation, or a business rule said no |\n| `423` | The account has been blocked. Blocking also revokes every token the account holds |\n| `429` | Too many requests. Wait for the window in `Retry-After` |\n\nA `422` comes back in one of two shapes. Field validation answers with `errors`, keyed by field name.\nA business rule answers with `problems`, a list of complete sentences meant to be shown to a person:\n\n```json\n{ \"message\": \"This program cannot be deleted. Archive it instead.\",\n  \"problems\": [\"Day \\u201cNotice the signal\\u201d has been published to users at least once.\"] }\n```\n\n## Ids\n\nIds are readable strings rather than numbers, and the first letter says what the thing is:\n`p-` program, `d-` day, `t-` tool, `k-` knowledge, `g-` guided practice, `s-` scenario,\n`r-` reflection, `dp-` deployment, `sr-` saved rule, `n-` notification, `fb-` feedback.\nUser, category and media ids are numbers.\n\n## Four things worth knowing before you test\n\n**Drafts and live versions are separate.** Editing a published item changes the draft and leaves\nwhat users are reading alone until somebody publishes. That is why admin responses carry `pending`,\n`everPublished` and `live`, and why `/bootstrap` and `/admin/bootstrap` can disagree about the same row.\n\n**Missions run in order, and are finished a component at a time.** A Day is refused until every\npublished Day before it in the program is complete. Inside a Day, each component reports itself\nthrough `POST /sessions/step` as it finishes, and `POST /sessions/complete` is checked against those\nrows rather than taking the caller’s word for it. Testing a completion means walking the sequence.\n\n**Entitlement is the server’s, not the client’s.** A program, mission or tool whose access is\n`premium` answers `403` to an account that is not premium, whatever the app shows. `premium` cannot\nbe set on your own account; an administrator sets it through `PATCH /admin/users/{id}`.\n\n**This is the demo database.** Every write you make here changes what the demo shows. There is a\ntest account set aside for exactly this; use it rather than the accounts on the walkthrough.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://serenize-api.217-154-115-96.sslip.io/api",
      "type": "string"
    },
    {
      "key": "email",
      "value": "",
      "type": "string"
    },
    {
      "key": "password",
      "value": "",
      "type": "string"
    },
    {
      "key": "adminEmail",
      "value": "",
      "type": "string"
    },
    {
      "key": "adminPassword",
      "value": "",
      "type": "string"
    },
    {
      "key": "token",
      "value": "",
      "type": "string"
    },
    {
      "key": "adminToken",
      "value": "",
      "type": "string"
    }
  ],
  "item": [
    {
      "name": "Service",
      "description": "One call that needs nothing, for proving you can reach the API.",
      "item": [
        {
          "name": "Is the API up",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/health",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "health"
              ]
            },
            "description": "GET /health\n\nNo token needed. Useful as a first call to prove a client can reach the API at all.\n\nOpen, no token needed.\n\nAnswers: 200.",
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Authentication",
      "description": "Getting a token, and giving it back. Two doors, one kind of token.",
      "item": [
        {
          "name": "Create an account",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auth/register",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auth",
                "register"
              ]
            },
            "description": "POST /auth/register\n\nCreates the account, enrols it in the first published free program, makes that program active and writes a welcome notification. A new account is never premium, so enrolling it in a premium program would hand it a program it is not allowed to open. The token comes back with the account, so there is no need to sign in afterwards.\n\nOpen, no token needed.\n\nAnswers: 201, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Jordan Lee\",\n  \"email\": \"jordan.lee@example.com\",\n  \"password\": \"a-good-password\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "noauth"
            }
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"answers 201\", function () {",
                  "  pm.response.to.have.status(201);",
                  "});",
                  "",
                  "// Nothing else needs touching after this call succeeds.",
                  "if (pm.response.code === 201) {",
                  "  const body = pm.response.json();",
                  "  if (body.token) {",
                  "    pm.collectionVariables.set(\"token\", body.token);",
                  "    console.log(\"saved token\");",
                  "  }",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "Sign in",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auth/login",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auth",
                "login"
              ]
            },
            "description": "POST /auth/login\n\nA wrong address and a wrong password give exactly the same answer, so a stranger cannot learn which accounts exist.\n\nOpen, no token needed.\n\nAnswers: 200, 422, 423.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "noauth"
            }
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"answers 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "",
                  "// Nothing else needs touching after this call succeeds.",
                  "if (pm.response.code === 200) {",
                  "  const body = pm.response.json();",
                  "  if (body.token) {",
                  "    pm.collectionVariables.set(\"token\", body.token);",
                  "    console.log(\"saved token\");",
                  "  }",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "Sign in to the admin portal",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auth/admin/login",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auth",
                "admin",
                "login"
              ]
            },
            "description": "POST /auth/admin/login\n\nThe same credentials check as the member door, plus one more question: the account has to carry the admin role. A member who signs in here is refused after the password has already been accepted, so a wrong guess on an administrator address answers like any other wrong guess.\n\nOpen, no token needed.\n\nAnswers: 200, 403, 422, 423.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{adminEmail}}\",\n  \"password\": \"{{adminPassword}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "noauth"
            }
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"answers 200\", function () {",
                  "  pm.response.to.have.status(200);",
                  "});",
                  "",
                  "// Nothing else needs touching after this call succeeds.",
                  "if (pm.response.code === 200) {",
                  "  const body = pm.response.json();",
                  "  if (body.token) {",
                  "    pm.collectionVariables.set(\"adminToken\", body.token);",
                  "    console.log(\"saved adminToken\");",
                  "  }",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "Confirm an email address",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auth/verify",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auth",
                "verify"
              ]
            },
            "description": "POST /auth/verify\n\nThe token comes from the link in the confirmation email. Open on purpose: mail is usually read on a different device from the one that is signed in, and asking somebody to sign in before confirming the address they may have lost the password to is the wrong way round.\n\nFollowing the same link twice is not an error. The second time answers `already: true`, because that is what happened. A token is bound to the address it was issued for, so changing the address on an account retires every link already sent to the old one without anything having to be cancelled.\n\nConfirmation is not a gate. An account works from the moment it is created; this is what makes a password reset trustworthy later.\n\nOpen, no token needed.\n\nAnswers: 200, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"token\": \"eyJwIjoidmVyaWZ5LWVtYWlsIiwidSI6MX0.Ab3d\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        },
        {
          "name": "Ask for a password reset link",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auth/password/forgot",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auth",
                "password",
                "forgot"
              ]
            },
            "description": "POST /auth/password/forgot\n\nAlways answers the same way, whatever is behind it. Saying \"no account here\" would turn this into a way of asking which addresses are registered, which is the same reason a wrong address and a wrong password are one answer at sign-in.\n\nThree kinds of account are sent nothing and answer exactly like the rest: one that does not exist, one that has been blocked, and a demonstration account, whose password is published in this documentation on purpose.\n\nOpen, no token needed.\n\nAnswers: 200.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"you@example.com\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        },
        {
          "name": "Set a new password from a reset link",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auth/password/reset",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auth",
                "password",
                "reset"
              ]
            },
            "description": "POST /auth/password/reset\n\nThe token comes from the link in the reset email and is good for one hour and one use. The row behind it is deleted whether or not it was still in date, so pressing the same button in the same email twice says so plainly rather than working twice.\n\nEvery token the account holds is revoked as well. Somebody resetting a password may be doing it because a session is in the wrong hands, and leaving those alive is the one thing that would make the reset pointless. Reading mail at the address is also the proof a confirmation link asks for, so an unconfirmed account is confirmed by getting here.\n\nThe account is not signed in afterwards. The new password is used at the door like any other.\n\nOpen, no token needed.\n\nAnswers: 200, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"token\": \"a-64-character-token-from-the-email\",\n  \"password\": \"a-good-new-password\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        },
        {
          "name": "Sign out",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auth/logout",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auth",
                "logout"
              ]
            },
            "description": "POST /auth/logout\n\nDeletes the token that made this call. Other devices signed in to the same account keep their own tokens.\n\nNeeds a signed-in token.\n\nAnswers: 200.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Send the confirmation email again",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auth/verify/resend",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auth",
                "verify",
                "resend"
              ]
            },
            "description": "POST /auth/verify/resend\n\nFor the message that never arrived. An account whose address is already confirmed is not sent another and says so, which is not an error.\n\nNeeds a signed-in token.\n\nAnswers: 200.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Who this token belongs to",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auth/me",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auth",
                "me"
              ]
            },
            "description": "GET /auth/me\n\nThe account block on its own, without the catalogue that /bootstrap carries.\n\nNeeds a signed-in token.\n\nAnswers: 200.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "The store",
      "description": "The two calls that carry a whole screen of state at once.",
      "item": [
        {
          "name": "The whole client state in one call",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/bootstrap",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "bootstrap"
              ]
            },
            "description": "GET /bootstrap\n\nThe single call the app is built on. It replaces its in-memory copy with whatever comes back, so this response is the contract for the entire product. Everything in it is the published view: drafts never appear here.\n\nIt is one member’s own view. The feedback and the activity in it are that member’s, and the `insights` block carries the shape the app reads through with every figure zeroed. Workspace analytics are an administrator’s and belong to `GET /admin/bootstrap`.\n\nNeeds a signed-in token.\n\nAnswers: 200.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "The published shelf, no account needed",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/catalogue",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "catalogue"
              ]
            },
            "description": "GET /catalogue\n\nPrograms, tools, categories and moments, published only. This is what lets the marketing site show what the product contains without anybody signing in.\n\nA shelf rather than a library: names, descriptions and cover art. Media paths are not included, for free or premium content alike, because a caller with no account has no business holding a direct link to a file a subscription is meant to stand in front of, and a marketing page never plays one. Sign in and read `GET /bootstrap` for those.\n\nOpen, no token needed.\n\nAnswers: 200.",
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Profile and settings",
      "description": "Who the person is and how they want the app to behave.",
      "item": [
        {
          "name": "Change a setting",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/me",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "me"
              ]
            },
            "description": "PATCH /me\n\nSettings arrive as whatever the user just changed, one switch at a time, so every field is optional and only what is sent is written. `premium` is deliberately not among them: entitlement is granted by an administrator through `PATCH /admin/users/{id}`, or by the payment workflow once one is connected, and never by the account itself. Sending it here is ignored.\n\nNeeds a signed-in token.\n\nAnswers: 200, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"theme\": \"light\",\n  \"reminders\": {\n    \"enabled\": true,\n    \"time\": \"07:30\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Close this account",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/me",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "me"
              ]
            },
            "description": "DELETE /me\n\nSelf-service account deletion. Until this existed only an administrator could remove an account, which is a reasonable answer for a demo and the wrong one for a product people trust with what they have written down.\n\nThe current password is required, because a token left open on a borrowed phone must not be enough to erase somebody. Everything goes with the account: reflections, commitments, saved rules, sessions and feedback. Enrolment counts on each program are corrected first, so the headline figures do not keep counting somebody who has gone. It cannot be undone.\n\nAn administrator account is closed by another administrator, so that a workspace cannot be left with nobody able to run it. Demonstration accounts cannot be closed at all, and the two accounts the walkthrough signs in as are among them.\n\nNeeds a signed-in token.\n\nAnswers: 200, 403, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"current_password\": \"the-password-on-this-account\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Join a program",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/me/enrol",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "me",
                "enrol"
              ]
            },
            "description": "POST /me/enrol\n\nDoes not disturb a mission the user is part way through. Enrolling twice is not an error; the second time simply does nothing. A program whose access is premium is refused to an account that is not.\n\nNeeds a signed-in token.\n\nAnswers: 200, 403, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"program\": \"p-focus\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Switch the active program",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/me/active-program",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "me",
                "active-program"
              ]
            },
            "description": "POST /me/active-program\n\nEnrols the user first if they are not already in it, and refuses a premium program to an account that is not premium.\n\nNeeds a signed-in token.\n\nAnswers: 200, 403, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"program\": \"p-calm\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Mark a tool explainer as shown",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/me/tool-seen",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "me",
                "tool-seen"
              ]
            },
            "description": "POST /me/tool-seen\n\nThe first-run explainer on a utility tool is offered once and then stays available from inside the tool.\n\nNeeds a signed-in token.\n\nAnswers: 200, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"tool\": \"t-reset\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Missions",
      "description": "A mission from the moment it is opened to the moment it is finished. Every attempt is recorded whether or not it is completed, which is what makes the completion rate mean something.",
      "item": [
        {
          "name": "Open a mission",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/sessions/start",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "sessions",
                "start"
              ]
            },
            "description": "POST /sessions/start\n\nLeaving a mission and coming back is one attempt, so an attempt already open today is handed back rather than a second one being started. An attempt left open on an earlier day is closed first, because that attempt belongs to the day it was opened on. Every attempt lands in the session log whether or not it is finished, which is what the completion rate is read from.\n\nThe sequence is a sequence: a Day is refused until every published Day before it in the program has been completed. A Day already completed stays open, because replaying one is allowed. Days that are still drafts do not block progression, since they cannot be completed.\n\nNeeds a signed-in token.\n\nAnswers: 200, 403, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"day\": \"d-c1\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Report a component finished",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/sessions/step",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "sessions",
                "step"
              ]
            },
            "description": "POST /sessions/step\n\nComponent-level progress, reported as each part of the sequence is finished. This is what `POST /sessions/complete` is measured against, so a mission cannot be reported finished without its substance having been done.\n\n`step` is the position in the published sequence, counting from zero. The component's type is read from that sequence rather than taken from the request, so a caller cannot claim to have finished something it did not. Reporting the same component twice is not an error and keeps the first timestamp.\n\nA component that was skipped should not be reported. Scenario and Reflection are optional by design and may be skipped freely.\n\nNeeds a signed-in token.\n\nAnswers: 200, 403, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"day\": \"d-c1\",\n  \"step\": 0\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Finish a mission",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/sessions/complete",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "sessions",
                "complete"
              ]
            },
            "description": "POST /sessions/complete\n\nThe minutes credited are the published length of the Day, never a number the client sends. Finishing a mission a second time is another session and more minutes, but it is not another Day completed.\n\nThis is also where the streak moves. It counts the calendar days a mission was finished on, so opening one and walking away leaves it where it was.\n\nChecked against the components reported through `POST /sessions/step`. A Day is finished when its Introduction and its Completion Message have been reported, along with at least one Knowledge or Guided Practice, which is the same bar the Day had to meet to be publishable at all. Scenario and Reflection are optional and are not required here.\n\nNeeds a signed-in token.\n\nAnswers: 200, 403, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"day\": \"d-c1\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Walk away from a mission",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/sessions/abandon",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "sessions",
                "abandon"
              ]
            },
            "description": "POST /sessions/abandon\n\nThe attempt closes unfinished and stays on the record, which is what makes the completion rate honest. Abandoning a replay never undoes a completion already earned.\n\nNeeds a signed-in token.\n\nAnswers: 200.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"day\": \"d-c1\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Save a play head",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/resume",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "resume"
              ]
            },
            "description": "POST /resume\n\nWhere the user stopped inside a piece of media. Stored as whole seconds.\n\nThe key is the id of the thing being played, a Content Library item or a utility tool, and has to name one that exists. It was once free text of any shape with no limit on how many an account could hold, which made this a small store anybody with a token could write into. There is now a ceiling of 500 per account, comfortably more than a whole library.\n\nNeeds a signed-in token.\n\nAnswers: 200, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"key\": \"g-anchor\",\n  \"value\": 42\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Clear a play head",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/resume",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "resume"
              ]
            },
            "description": "DELETE /resume\n\nTakes a JSON body rather than a path parameter, because the key identifies the piece rather than a record of its own.\n\nNeeds a signed-in token.\n\nAnswers: 200.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"key\": \"g-anchor\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Answers",
      "description": "What the user wrote or chose inside a mission. Saving a Reflection with the deploy switch on is the only thing in the API that records a Real-world Deployment.",
      "item": [
        {
          "name": "Save a Reflection answer",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/reflections",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "reflections"
              ]
            },
            "description": "POST /reflections\n\nThe only thing in the whole API that records a Real-world Deployment. Whether it does is read off the Reflection itself, from the published version the user actually answered under, so the client never gets to say whether something counted. Editing an answer edits the commitment; it never records a second one, and the original date stands. The character limit is the editorial setting on the item, enforced here rather than trusted to the textarea that collected the answer.\n\nNeeds a signed-in token.\n\nAnswers: 200, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"content\": \"r-carry\",\n  \"day\": \"d-c1\",\n  \"text\": \"Before the stand-up I will take one slow breath and name the signal.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Answer a Scenario",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/scenarios",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "scenarios"
              ]
            },
            "description": "POST /scenarios\n\nA single-choice Scenario answers with the option index; a written one answers with text. Whether the answer was the recommended one is decided against the options the user was actually shown, which is the published version of the item. Changing your mind overwrites the answer rather than counting as a second scenario answered.\n\nNeeds a signed-in token.\n\nAnswers: 200, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"content\": \"s-interrupt\",\n  \"day\": \"d-c1\",\n  \"choice\": 0\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Real-world Deployments",
      "description": "The commitments behind the headline number. Read, remove and put back only; nothing here creates one.",
      "item": [
        {
          "name": "List commitments",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/deployments",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "deployments"
              ]
            },
            "description": "GET /deployments\n\nNewest first, which is the order the app renders. These are the rows the Real-world Deployments figure stands on.\n\nNeeds a signed-in token.\n\nAnswers: 200.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Remove a commitment",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/deployments/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "deployments",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "dp-1",
                  "description": "The deployment id."
                }
              ]
            },
            "description": "DELETE /deployments/{id}\n\nThe list position comes back with it, so the app can offer Undo without guessing where the row was.\n\nNeeds a signed-in token.\n\nAnswers: 200, 404.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Undo a removal",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/deployments/:id/restore",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "deployments",
                ":id",
                "restore"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "dp-1",
                  "description": "The id of the commitment that was removed."
                }
              ]
            },
            "description": "POST /deployments/{id}/restore\n\nPuts back a commitment this account removed, and nothing else. Removal is a tombstone rather than a delete, so this restores the row that was hidden instead of rebuilding one from what the client remembered; asking to restore an id that was never removed is refused. That keeps saving a deploy Reflection the only thing that can record a commitment. The row keeps the date it was originally made.\n\nThe body is optional and holds only the text, because the answer may have been edited between the removal and the undo. Pressing undo twice is not an error.\n\nNeeds a signed-in token.\n\nAnswers: 200, 404.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"text\": \"Before the stand-up I will take one slow breath.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Field rules",
      "description": "The one-line rules a user keeps from a mission or a tool.",
      "item": [
        {
          "name": "List saved rules",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/rules",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "rules"
              ]
            },
            "description": "GET /rules\n\nNeeds a signed-in token.\n\nAnswers: 200.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Save a rule",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/rules",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "rules"
              ]
            },
            "description": "POST /rules\n\nThe metric counts distinct rules per person, so saving the same wording twice hands back the rule already held instead of adding a second row. When the rule came from a mission or a tool the server writes the source wording itself rather than taking the client’s word for which Day the user was on.\n\nNeeds a signed-in token.\n\nAnswers: 200, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"text\": \"Name the signal before you answer it.\",\n  \"day\": \"d-c1\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Delete a rule",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/rules/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "rules",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sr-1",
                  "description": "The rule id."
                }
              ]
            },
            "description": "DELETE /rules/{id}\n\nNeeds a signed-in token.\n\nAnswers: 200, 404.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Utility tools",
      "description": "The short standalone exercises, counted separately from training.",
      "item": [
        {
          "name": "Record a tool use",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/tools/:tool/use",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "tools",
                ":tool",
                "use"
              ],
              "variable": [
                {
                  "key": "tool",
                  "value": "t-reset",
                  "description": "The tool id."
                }
              ]
            },
            "description": "POST /tools/{tool}/use\n\nTool activity is counted on its own and never folded into the session metrics: using a tool does not complete a mission and does not extend a streak. A tool that is not published is not on the shelf, so a use of one is refused, and a tool whose access is premium is refused to an account that is not.\n\nNeeds a signed-in token.\n\nAnswers: 200, 403, 404.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Feedback and notifications",
      "description": "What users tell you, and what you tell them.",
      "item": [
        {
          "name": "Send feedback",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/feedback",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "feedback"
              ]
            },
            "description": "POST /feedback\n\nThe context it arrives with is what makes it useful, so the program, day, content item and version travel with it.\n\nThat context is checked rather than taken at its word, because all four values come from the client and a response naming a Day and an item that have nothing to do with each other is worth reading rather than worth trusting. The Day has to be published, the program is taken from the Day rather than from the request, and a content item counts only if the Day being reported on actually plays it. A version later than the one that exists is pulled back, since nobody has played the future.\n\nAnything that does not hold together is dropped rather than refused. The comment is the part worth keeping, and a response with the wrong label on it is still better than no response.\n\nNeeds a signed-in token.\n\nAnswers: 200, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"kind\": \"session\",\n  \"rating\": \"up\",\n  \"chips\": [\n    \"Clear\",\n    \"Right length\"\n  ],\n  \"comment\": \"The guided practice landed well.\",\n  \"day\": \"d-c1\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "The bell",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/notifications",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "notifications"
              ]
            },
            "description": "GET /notifications\n\nScoped to the token that asked, so read state belongs to that account alone. Anything meant for everybody is fanned out into a row each when it is written, because a shared row would have nowhere to record that this person has read it.\n\nNeeds a signed-in token.\n\nAnswers: 200.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Mark one as read",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/notifications/:id/read",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "notifications",
                ":id",
                "read"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "n-1",
                  "description": "The notification id."
                }
              ]
            },
            "description": "POST /notifications/{id}/read\n\nNeeds a signed-in token.\n\nAnswers: 200, 404.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Clear the badge",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/notifications/read-all",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "notifications",
                "read-all"
              ]
            },
            "description": "POST /notifications/read-all\n\nOpening the bell clears it, so this is one call rather than a loop.\n\nNeeds a signed-in token.\n\nAnswers: 200.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{token}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Admin: the store",
      "description": "The portal twin of /bootstrap, carrying drafts as well as what is live.",
      "item": [
        {
          "name": "Everything the portal edits",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/bootstrap",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "bootstrap"
              ]
            },
            "description": "GET /admin/bootstrap\n\nThe admin twin of /bootstrap. Same envelope, but carrying the rows themselves rather than their published snapshots, so the portal can edit a draft and still show what users are reading in the meantime. Every entity therefore also carries pending, everPublished and live.\n\nNeeds an admin token.\n\nAnswers: 200.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Admin: programs",
      "description": "Programs and the running order of their days.",
      "item": [
        {
          "name": "List programs",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/programs",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "programs"
              ]
            },
            "description": "GET /admin/programs\n\nDrafts and archived included, in display order.\n\nNeeds an admin token.\n\nAnswers: 200.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Create a program",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/programs",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "programs"
              ]
            },
            "description": "POST /admin/programs\n\nArrives as a draft with its Days already generated. The count is a release decision made once, up front, because adding the eighth Day later is a different action from designing an eight day program. Send days as a number to generate that many (5 to 14, default 7), or as an array of ids the caller has already built.\n\nNeeds an admin token.\n\nAnswers: 201, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Steady Hands\",\n  \"desc\": \"Composure when the stakes are visible.\",\n  \"access\": \"premium\",\n  \"days\": 7\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "One program",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/programs/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "programs",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "p-calm",
                  "description": "The program id."
                }
              ]
            },
            "description": "GET /admin/programs/{id}\n\nNeeds an admin token.\n\nAnswers: 200, 404.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Edit a program",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/programs/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "programs",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "p-calm",
                  "description": "The program id."
                }
              ]
            },
            "description": "PATCH /admin/programs/{id}\n\nDays are created and deleted through their own endpoints, so sending days here may only ever reorder the list the program already holds.\n\nNeeds an admin token.\n\nAnswers: 200, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Calm Under Pressure\",\n  \"access\": \"premium\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Delete a program",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/programs/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "programs",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "p-calm",
                  "description": "The program id."
                }
              ]
            },
            "description": "DELETE /admin/programs/{id}\n\nRefused if the program or any Day it holds has ever reached users. Those are archived instead. A successful delete takes the program’s Days with it.\n\nNeeds an admin token.\n\nAnswers: 200, 422.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Reorder the days",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/programs/:id/reorder-days",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "programs",
                ":id",
                "reorder-days"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "p-calm",
                  "description": "The program id."
                }
              ]
            },
            "description": "POST /admin/programs/{id}/reorder-days\n\nThe new order has to hold exactly the Days the program already has. Anything else is a lost or duplicated Day arriving disguised as a sort.\n\nNeeds an admin token.\n\nAnswers: 200, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"days\": [\n    \"d-c2\",\n    \"d-c1\",\n    \"d-c3\"\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Admin: days",
      "description": "A mission and the components it plays, in order.",
      "item": [
        {
          "name": "List days",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/days?program=p-calm",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "days"
              ],
              "query": [
                {
                  "key": "program",
                  "value": "p-calm",
                  "description": "Limit to one program.",
                  "disabled": true
                }
              ]
            },
            "description": "GET /admin/days\n\nOrdered by the position each Day’s program gives it; the row order in the table means nothing to the product.\n\nNeeds an admin token.\n\nAnswers: 200.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Create a day",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/days",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "days"
              ]
            },
            "description": "POST /admin/days\n\nA new Day arrives with its two framing components already in place, so it is never invalid for a reason the author did not choose. The server decides what a component is: a step is stripped back to the keys its own type uses before it is stored.\n\nNeeds an admin token.\n\nAnswers: 201, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"program\": \"p-calm\",\n  \"title\": \"Day 9 Mission\",\n  \"obj\": \"Hold the line when the room turns.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "One day",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/days/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "days",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "d-c1",
                  "description": "The day id."
                }
              ]
            },
            "description": "GET /admin/days/{id}\n\nNeeds an admin token.\n\nAnswers: 200, 404.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Edit a day",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/days/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "days",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "d-c1",
                  "description": "The day id."
                }
              ]
            },
            "description": "PATCH /admin/days/{id}\n\nNeeds an admin token.\n\nAnswers: 200, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"title\": \"Notice the signal\",\n  \"rule\": \"Name it before you answer it.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Delete a day",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/days/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "days",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "d-c1",
                  "description": "The day id."
                }
              ]
            },
            "description": "DELETE /admin/days/{id}\n\nRefused if the Day has ever reached users. The program that lists it drops the reference at the same time.\n\nNeeds an admin token.\n\nAnswers: 200, 422.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Admin: content library",
      "description": "Knowledge, guided practice, scenarios and reflections. Which fields an item has depends on its type, and the server enforces that.",
      "item": [
        {
          "name": "Search the library",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/content?type=&q=&status=&program=p-calm&sort=&page=&per_page=",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "content"
              ],
              "query": [
                {
                  "key": "type",
                  "value": "",
                  "description": "Defaults to all.",
                  "disabled": true
                },
                {
                  "key": "q",
                  "value": "",
                  "description": "Matches the title and the body: a Knowledge description and takeaway, a Guided instruction and completion message, a Scenario situation and question, and the text of every answer option along with its feedback line.",
                  "disabled": true
                },
                {
                  "key": "status",
                  "value": "",
                  "description": "Defaults to all.",
                  "disabled": true
                },
                {
                  "key": "program",
                  "value": "p-calm",
                  "description": "Only items some Day of this program plays.",
                  "disabled": true
                },
                {
                  "key": "sort",
                  "value": "",
                  "description": "Defaults to name.",
                  "disabled": true
                },
                {
                  "key": "page",
                  "value": "",
                  "description": "From 1.",
                  "disabled": true
                },
                {
                  "key": "per_page",
                  "value": "",
                  "description": "1 to 100. Defaults to 8.",
                  "disabled": true
                }
              ]
            },
            "description": "GET /admin/content\n\nPaged. Sorting by recent means recently updated, which only the server knows.\n\nNeeds an admin token.\n\nAnswers: 200.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Create a library item",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/content",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "content"
              ]
            },
            "description": "POST /admin/content\n\nCreates a blank draft of the chosen type, with the fields that type uses already present and empty. Fill them in with a PATCH afterwards, which is the order the admin screens work in too.\n\nNeeds an admin token.\n\nAnswers: 201, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"type\": \"knowledge\",\n  \"title\": \"What pressure does to attention\",\n  \"dur\": 4\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "One library item",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/content/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "content",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "k-curve",
                  "description": "The content id."
                }
              ]
            },
            "description": "GET /admin/content/{id}\n\nNeeds an admin token.\n\nAnswers: 200, 404.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Edit a library item",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/content/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "content",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "k-curve",
                  "description": "The content id."
                }
              ]
            },
            "description": "PATCH /admin/content/{id}\n\nFields that mean nothing for this item’s type are dropped whatever the request says, so a Reflection can never grow scenario options and a Knowledge item can never be made to record a Deployment. At most one option can be the recommended answer.\n\nNeeds an admin token.\n\nAnswers: 200, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"desc\": \"Attention narrows under load, and the narrowing is what you feel first.\",\n  \"takeaway\": \"Narrowing is information, not failure.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Delete a library item",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/content/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "content",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "k-curve",
                  "description": "The content id."
                }
              ]
            },
            "description": "DELETE /admin/content/{id}\n\nRefused if it has ever reached users. A successful delete strips the reference out of every draft sequence that pointed at it, because a sequence pointing at a row that no longer exists would read as a broken reference forever with nobody able to tell which item it was. Published snapshots are deliberately left alone: what users are reading only changes when somebody publishes.\n\nNeeds an admin token.\n\nAnswers: 200, 422.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Where an item is played",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/content/:id/used-in",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "content",
                ":id",
                "used-in"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "k-curve",
                  "description": "The content id."
                }
              ]
            },
            "description": "GET /admin/content/{id}/used-in\n\nEvery program and Day that references it, so an editor can see what an edit would reach before making it.\n\nNeeds an admin token.\n\nAnswers: 200.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Admin: utility tools",
      "description": "The tools on the shelf, and the order they sit in.",
      "item": [
        {
          "name": "List tools",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/tools",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "tools"
              ]
            },
            "description": "GET /admin/tools\n\nDrafts included, in the order the app shows them.\n\nNeeds an admin token.\n\nAnswers: 200.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Create a tool",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/tools",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "tools"
              ]
            },
            "description": "POST /admin/tools\n\nNeeds an admin token.\n\nAnswers: 201, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Shoulder Drop\",\n  \"purpose\": \"Release held tension between meetings\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "One tool",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/tools/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "tools",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "t-reset",
                  "description": "The tool id."
                }
              ]
            },
            "description": "GET /admin/tools/{id}\n\nNeeds an admin token.\n\nAnswers: 200, 404.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Edit a tool",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/tools/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "tools",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "t-reset",
                  "description": "The tool id."
                }
              ]
            },
            "description": "PATCH /admin/tools/{id}\n\nDisplay order comes from the list and the use counter from what people actually did, so neither is the editor’s to send.\n\nNeeds an admin token.\n\nAnswers: 200, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"purpose\": \"Drop your shoulders and reset in ninety seconds\",\n  \"dur\": 1.5\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Delete a tool",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/tools/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "tools",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "t-reset",
                  "description": "The tool id."
                }
              ]
            },
            "description": "DELETE /admin/tools/{id}\n\nRefused if it has ever reached users. A successful delete closes the gap in the ordering so the positions stay 1 to n.\n\nNeeds an admin token.\n\nAnswers: 200, 422.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Reorder the tools",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/tools-reorder",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "tools-reorder"
              ]
            },
            "description": "POST /admin/tools-reorder\n\nPosition is presentation rather than content, so moving a published tool is not a draft revision and never marks it as having unpublished changes. The new order has to hold exactly the tools that exist.\n\nNeeds an admin token.\n\nAnswers: 200, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"tools\": [\n    \"t-energy\",\n    \"t-reset\",\n    \"t-mind\",\n    \"t-lock\"\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Admin: publishing",
      "description": "One engine for all four entity kinds. Every route here takes a {kind} of programs, days, content or tools.",
      "item": [
        {
          "name": "Publish",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/:kind/:id/publish",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                ":kind",
                ":id",
                "publish"
              ],
              "variable": [
                {
                  "key": "kind",
                  "value": "days",
                  "description": "One of programs, days, content or tools."
                },
                {
                  "key": "id",
                  "value": "d-c1",
                  "description": "The entity id."
                }
              ]
            },
            "description": "POST /admin/{kind}/{id}/publish\n\nOne publishing engine for all four entity kinds. Publishing revalidates on the server, so a client that skips its own checks still cannot push a broken Day. A first publication and a versioned update are told apart by whether a live snapshot already exists.\n\nNeeds an admin token.\n\nAnswers: 200, 404, 422.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Take it off the air",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/:kind/:id/unpublish",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                ":kind",
                ":id",
                "unpublish"
              ],
              "variable": [
                {
                  "key": "kind",
                  "value": "days",
                  "description": "One of programs, days, content or tools."
                },
                {
                  "key": "id",
                  "value": "d-c1",
                  "description": "The entity id."
                }
              ]
            },
            "description": "POST /admin/{kind}/{id}/unpublish\n\nReports the impact it had on the way out, because by the time the portal re-renders the evidence of what users lost is already gone. Content reports the live days that played it, a program reports its enrolment, a day reports its program, a tool reports its use count.\n\nNeeds an admin token.\n\nAnswers: 200, 404, 422.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Throw away unpublished edits",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/:kind/:id/discard",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                ":kind",
                ":id",
                "discard"
              ],
              "variable": [
                {
                  "key": "kind",
                  "value": "content",
                  "description": "One of programs, days, content or tools."
                },
                {
                  "key": "id",
                  "value": "k-curve",
                  "description": "The entity id."
                }
              ]
            },
            "description": "POST /admin/{kind}/{id}/discard\n\nReturns the draft to the live version. Only possible when there is a live version to return to.\n\nNeeds an admin token.\n\nAnswers: 200, 404, 422.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Archive",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/:kind/:id/archive",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                ":kind",
                ":id",
                "archive"
              ],
              "variable": [
                {
                  "key": "kind",
                  "value": "content",
                  "description": "One of programs, days, content or tools."
                },
                {
                  "key": "id",
                  "value": "k-curve",
                  "description": "The entity id."
                }
              ]
            },
            "description": "POST /admin/{kind}/{id}/archive\n\nHides an entity from new assignments. It deliberately leaves the version history, the live snapshot and the pending flag alone, so a restore puts the editor back exactly where they were.\n\nNeeds an admin token.\n\nAnswers: 200, 404.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Bring it back from the archive",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/:kind/:id/restore",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                ":kind",
                ":id",
                "restore"
              ],
              "variable": [
                {
                  "key": "kind",
                  "value": "content",
                  "description": "One of programs, days, content or tools."
                },
                {
                  "key": "id",
                  "value": "k-curve",
                  "description": "The entity id."
                }
              ]
            },
            "description": "POST /admin/{kind}/{id}/restore\n\nComes back as a draft, never straight back onto the air.\n\nNeeds an admin token.\n\nAnswers: 200, 404, 422.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "What would change if this were published",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/:kind/:id/diff",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                ":kind",
                ":id",
                "diff"
              ],
              "variable": [
                {
                  "key": "kind",
                  "value": "days",
                  "description": "One of programs, days, content or tools."
                },
                {
                  "key": "id",
                  "value": "d-c1",
                  "description": "The entity id."
                }
              ]
            },
            "description": "GET /admin/{kind}/{id}/diff\n\nThe draft measured against the live snapshot, field by field.\n\nNeeds an admin token.\n\nAnswers: 200, 404.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Ask what is wrong with it",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/:kind/:id/validate",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                ":kind",
                ":id",
                "validate"
              ],
              "variable": [
                {
                  "key": "kind",
                  "value": "days",
                  "description": "One of programs, days, content or tools."
                },
                {
                  "key": "id",
                  "value": "d-c1",
                  "description": "The entity id."
                }
              ]
            },
            "description": "GET /admin/{kind}/{id}/validate\n\nThe same checks publishing runs, without publishing. Problems block publication; advisories and gaps are worth knowing but do not. Blockers are the reasons it could not be deleted.\n\nNeeds an admin token.\n\nAnswers: 200, 404.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Admin: insights",
      "description": "The figures, the table behind them, what users said and what the workspace did.",
      "item": [
        {
          "name": "The insights screen",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/insights?program=p-calm",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "insights"
              ],
              "query": [
                {
                  "key": "program",
                  "value": "p-calm",
                  "description": "Scope to one program. \"all\" or an unknown id means the whole workspace.",
                  "disabled": true
                }
              ]
            },
            "description": "GET /admin/insights\n\nHeadline figures with a change against the previous 30 days, the status mix, completion by program and the retention curve. An unknown or absent program reports the whole workspace.\n\nNeeds an admin token.\n\nAnswers: 200.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "The table behind the charts",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/insights/report",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "insights",
                "report"
              ]
            },
            "description": "GET /admin/insights/report\n\nAlways covers every program, drafts included, because \"no enrolments yet\" is itself the answer for anything that has not shipped.\n\nNeeds an admin token.\n\nAnswers: 200.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "What users said",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/feedback?filter=&user=1&device_os=&app_platform=&platform=",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "feedback"
              ],
              "query": [
                {
                  "key": "filter",
                  "value": "",
                  "description": "Defaults to all.",
                  "disabled": true
                },
                {
                  "key": "user",
                  "value": "1",
                  "description": "Narrow to one account. This is the whole list behind the ten shown on a profile.",
                  "disabled": true
                },
                {
                  "key": "device_os",
                  "value": "",
                  "description": "The device family the response was written on, not the one the account is on now.",
                  "disabled": true
                },
                {
                  "key": "app_platform",
                  "value": "",
                  "description": "The way in rather than the device. Phase one is a web app, so every response answers web today; the field exists for the native builds.",
                  "disabled": true
                },
                {
                  "key": "platform",
                  "value": "",
                  "description": "The older spelling of device_os, kept working for existing callers. Prefer device_os.",
                  "disabled": true
                }
              ]
            },
            "description": "GET /admin/feedback\n\nEvery response names the account that left it and the platform it was written on, so a complaint about a particular scenario can be traced to the person who made it and the device they made it from. The totals report the whole picture whatever the list is filtered to.\n\nNeeds an admin token.\n\nAnswers: 200.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "The activity log",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/events?limit=40",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "events"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "40",
                  "description": "1 to 200. Defaults to 40.",
                  "disabled": true
                }
              ]
            },
            "description": "GET /admin/events\n\nNeeds an admin token.\n\nAnswers: 200.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Admin: users",
      "description": "People management. Content is versioned and reversible; an account is neither, so every destructive action here is guarded on the server.",
      "item": [
        {
          "name": "Download the current filter as a CSV",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/users/export?q=&role=&status=&device_os=&app_platform=&platform=&include_seed=",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "users",
                "export"
              ],
              "query": [
                {
                  "key": "q",
                  "value": "",
                  "description": "Match on name or email.",
                  "disabled": true
                },
                {
                  "key": "role",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "status",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "device_os",
                  "value": "",
                  "description": "Device family.",
                  "disabled": true
                },
                {
                  "key": "app_platform",
                  "value": "",
                  "description": "The way in rather than the device.",
                  "disabled": true
                },
                {
                  "key": "platform",
                  "value": "",
                  "description": "The older spelling of device_os.",
                  "disabled": true
                },
                {
                  "key": "include_seed",
                  "value": "",
                  "description": "Include the synthetic population. Off by default.",
                  "disabled": true
                }
              ]
            },
            "description": "GET /admin/users/export\n\nTakes the same filters as the list, and shares its query builder, so a CSV always holds exactly the rows that were on screen when it was asked for. Answers with a file, not JSON.\n\nNeeds an admin token.\n\nAnswers: 200, 422.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "List people",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/users?q=&role=&status=&device_os=&app_platform=&platform=&sort=&include_seed=&page=&per_page=",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "users"
              ],
              "query": [
                {
                  "key": "q",
                  "value": "",
                  "description": "Match on name or email.",
                  "disabled": true
                },
                {
                  "key": "role",
                  "value": "",
                  "description": "Defaults to all.",
                  "disabled": true
                },
                {
                  "key": "status",
                  "value": "",
                  "description": "Defaults to all.",
                  "disabled": true
                },
                {
                  "key": "device_os",
                  "value": "",
                  "description": "Device family, which is the question the Users screen asks. \"unknown\" means never seen since recording began, which is its own answer rather than a value to hide.",
                  "disabled": true
                },
                {
                  "key": "app_platform",
                  "value": "",
                  "description": "The way in rather than the device. Phase one is a web app, so every account answers web today and filtering on it returns everyone; the field exists for the native builds, which will identify themselves.",
                  "disabled": true
                },
                {
                  "key": "platform",
                  "value": "",
                  "description": "The older spelling of device_os, kept working for existing callers. Prefer device_os.",
                  "disabled": true
                },
                {
                  "key": "sort",
                  "value": "",
                  "description": "Defaults to recent.",
                  "disabled": true
                },
                {
                  "key": "include_seed",
                  "value": "",
                  "description": "Include the synthetic population. Off by default.",
                  "disabled": true
                },
                {
                  "key": "page",
                  "value": "",
                  "description": "From 1.",
                  "disabled": true
                },
                {
                  "key": "per_page",
                  "value": "",
                  "description": "1 to 100. Defaults to 25.",
                  "disabled": true
                }
              ]
            },
            "description": "GET /admin/users\n\nReal sign-ups come first whatever else the sort says, because the synthetic population that gives the analytics their shape is not what an administrator is looking for. The summary tiles count real accounts only; the seeded figure is reported separately.\n\nNeeds an admin token.\n\nAnswers: 200.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "One person in full",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/users/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "users",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "1",
                  "description": "The numeric user id."
                }
              ]
            },
            "description": "GET /admin/users/{id}\n\nWho they are, what they are enrolled in and what they have actually done. An administrator deciding whether to block somebody should not have to guess at any of it.\n\nNeeds an admin token.\n\nAnswers: 200, 404.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Change a role, grant premium, block or unblock",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/users/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "users",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "1",
                  "description": "The numeric user id."
                }
              ]
            },
            "description": "PATCH /admin/users/{id}\n\nBlocking records the date rather than a flag, so the account can answer since when. It also revokes every token the account holds, because a block that leaves the existing session alive is not a block. Three guards apply and are enforced here rather than hidden in the client: an administrator cannot remove their own role, cannot block themselves, and no seeded account can be edited at all.\n\nNeeds an admin token.\n\nAnswers: 200, 404, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"blocked\": true,\n  \"reason\": \"Abusive language in feedback\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Set somebody’s password",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/users/:id/password",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "users",
                ":id",
                "password"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "1",
                  "description": "The numeric user id."
                }
              ]
            },
            "description": "POST /admin/users/{id}/password\n\nThere is no mail transport on this deployment, so a reset link would go nowhere. The administrator sets the password and passes it on. Every open session on that account ends, so an unknown holder of the old one cannot stay in. Seeded accounts are refused.\n\nNeeds an admin token.\n\nAnswers: 200, 404, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"password\": \"a-new-password\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Delete an account",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/users/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "users",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "1",
                  "description": "The numeric user id."
                }
              ]
            },
            "description": "DELETE /admin/users/{id}\n\nTakes everything personal with it: sessions, answers, commitments and rules all cascade. Feedback and activity rows are detached rather than deleted, so the person goes and the aggregate history stays honest. Enrolment counts on each program are decremented at the same time. An administrator cannot delete their own account, and seeded accounts are refused.\n\nNeeds an admin token.\n\nAnswers: 200, 404, 422.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Admin: media",
      "description": "Uploading files and attaching them to content.",
      "item": [
        {
          "name": "Upload a file",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/media",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "media"
              ]
            },
            "description": "POST /admin/media\n\nmultipart/form-data, not JSON. The file itself decides what it is; a declared kind is only ever allowed to agree with it. Video and audio may be up to 300 MB, matching the hard limit in the content production specification, and images up to 10 MB. Giving an entity attaches the upload to that content item or tool, which is an edit like any other: users keep seeing the published version until somebody publishes the update. Which slot it lands in is `field`; a Guided Practice takes its voice and background tracks as two separate uploads.\n\nNeeds an admin token.\n\nAnswers: 201, 422.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "value": "",
                  "description": "A video, an audio file or an image.",
                  "disabled": false
                },
                {
                  "key": "kind",
                  "type": "text",
                  "value": "",
                  "description": "Optional. Must agree with the file.",
                  "disabled": true
                },
                {
                  "key": "entity",
                  "type": "text",
                  "value": "",
                  "description": "A content item or tool id to attach it to.",
                  "disabled": true
                },
                {
                  "key": "field",
                  "type": "text",
                  "value": "",
                  "description": "Which slot to attach it to. Defaults to media. Guided Practice carries two audio slots, voice and bg, and takes the mix of them at play time; media and thumb are the slots every other content item and every tool uses.",
                  "disabled": true
                }
              ]
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Delete an upload",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/media/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "media",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "1",
                  "description": "The numeric media id."
                }
              ]
            },
            "description": "DELETE /admin/media/{id}\n\nRemoves the file from disk as well as the record.\n\nNeeds an admin token.\n\nAnswers: 200, 404.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Admin: categories",
      "description": "The categories utility tools are grouped by.",
      "item": [
        {
          "name": "List categories",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/categories",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "categories"
              ]
            },
            "description": "GET /admin/categories\n\nEach one carries how many tools currently sit in it.\n\nNeeds an admin token.\n\nAnswers: 200.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Create a category",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/categories",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "categories"
              ]
            },
            "description": "POST /admin/categories\n\nNeeds an admin token.\n\nAnswers: 201, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Recovery\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Rename or reorder a category",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/categories/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "categories",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "1",
                  "description": "The numeric category id."
                }
              ]
            },
            "description": "PATCH /admin/categories/{id}\n\nTools carry the category by name, and a name is one of the fields a published tool reports, so a rename is a draft revision for every tool holding the old one.\n\nNeeds an admin token.\n\nAnswers: 200, 422.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Reset\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Delete a category",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/admin/categories/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "admin",
                "categories",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "1",
                  "description": "The numeric category id."
                }
              ]
            },
            "description": "DELETE /admin/categories/{id}\n\nRefused while any tool still carries it, and the refusal names the tools.\n\nNeeds an admin token.\n\nAnswers: 200, 422.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{adminToken}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        }
      ]
    }
  ]
}