# PositiveForm app guide for AI assistants

## App-navigation knowledge pack

This file gives any authorized PositiveForm user a source-grounded app coach
inside ChatGPT or another capable AI assistant. It is for owners, managers,
front-desk staff, instructors, integration staff, and other roles using the
workflows documented below. Upload the complete file; do not paste only the
workflow that seems relevant. The shared rules, vocabulary, maturity labels,
and recovery guidance are part of the operating context.

This is documentation, not a connection to PositiveForm. Uploading it does not
sign ChatGPT into the console, reveal live records, grant permissions, or allow
ChatGPT to confirm that an action happened. ChatGPT can coach from the guide. It
can interact with a browser only when the user separately enables and supervises
an interactive capability.

## Set it up in ChatGPT

For repeated use, create a ChatGPT Project named **PositiveForm app help**:

1. Add this complete Markdown file to the Project's sources.
2. Add the instruction block below to the Project instructions.
3. Start a new chat from that Project for each distinct task.
4. Replace the old pack when PositiveForm supplies a newer one. Do not keep two
   revisions in the same Project.

For a one-time question, attach this file directly to a new chat and paste the
same instruction block as the first message.

Ask for help in plain language: describe what you need to accomplish, your
role, and the page you are on if you know it. You do not need to know a story
ID, route, or exact PositiveForm term before you ask.

OpenAI's current Projects documentation says Project chats share uploaded
files and Project instructions, while an uploaded source does not give ChatGPT
direct access to a computer folder or application. See
[Projects and chats](https://learn.chatgpt.com/docs/projects).

## Copy this into the ChatGPT Project instructions

```text
You are a source-grounded PositiveForm app coach for authorized users. Treat
the attached "PositiveForm app guide for AI assistants" as the primary source
for product navigation and workflows.

For every operational request:
1. Translate the user's plain-language goal into the closest documented
   workflow, then identify that workflow by story ID and title. Never require
   the user to know the story ID or route. Ask only for missing context that
   changes the answer: role, selected organization or location, current page,
   intended record, or intended outcome.
2. State whether the workflow is verified or an unverified draft. Never present
   a draft as confirmed behavior.
3. Give prerequisites and required access, the exact navigation path, numbered
   steps, the expected persisted result, and recovery steps.
4. Use the product vocabulary in this pack. Do not invent controls, statuses,
   routes, permissions, policies, or provider behavior.
5. If the visible page differs from the pack, stop. Ask for the current route,
   exact visible labels or error text, role, selected location, and a redacted
   screenshot if needed. Clearly say the guide may be out of date.
6. Before any consequential action, state the target and consequence, provide a
   final confirmation checkpoint, and have the user verify organization,
   location, person or household, amount, date, and scope as applicable.
7. Never ask for passwords, sign-in codes, session cookies, access tokens, API
   keys, signing secrets, full payment-card or bank details, unredacted member
   exports, or provider credentials. Ask for placeholders or redacted details.
8. Never claim that you viewed, clicked, saved, sent, charged, refunded,
   deleted, imported, or changed anything unless interactive access was
   explicitly enabled and you directly observed the completed result.
9. Do not coach around a missing permission or disabled safety control. Explain
   the required access and direct the user to an authorized owner or manager.
10. When the requested workflow is absent from the pack, say that it is not
    documented here and collect a redacted escalation report instead of
    improvising.

Keep answers concise enough to use at the desk. For a complex task, give one
safe phase at a time and wait at confirmation checkpoints.
```

## Privacy rules for all users

Use only an AI account and workspace approved by the user's studio for business
use. If the studio has not approved one, use fabricated or de-identified
examples only.

Safe context includes:

- a page name or route with record identifiers removed when possible;
- the signed-in role and selected location;
- exact button labels, headings, and error text;
- an approximate time and timezone;
- placeholders such as `Member A`, `Household B`, and `$XX.XX`; and
- a cropped, redacted screenshot with names, contact details, balances, card
  details, barcodes, and browser/session material removed.

Do not paste or upload:

- a member list, customer export, import file, attendance roster, invoice
  ledger, or communication history;
- names together with birth dates, addresses, phone numbers, email addresses,
  health or safety notes, or household relationships;
- passwords, one-time codes, recovery codes, cookies, tokens, API keys,
  webhook secrets, signing secrets, or provider credentials;
- full payment-card or bank information; or
- an unredacted console screenshot, browser network capture, or provider
  dashboard screenshot.

When real identifiers are needed to finish the task, enter them directly in
PositiveForm. Do not route them through ChatGPT.

## Safety checkpoints

Before changing anything, verify:

1. **Workspace:** the correct organization and operating location are selected.
2. **Authority:** the signed-in person has the required role or capability.
3. **Target:** the correct person, household, lead, program, event, or provider
   connection is open.
4. **Effect:** the expected result is understood, including who will see it and
   whether money, messages, access, or records will change.
5. **Recovery:** the guide explains how to retry, reverse, or safely stop.

Require explicit confirmation from the authorized user immediately before:

- creating, changing, pausing, resuming, or cancelling a subscription;
- charging or refunding money;
- approving a billing change;
- deleting or retiring an organization, location, household, member, program,
  role, tag, agreement, API key, or webhook endpoint;
- committing, rolling back, or discarding an import;
- publishing an agreement or changing required signing order;
- changing staff permissions;
- sending a message or changing marketing configuration; or
- connecting, disconnecting, or changing Stripe, Resend, API, or webhook setup.

If the consequence is unclear, stop before the final control and escalate.

## Product mental model

- **Organization:** the studio business boundary. Records and staff authority
  belong to an organization.
- **Location:** the operating site inside the organization. Check-in, lists,
  schedules, and counts may change with the selected location.
- **Staff:** a person who operates the console. A visible control is not proof
  of authority; server-enforced roles and capabilities decide access.
- **Member:** any adult or dependent the studio serves, whether or not that
  person trains or pays.
- **Lead:** a person or family in the active sales and trial workflow until
  explicit conversion.
- **Household:** an operational family grouping. It provides shared desk and
  safety context; it is not itself a wallet.
- **Program:** an offering defined by the studio. It may be a scheduled class,
  ongoing access, an add-on, or a short session.
- **Enrollment:** a member's placement in a program. Enrollment is independent
  from payment.
- **Billing profile:** an adult payer's durable payment identity, reused by the
  subscriptions that adult pays.
- **Subscription:** the recurring money object. It is separate from program
  enrollment.
- **Charge:** one-time money movement, not a subscription.
- **Active / Trialing / Inactive member:** an enrollment-derived operating
  state. Billing conditions such as past due or no card are separate flags and
  do not change member activity by themselves.
- **Attendance:** a check-in record. Checking in does not enroll, unenroll, or
  bill a member.
- **Progression and rank:** the studio's advancement ladder and the member's
  recorded place or history in it.
- **Testing event:** a scheduled advancement event with a roster and completion
  workflow.
- **Tag:** a studio-managed label for organizing members.
- **Training group:** a saved audience used for schedules and rosters. It may
  combine filters, eligibility, and explicit members; it does not itself enroll
  or unenroll anyone.

## How ChatGPT should answer

A strong answer follows this shape:

```text
Documented workflow: ATT-002 — Run front-desk check-in
Maturity: verified
Required access: organization membership; selected location

Before you start
- Confirm the correct location in the console.

Steps
1. ...

Confirmation checkpoint
- Confirm the person and class context before saving.

Expected result
- ...

If it does not match
- ...
```

Do not answer from a similar workflow when an exact workflow exists. Do not
combine a verified guide with an undocumented guess. If two guides are needed,
name both and make the handoff between them explicit.

## Useful prompts for PositiveForm users

These prompts use placeholders on purpose:

- `I work at the front desk for Location A. Walk me through checking in Member
  A, one step at a time, and stop before the final save.`
- `Which verified workflow should I use to create a lead and schedule a trial?
  Give me prerequisites and the exact expected result.`
- `Explain the difference between enrolling Member A in Program B and creating
  the related subscription. Show the confirmation checks for both.`
- `I am on the household billing page and need to refund a one-off charge of
  $XX.XX. Use only verified instructions and stop before the refund control.`
- `Help me prepare an import without committing it. Tell me exactly where the
  current guide becomes an unverified draft.`
- `The screen does not match the guide. Build a redacted escalation report from
  the route, my role, selected location, visible labels, and exact error.`
- `I am signed in as a manager. What can my role do in this workflow, and which
  capability does the guide say is required?`

## When the guide and screen disagree

The guide and product must not be silently reconciled by guesswork.

1. Stop before changing data.
2. Record the page name and route, removing record identifiers when practical.
3. Record the signed-in role, selected organization, and selected location.
4. Copy the exact visible label or error text.
5. Describe the intended result and what appeared instead.
6. Note the approximate time and timezone.
7. Capture a redacted screenshot only when it adds necessary context.
8. Send that report through the studio's approved PositiveForm support channel.

Use this template:

```text
PositiveForm guide mismatch
Time and timezone:
Role:
Organization/location:
Page and redacted route:
Documented story ID:
Task:
Exact visible labels or error:
Expected:
Actual:
Safe steps already tried:
Operational impact:
Redacted screenshot attached: yes/no
```

Do not put real member, household, payment, credential, or provider data into
the ChatGPT conversation or escalation draft.

## Coverage and reliability

This pack is generated from the canonical PositiveForm customer workflow
registry and its active guide files. It contains **77 active
workflows**: **65 verified** and **12 draft**.
The registry's route and navigation inventory records a review date of
**2026-07-26**; each verified workflow below also names its own
verification date and deployed commit.

Definitions:

- **Verified / published:** the source guide has numbered steps, an expected
  result, recovery guidance, and a recorded browser verification. It is
  still possible for a later product change to make a label stale.
- **Draft / unverified:** the product surface is active, but the complete path
  has not been verified against a deployed environment. ChatGPT may explain the
  provisional guide while clearly naming that limitation. It must not fill a
  gap from general knowledge.
- **Absent:** the workflow is not in this pack. ChatGPT must say it is not
  documented and prepare a redacted escalation report.

Current draft workflows:

- BILL-006 — Resolve billing reviews (`/billing/reviews/:id`)
- BILL-007 — Approve billing changes (`/billing/approvals/:id`)
- COMMS-001 — Work the conversation inbox (`/inbox`)
- CRM-002 — Work the lead pipeline (`/leads`)
- CRM-003 — Review a lead record (`/leads/:leadId`)
- IMP-002 — Upload, map, validate, and review import data (`/settings/imports/:jobId/validation`)
- IMP-003 — Commit and reconcile an import (`/settings/imports/:jobId/reconciliation`)
- INT-002 — Connect and verify Resend (`/settings/integrations/resend`)
- MEM-002 — Maintain member profile and notes (`/members/:memberId/overview`)
- MEM-006 — Print member barcode labels (`/members/barcode-labels`)
- ONB-003 — Build a studio workspace before creating an account (`/setup`)
- WORK-001 — Switch organizations (`/`)

The family/member portal remains outside active guide coverage. Internal
PositiveForm control-plane operations, provider credentials, programming
references, and third-party integration implementation are also excluded.

## Workflow index

Find the story ID here, then use the complete reference section below.

### Advancement

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| ADV-001 | Configure a progression | `/settings/progressions` | verified |
| ADV-002 | Review advancement readiness | `/advancement` | verified |
| ADV-003 | Record member advancement | `/advancement/review` | verified |

### Attendance

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| ATT-001 | Check in from a member workspace | `/members/:memberId/overview` | verified |
| ATT-002 | Run front-desk check-in | `/attendance` | verified |
| ATT-003 | Review attendance history | `/attendance` | verified |
| ATT-004 | Record bulk attendance | `/attendance/bulk` | verified |

### Authentication

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| AUTH-001 | Sign in to the staff console | `/sign-in` | verified |
| AUTH-002 | Create a staff account | `/sign-up` | verified |

### Billing approvals

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| BILL-007 | Approve billing changes | `/billing/approvals/:id` | draft |

### Billing operations

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| BILL-004 | Review and reconcile billing operations | `/billing` | verified |
| BILL-005 | Create and refund one-off charges | `/billing` | verified |

### Billing reviews

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| BILL-006 | Resolve billing reviews | `/billing/reviews/:id` | draft |

### Billing setup

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| BILL-008 | Configure billing policies and catalog | `/settings/billing` | verified |

### Communications

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| COMMS-001 | Work the conversation inbox | `/inbox` | draft |

### Dashboard

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| DASH-001 | Review the studio dashboard | `/` | verified |

### Data imports

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| IMP-001 | Start a studio import | `/settings/imports/new` | verified |
| IMP-002 | Upload, map, validate, and review import data | `/settings/imports/:jobId/validation` | draft |
| IMP-003 | Commit and reconcile an import | `/settings/imports/:jobId/reconciliation` | draft |
| IMP-004 | Discard or safely roll back an import | `/settings/imports/:jobId/completion` | verified |

### Developer setup

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| API-001 | Manage developer API keys | `/settings/developer/api` | verified |
| API-002 | Operate webhook endpoints | `/settings/developer/webhooks` | verified |

### Global search

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| SEARCH-001 | Find operational records | `/` | verified |

### Household billing

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| BILL-001 | Create a household billing profile | `/households/:householdId/billing` | verified |
| BILL-002 | Review household billing | `/households/:householdId/billing` | verified |
| BILL-003 | Create a household subscription | `/households/:householdId/billing/subscriptions/new` | verified |

### Households

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| HH-001 | Create and find a household | `/households` | verified |
| HH-002 | Add an existing person to a household | `/households/:householdId/people` | verified |
| HH-003 | Manage household people and safety context | `/households/:householdId/people` | verified |
| HH-004 | Manage member agreement completion | `/households/:householdId/people` | verified |
| HH-005 | Change or retire a household | `/households/:householdId/settings` | verified |

### Integrations

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| INT-001 | Connect and operate Stripe | `/settings/integrations/stripe` | verified |
| INT-002 | Connect and verify Resend | `/settings/integrations/resend` | draft |
| INT-003 | Configure organization email marketing | `/settings/integrations/resend` | verified |

### Leads and trials

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| CRM-001 | Create a lead and schedule a trial | `/leads/new` | verified |
| CRM-002 | Work the lead pipeline | `/leads` | draft |
| CRM-003 | Review a lead record | `/leads/:leadId` | draft |
| CRM-004 | Convert a lead | `/leads/:leadId/convert` | verified |
| CRM-005 | Find and archive leads | `/leads/all` | verified |

### Locations

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| LOC-001 | Create a location | `/locations/new` | verified |
| LOC-002 | Maintain or retire a location | `/settings/locations/:publicId` | verified |

### Member agreements

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| AGR-001 | Manage agreement templates | `/settings/agreements/templates` | verified |
| AGR-002 | Configure agreement onboarding order | `/settings/agreements` | verified |
| AGR-003 | Sign a public agreement | `/w/:linkId` | verified |

### Members

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| MEM-001 | Create a member | `/members` | verified |
| MEM-002 | Maintain member profile and notes | `/members/:memberId/overview` | draft |
| MEM-003 | Browse and filter people | `/members` | verified |
| MEM-004 | Manage member lifecycle and subscriptions | `/members/:memberId/subscription` | verified |
| MEM-005 | Review member history | `/members/:memberId/history` | verified |
| MEM-006 | Print member barcode labels | `/members/barcode-labels` | draft |

### Organization onboarding

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| ONB-001 | Create a studio organization | `/organizations/new` | verified |
| ONB-002 | Record migration and service needs | `/migration-offer` | verified |
| ONB-003 | Build a studio workspace before creating an account | `/setup` | draft |

### Organization settings

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| ORG-001 | Maintain organization details and branding | `/settings/organization` | verified |
| ORG-002 | Delete an organization | `/settings/organization` | verified |

### People

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| PEOPLE-001 | Review parents and guardians | `/people/adults` | verified |

### PositiveForm subscription

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| BILL-009 | Manage the studio subscription | `/settings/subscription` | verified |

### Program enrollment

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| ENR-001 | Add a member to a program roster | `/programs/:slug/members` | verified |
| ENR-002 | Manage the enrollment queue | `/enrollments` | verified |

### Programs

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| PROG-001 | Create a program | `/programs` | verified |
| PROG-002 | Manage program schedules | `/programs/:slug/schedule/new` | verified |
| PROG-003 | Manage program lifecycle | `/programs/:slug/settings` | verified |
| PROG-004 | Duplicate a program | `/programs/:slug/overview` | verified |
| PROG-005 | Assign program staff and operating notes | `/programs/:slug/staff` | verified |
| PROG-006 | Manage program pricing plans | `/programs/:slug/overview` | verified |

### Settings

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| SET-001 | Navigate studio settings | `/settings` | verified |

### Staff

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| STAFF-001 | Invite staff | `/settings/staff` | verified |
| STAFF-002 | Maintain a staff profile | `/settings/staff/:staffId/identity` | verified |
| STAFF-003 | Record staff achievements and ranks | `/settings/staff/:staffId/achievements` | verified |
| STAFF-004 | Manage staff access | `/settings/staff/:staffId/permissions` | verified |

### Tags

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| TAG-001 | Manage the tag catalog | `/settings/tags` | verified |

### Testing events

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| TEST-001 | Schedule a testing event | `/testing` | verified |
| TEST-002 | Run and finish testing | `/testing/:eventId/roster` | verified |

### User roles

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| ROLE-001 | Manage reusable roles | `/settings/roles` | verified |
| ROLE-002 | Preview a role safely | `/settings/roles` | verified |

### Workspace navigation

| Story | Workflow | Route | Maturity |
| --- | --- | --- | --- |
| WORK-001 | Switch organizations | `/` | draft |
| WORK-002 | Switch operating locations | `/` | verified |

# Complete workflow reference

The entries below are generated from the canonical customer guides. Screenshot
and repository-evidence sections are intentionally omitted: they do not travel
with this text file and may contain context that should not be uploaded. Local
links are rendered as labels; external public documentation links are retained.

## ADV-001 — Configure a progression

- Area: Advancement
- Surface: staff-console
- Route: `/settings/progressions`
- Roles: organization-owner, manager
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `fe1de72`.

### Who this is for

This guide is for **organization owners and managers** who define belt or level
sequences before staff review readiness or run testing events.

You need the `members.change` permission.

### Before you begin

- Decide whether you are creating a new progression or reviewing one that
  already exists.
- Progressions live under **Settings › Progressions**.

### Steps

1. Open **Settings**, then **Progressions** (or use **Configure progressions**
   from the Advancement page).
2. Read the list of existing progressions. Each row offers **Review progression**
   and, when allowed, **Duplicate**.
3. Click **New progression** to start a new sequence (or expand creation options
   if the button toggles them).
4. Choose a template or blank builder, name the progression, and define the
   ordered ranks.
5. Save. Return to the Progressions list and open **Review progression** to
   confirm the ranks and draft or active state.
6. Use **Duplicate** when you need a variation without rebuilding from scratch.

### Expected outcome

A progression is available for Advancement readiness and for testing events.
Staff can choose it on **Advancement** and when scheduling a testing day.

### Recovery

If **New progression** is missing, your role cannot manage progressions.

If the list is empty for a non-manager, wait until an owner creates one; the
empty state explains that progressions appear once staff create them.

If save fails, stay on the builder, fix the named fields, and try again before
leaving.

### Related workflows

- ADV-002 — review who is ready to advance.
- TEST-001 — schedule testing against a
  progression.

## ADV-002 — Review advancement readiness

- Area: Advancement
- Surface: staff-console
- Route: `/advancement`
- Roles: organization-owner, manager, staff
- Required access: organization.membership
- Maturity: Published and verified on 2026-07-29 against deployed commit `fe1de72`.

### Who this is for

This guide is for **staff, managers, and owners** who filter the member list by
progression and readiness before recording a promotion.

You need membership access to open Advancement. Recording still needs
`members.change` (ADV-003).

### Before you begin

- A progression must exist (ADV-001).
- Select the location you are reviewing.

### Steps

1. Click **Advancement** in the left navigation.
2. Choose the progression in the progression control (for example **Striking
   Belts**).
3. Optionally search with **Search members**.
4. Narrow with **All ranks**, **Active only** (or other status), **Filter by
   program**, and age bounds when needed.
5. Select the members you intend to advance. Use page-level select when the
   list supports selecting everyone on the page.
6. Click **Review next →** to open the review step with your selection.

### Expected outcome

You have a filtered, selected cohort ready for the advancement review screen,
where ranks are confirmed before they are saved.

### Recovery

If the board tells you to configure a progression first, complete
ADV-001.

If no members match, clear filters and search, or confirm the progression and
location.

If **Review next** fails to prepare, refresh the list and try again; do not
assume ranks changed.

### Related workflows

- ADV-001 — maintain progressions.
- ADV-003 — record the promotion.

## ADV-003 — Record member advancement

- Area: Advancement
- Surface: staff-console
- Route: `/advancement/review`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `a15afd7`.

### Who this is for

This guide is for **staff, managers, and owners** confirming the next ranks for
members selected on Advancement.

You need the `members.change` permission.

### Before you begin

- Start from ADV-002 so the review screen has a prepared
  selection. Opening `/advancement/review` with no selection returns you to
  Advancement.
- Confirm the progression and target ranks before saving.

### Steps

1. Open **Advancement** in the left navigation.
2. Select the members to advance (row **Select …** checkboxes, or **Select all**
   when appropriate).
3. Click **Review next →**. The review screen lists each member with current and
   next rank.
4. Click **Advance** (or **Advance N members**).
5. Confirm in the dialog (**Advance N members**). Cancel leaves ranks unchanged.
6. When the save finishes you return to Advancement with ranks updated.

### Expected outcome

Selected members receive the recorded ranks once; member history reflects the
change.

### Recovery

If review is empty, return to Advancement and prepare a selection first.
If the confirm dialog is dismissed, no ranks change; open **Review next →**
again when ready.

### Related workflows

- ADV-002 — build the review selection.
- TEST-002 — award ranks through a testing event.

## AGR-001 — Manage agreement templates

- Area: Member agreements
- Surface: staff-console
- Route: `/settings/agreements/templates`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `2ac7ef8`.

### Who this is for

This guide is for whoever owns the studio's paperwork: an **owner**, a
**manager**, or **staff** with the `members.change` permission.

An agreement template is the document families sign. It has a lifecycle you can
see on the page: **Draft** while you write it, **Published** once families can
sign it, and **Archived** when you retire it.

The property that matters is that published content is **versioned**. Editing a
published agreement produces a new version rather than rewriting the one people
already signed, so what somebody agreed to stays what they agreed to.

### Before you begin

- Have the wording ready, or be prepared to draft it here.
- Know that only a **published** agreement can be sent or signed. A draft cannot
  be requested from a family.
- Know that archiving does not invalidate signatures already collected.

### Steps

1. Click **Settings** in the left navigation, then **Member agreements**, then
   the **Agreement templates** tab. With none set up it reads **No agreement
   templates** and invites you to create one.
2. Click **New agreement**.
3. Give it a title families will recognise, and write the content.
4. Save it. It stays a **Draft** until you publish it, and each template shows
   its version number and when it last changed.
5. Publish it when the wording is right. It moves under **Published**, and only
   then can it be sent or signed.
6. To change a published agreement, edit it and publish again. That creates a new
   version; signatures already collected stay attached to the version that was
   signed.
7. To retire one, archive it. It moves under **Archived** and stops being offered
   for new signatures.

### Expected outcome

The templates page groups agreements by where they are in their lifecycle:
drafts you are still writing, **Published** ones families can sign, and
**Archived** ones you have retired. Each shows a version number and the date it
last changed.

A published agreement becomes available everywhere signatures are collected,
including the **Request signatures** and **Sign here** actions on a household;
see HH-004.

Publishing a new version does not alter what anyone has already signed. That is
the reason it versions rather than editing in place.

### Recovery

If an agreement does not appear when you try to send one, check it is published
rather than still a draft. A draft is invisible to the sending flow on purpose.

If you archived one families still need, unarchive or republish it. Signatures
collected before archiving are unaffected either way.

If you published wording with a mistake, fix it and publish again rather than
trying to undo. The corrected version applies to future signatures, and the
earlier one stays attached to the people who signed it, which is what you want if
anyone ever asks what they agreed to.

If saving or publishing fails, PositiveForm shows the reason and the template is
unchanged.

### Related workflows

- HH-004 — send a published
  agreement to a family, or record one they signed on paper.
- AGR-003 — what the signer sees when they open
  the link.

## AGR-002 — Configure agreement onboarding order

- Area: Member agreements
- Surface: staff-console
- Route: `/settings/agreements`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `2ac7ef8`.

### Who this is for

This guide is for an **owner**, **manager**, or **staff** member with the
`members.change` permission, deciding what a new family signs and in what order.

Most studios ask for more than one document. Rather than sending them one at a
time, you set a **signing sequence** once: the packet a new family works through
from start to finish.

### Before you begin

- Publish the agreements first. The sequence is built from published templates,
  and the page says **Create an agreement first.** until at least one exists; see
  AGR-001.
- Decide the order deliberately. It is the order families meet the documents in,
  so put the one that explains the others first.

### Steps

1. Click **Settings** in the left navigation, then **Member agreements**. The
   page opens with **Send agreements**, **Stored agreements**, and **Agreement
   templates** across the top.
2. Find **Signing sequence**. Until you publish it, it is marked **Draft**.
3. Add the published agreements a new family should sign, and arrange them in the
   order you want them met.
4. Click **Preview flow** to walk through it the way a family will. Do this
   before publishing: the order reads differently from the family's side than it
   does from a list.
5. Publish the sequence when it is right.

### Expected outcome

The sequence saves and, once published, becomes the packet offered wherever
signatures are requested. Choosing to send the packet on a household sends these
documents, in this order; see
HH-004.

A member's signing status then reflects the whole packet rather than one
document, so a family who has signed two of three reads as outstanding, which is
the honest state.

While the sequence is still a **Draft**, it is not offered for sending. That is
what stops a half-built packet reaching a family.

### Recovery

If the page says **Create an agreement first.**, there are no published
agreements to sequence; see AGR-001.

If sending a packet reports **Configure the signing sequence first.**, the
sequence exists but is not published. Publish it.

If a family is signing documents in an order that confuses them, use **Preview
flow** to see what they see, then reorder. Changing the sequence does not
invalidate signatures already collected.

If saving fails, PositiveForm shows the reason and the sequence is unchanged.

### Related workflows

- AGR-001 — publish the agreements this
  sequence is assembled from.
- HH-004 — send the published
  packet to a family.

## AGR-003 — Sign a public agreement

- Area: Member agreements
- Surface: public
- Route: `/w/:linkId`
- Roles: public-signer
- Required access: public
- Maturity: Published and verified on 2026-07-30 against deployed commit `5dab2df`.

### Who this is for

This guide is for a parent, guardian, or adult participant opening a studio's
agreement link or scanning its front-desk QR code.

Required access: none. The signing page is public and does not ask you to sign
in or create an account.

### Before you begin

- Use the current link or QR code supplied by the studio. A disabled or replaced
  link cannot accept a signature.
- Have an email address the studio can use if it needs to follow up about the
  agreement.
- Know which students the agreement covers. Leave the student field blank when
  you are signing only for yourself.
- Read the agreement before completing the signature fields.

### Steps

1. Open the studio's agreement link or scan its QR code. The page identifies
   the studio and agreement above the full document.
2. Read the agreement, then enter **Your name** and **Your email**.
3. In **Student name(s)**, list everyone the agreement covers. Leave this field
   blank if you are signing for yourself.
4. Review **Sign by typing your name**. PositiveForm copies **Your name** into
   this field automatically; correct it if the legal signature should differ.
5. Read and select the agreement checkbox. **Agree and sign** becomes available
   only after the required details and this attestation are present.
6. Select **Agree and sign** once. Wait until the page says **Signed**, names the
   agreement and who it covers, and says the device can be handed back or the
   page closed.

### Expected outcome

PositiveForm records one public signing submission and shows **Signed** without
requiring a staff or family account. The studio receives the submission as
unmatched so staff can associate it with the right member during reconciliation.

### Recovery

- If the page says **This signing link isn't active**, ask the front desk for a
  current QR code or link. The old link cannot be repaired from the signing
  page.
- If it says **Too many requests right now**, wait a minute and scan or open the
  link again.
- If **Your email** is rejected, correct it before trying to sign. The studio
  uses this address for agreement follow-up.
- If **Agree and sign** is unavailable, complete your name and email, make sure
  the typed signature is present, and select the agreement checkbox.
- If the page already says **Signed**, do not submit again. The signature was
  recorded; hand the device back or close the page.
- If submission fails without reaching **Signed**, keep the page open and ask
  the front desk to confirm whether the signature arrived before retrying.

### Related workflows

- HH-004 — how studio staff review
  a member's outstanding and completed agreements.
- AGR-001 — how studio staff publish the
  agreement shown on this page.

## API-001 — Manage developer API keys

- Area: Developer setup
- Surface: staff-console
- Route: `/settings/developer/api`
- Roles: organization-owner, manager
- Required access: integrations.manage
- Maturity: Published and verified on 2026-07-30 against deployed commit `a15afd7`.

### Who this is for

This guide is for **integration staff** creating and managing developer API keys
inside PositiveForm.

### Before you begin

- Prefer a non-production studio when experimenting.
- Copy any new secret immediately; it is shown only once.

### Steps

1. Open **Settings › Developer › API** (`/settings/developer/api`). The
   heading is **Developer API**.
2. Enter an **Integration name**.
3. Tick the locations the key may access (for example studio location
   checkboxes shown on the form).
4. Click **Create key**.
5. Copy the secret from the one-time display, store it in your secret manager,
   then close the dialog. Rotate or revoke from the same page when a key is
   compromised.
6. Use the **Developer** breadcrumb to reach webhooks next if needed.

### Expected outcome

Key lifecycle is visible. New secrets appear once; revoked keys cannot be used.

### Recovery

If create fails, check that a name and at least one location are selected. If
you lost a secret, revoke the key and create a new one rather than hunting for
it in ChatGPT or support material.

### Related workflows

- API-002

## API-002 — Operate webhook endpoints

- Area: Developer setup
- Surface: staff-console
- Route: `/settings/developer/webhooks`
- Roles: organization-owner, manager
- Required access: integrations.manage
- Maturity: Published and verified on 2026-07-29 against deployed commit `beb3f3c`.

### Who this is for

This guide is for **integration staff** creating and inspecting customer webhook
endpoints and deliveries.

### Before you begin

- Have the destination URL your system will receive.
- Do not paste signing secrets into ChatGPT or support material.

### Steps

1. Open **Settings › Developer › Webhooks** (`/settings/developer/webhooks`).
   The heading is **Webhooks**.
2. Click **Create webhook** to add an endpoint.
3. Complete the URL, events, and secret fields the form requires, then save.
4. Return to the list to inspect delivery history, replay, archive, or rotate as
   offered on the endpoint.
5. Use **Developer** to return to API keys if needed.

### Expected outcome

Endpoint state and delivery/replay history are visible without turning the guide
into a full integration test harness.

### Recovery

If deliveries fail, open the endpoint detail for response codes before rotating
the secret. Archive unused endpoints rather than leaving dead URLs active.

### Related workflows

- API-001

## ATT-001 — Check in from a member workspace

- Area: Attendance
- Surface: staff-console
- Route: `/members/:memberId/overview`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `fe1de72`.

### Who this is for

This guide is for **desk staff, managers, and owners** who already have a
member open and want to record that they trained without going to the Check-in
desk.

You need the `members.change` permission.

### Before you begin

- Open the member at the location where they train.
- Prefer ATT-002 when several people are arriving at
  once; this path is best for one person already on screen.

### Steps

1. Open the member from **Members**, search, or a related link. You land on
   their **Overview** tab.
2. Find the **Check in** action on the overview.
3. Click **Check in**. While it saves, the button may read **Checking in…**.
4. Confirm the success state: PositiveForm toasts that the member checked in,
   or that they were just checked in if a recent visit already exists.
5. When this session created the visit, an **Undo check-in** control appears so
   you can reverse a mistaken click. Do not undo a visit you did not create.
6. Review **Recent check-ins** on the same overview for class or **General
   check-in** context and times.

### Expected outcome

A staff-method attendance record is stored for the member. It appears in their
recent check-ins and in the location’s Check-in history
(ATT-003).

### Recovery

If **Check in** is missing or disabled, confirm your role has `members.change`
and that a location is selected.

If check-in fails, PositiveForm shows **Check-in failed. Please try again.**
and does not invent a visit.

If you click **Check in** twice in quick succession, the second result may be
the neutral **This member was just checked in** notice without a second undo
control.

### Related workflows

- ATT-002 — front-desk search and check-in.
- ATT-003 — location-wide history.

## ATT-002 — Run front-desk check-in

- Area: Attendance
- Surface: staff-console
- Route: `/attendance`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `fe1de72`.

### Who this is for

This guide is for **desk staff, managers, and owners** checking people in as they
arrive for class.

You need the `members.change` permission. The page lives under **Check-in** in
the left navigation (route `/attendance`).

### Before you begin

- Select the location at the top of the console. Check-ins are recorded for the
  selected location only.
- Prefer searching by name, student number, Member ID, household, phone, or
  email when you are not using a member card. A USB barcode scanner can type a
  card code into the Check-in field and finish with Enter. On any other
  authenticated console page, the same scanner is recognized as a rapid burst
  of keys ending in Enter, then asks you to confirm check-in. It does not
  record attendance by itself.

### Steps

1. Click **Check-in** in the left navigation. The page title is **Check-in**,
   with the subtitle **Find a member, then check them in.**
2. Type into **Find a member or scan a barcode**. After two characters,
   PositiveForm searches members at this location by name, household, phone,
   email, student number, or Member ID.
3. Read each match: the member name, Member ID, student number when one is on
   file, age when known, household, and program context (or **No program
   context**). Inactive members are labelled **Inactive**.
4. Click **Check in** on the correct person. The method recorded is **Staff**
   for a manual desk check-in.
5. Confirm the success banner: **{name} checked in**, with the class context
   (or **General check-in**) and the time. You can **Dismiss** the banner.
6. Confirm the person appears under **Today’s check-ins**, with columns
   **Member**, **Class**, **Method**, and **Time**.
7. Optional: open **Bulk attendance** when you need to mark a whole class at
   once; see ATT-004.

### Expected outcome

A staff-method attendance record is saved for that member at the selected
location. They appear in **Today’s check-ins**, and the same visit is available
later under **Check-in history** on this page (ATT-003).

If they already checked in moments ago, PositiveForm may report that they were
just checked in rather than creating a duplicate visit.

### Recovery

If the search box is disabled, check the location picker and your network. When
you are offline, the page says check-ins are not being saved until the
connection returns.

If no members match, try another name, student number, Member ID, household,
phone, or email, or open **Members** to add the person first.

From any authenticated console page, a USB scanner that types a rapid card
code and Enter opens **Check in this member?** when the card is on file. Confirm
**Check in**, or **Dismiss** to leave attendance unchanged. If the card is not
recognised, PositiveForm opens the Check-in recovery path and shows the scanned
card number. Search for the
correct person by name, student number, Member ID, household, phone, or email,
then **Assign card**. Each match shows Member ID and student number when those
exist so you can confirm the card.
PositiveForm tells you whether this replaces an unused PositiveForm code or a
physical card already in use. Confirm before replacing a physical card. You
can **Assign card and check in**, or **Assign card only** if you only need the
card on file. A card that already belongs to someone else in this studio cannot
be reassigned.

If **Check in** fails for another reason, PositiveForm shows **Check-in failed.
Please try again.** and leaves you on the desk so you can retry.

If **Check-in** is missing from the navigation, your role does not carry the
attendance surface for this studio.

### Related workflows

- ATT-001 — check someone in from their member workspace.
- ATT-003 — filter and review past check-ins.
- ATT-004 — record attendance for a whole class.

## ATT-003 — Review attendance history

- Area: Attendance
- Surface: staff-console
- Route: `/attendance`
- Roles: organization-owner, manager, staff
- Required access: organization.membership
- Maturity: Published and verified on 2026-07-29 against deployed commit `fe1de72`.

### Who this is for

This guide is for **desk staff, managers, and owners** reviewing who checked in
earlier today or on past days at the selected location.

You need `organization.membership` access so you can open the Check-in page.
Recording new check-ins still needs `members.change`; see
ATT-002.

### Before you begin

- Select the location at the top of the console. History is scoped to that
  location.
- Use **Today’s check-ins** for the current day, and **Check-in history** for
  earlier visits with date and filter controls.

### Steps

1. Click **Check-in** in the left navigation.
2. Review **Today’s check-ins** for the current day. Columns are **Member**,
   **Class**, **Method**, and **Time**. Empty days say **No one has checked in
   today yet.**
3. Scroll to **Check-in history**.
4. Set a date range with the two date fields (from and to), using the calendar
   buttons when you prefer a picker.
5. Narrow results with **Program filter** (defaults to **All programs**) and
   **Method filter** (defaults to **All methods**), which covers staff, barcode,
   kiosk, and other methods.
6. Sort the history table with the **Member** or **Time** column headers when
   you need a different order.
7. Page with **Previous** and **Next** when the list is long.
8. Open a member name to jump to their member workspace for deeper context.

### Expected outcome

You can answer who trained, which class or general check-in they used, how they
checked in, and when. Filters and dates stay on the page so you can refine the
view without leaving Check-in.

### Recovery

If history is empty after filtering, clear **Program filter** and **Method
filter**, widen the dates, and confirm the location picker.

If the tables fail to load, PositiveForm shows an error with a retry path on
the page. Stay on Check-in rather than refreshing mid-filter unless the retry
does not recover.

If you expected a visit that is missing, confirm it was recorded at this
location and that the method filter is not hiding it.

### Related workflows

- ATT-002 — record a new desk check-in.
- ATT-004 — mark a whole class at once.

## ATT-004 — Record bulk attendance

- Area: Attendance
- Surface: staff-console
- Route: `/attendance/bulk`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `fe1de72`.

### Who this is for

This guide is for **desk staff, managers, and owners** marking attendance for a
whole class (or everyone at the location) on a chosen date.

You need the `members.change` permission.

### Before you begin

- Select the location.
- Know the attendance date. Defaults to today; use previous/next day controls
  when you are catching up.
- Prefer this path after class ends; use ATT-002 for
  one-by-one arrivals.

### Steps

1. Open **Check-in**, then click **Bulk attendance**, or go directly to
   `/attendance/bulk`.
2. Confirm the **Attendance date** with the date field or **Previous day** /
   **Next day**.
3. Choose who to record:
   - A program row such as **Youth Martial Arts** with its enrolled count and
     how many are already recorded that day, or
   - **Everyone** for the full location roster.
4. Open the program (or Everyone). The detail view lists people for that
   selection so you can mark attendance class by class.
5. Record attendance for the people who trained, then return with **Back to
   check-in** when finished.

### Expected outcome

Attendance rows exist for the people you marked on that date, counted under the
program (or Everyone) on the bulk list, and visible later in Check-in history.

### Recovery

If no programs appear, add a program first or use **Everyone** when the empty
state offers it.

If a program shows zero enrolled, enroll members before bulk marking, or use
front-desk check-in for visitors.

If you marked the wrong day, change the attendance date and correct that day’s
list rather than inventing a second visit without checking history.

### Related workflows

- ATT-002 — single check-in at the desk.
- ATT-003 — review what was recorded.

## AUTH-001 — Sign in to the staff console

- Area: Authentication
- Surface: staff-console
- Route: `/sign-in`
- Roles: staff, manager, organization-owner, studio-owner
- Required access: clerk.authentication
- Maturity: Published and verified on 2026-07-26 against deployed commit `1096a65`.

### Who this is for

Staff, managers, and studio owners who already have a PositiveForm account and
need to enter their organization-scoped console.

Required access: an active Clerk account connected to a staff record.

### Before you begin

- Confirm that your account belongs to an active staff record.
- Ask a studio owner which organization you should see after sign-in if your
  account can access more than one.

### Steps

1. Open `/sign-in`.
2. Select **Continue with Google**, or enter your account email address and
   select **Continue**.
3. Complete Clerk's remaining sign-in prompts. Keep passwords and verification
   codes private.
4. Confirm that PositiveForm opens the selected organization's console. A new
   account without organization access may instead see the organization setup
   or access screen.

### Expected outcome

The signed-in user reaches the correct organization-scoped console, or sees a
clear organization setup/access state when no organization is available.

### Recovery

- If Clerk says the account cannot be found, confirm the email address or ask a
  studio owner to invite the account.
- If the wrong organization opens, use the organization switcher before doing
  any work.
- If sign-in loops or returns an error, reload once and try again. Preserve the
  visible error and send an escalation report if the problem continues.

### Related workflows

- AUTH-002 — Create a staff account when one does not exist.
- ONB-001 — Create an organization when the account has no organization access.

## AUTH-002 — Create a staff account

- Area: Authentication
- Surface: staff-console
- Route: `/sign-up`
- Roles: prospective-staff
- Required access: public
- Maturity: Published and verified on 2026-07-26 against deployed commit `1096a65`.

### Who this is for

Prospective staff who need to open PositiveForm's dedicated account
registration flow before joining or creating a studio workspace.

Required access: none. This is a public, signed-out workflow.

### Before you begin

- Sign out of any existing PositiveForm session.

### Steps

1. Open the PositiveForm sign-in page and select **Sign up**, or go directly to
   `/sign-up`.
2. Confirm that the page says **Create your account** and offers both
   **Continue with Google** and an email registration form.
3. For email registration, enter optional first and last names, an email
   address, and a password, then select **Continue**.
4. Follow any verification prompts from Clerk. After account creation,
   PositiveForm will either open an available organization or show the next
   organization access/setup step.

### Expected outcome

The `/sign-up` route renders Clerk's **Create your account** form. It includes
Google registration, email and password fields, a **Continue** button, and a
link back to **Sign in**.

### Recovery

- If sign-in appears instead, sign out and reopen `/sign-up`.
- If the email is already registered, select **Sign in** rather than creating a
  duplicate account.
- If the form does not load, reload once. Preserve the visible error and send an escalation report if the dedicated route still does not render.

### Related workflows

- AUTH-001 — Sign in to the staff console after the account exists.
- ONB-001 — Create an organization when the account has no studio access yet.

## BILL-001 — Create a household billing profile

- Area: Household billing
- Surface: staff-console
- Route: `/households/:householdId/billing`
- Roles: organization-owner, manager, staff
- Required access: billing.manage
- Maturity: Published and verified on 2026-07-29 against deployed commit `beb3f3c`.

### Who this is for

This guide is for **staff, managers, and owners** attaching billing to a household
so subscriptions and charges have a place to land.

You need billing/members change access for the selected location.

### Before you begin

- Open the household that will pay.

### Steps

1. Open **Households** and select the family.
2. Open the **Household billing** tab.
3. If no profile exists, start **Create billing profile** (or the equivalent empty-state action).
4. Enter the payer details the form requires and save.
5. Confirm the household billing workspace shows the new profile instead of the empty state.

### Expected outcome

The household has a billing profile ready for subscriptions and charges.

### Recovery

If save fails, read the on-page error and correct required fields. Do not create a second profile for the same household without checking the first.

### Related workflows

- BILL-002
- BILL-003

## BILL-002 — Review household billing

- Area: Household billing
- Surface: staff-console
- Route: `/households/:householdId/billing`
- Roles: organization-owner, manager, staff
- Required access: billing.manage
- Maturity: Published and verified on 2026-07-29 against deployed commit `beb3f3c`.

### Who this is for

This guide is for **staff, managers, and owners** reading a household’s billing
status, subscriptions, and open items.

### Before you begin

- Select the location and open the household.
- A billing profile may already exist (BILL-001).

### Steps

1. Open the household from **Households**.
2. Open the **Household billing** tab.
3. Review the profile summary, subscriptions, and recent activity shown on the page.
4. Use links from this tab to create a subscription or return to studio billing operations when needed.

### Expected outcome

You can answer whether the household can be charged, what subscriptions exist, and what needs attention.

### Recovery

If the tab is empty, create a profile first. If data fails to load, retry from the household before changing anything.

### Related workflows

- BILL-001
- BILL-003
- BILL-004

## BILL-003 — Create a household subscription

- Area: Household billing
- Surface: staff-console
- Route: `/households/:householdId/billing/subscriptions/new`
- Roles: organization-owner, manager, staff
- Required access: billing.manage
- Maturity: Published and verified on 2026-07-29 against deployed commit `beb3f3c`.

### Who this is for

This guide is for **staff, managers, and owners** starting a subscription on a household billing profile.

### Before you begin

- The household should have a billing profile (BILL-001).
- Know which program and plan the subscription uses.

### Steps

1. Open the **Household billing** tab.
2. Choose **Create subscription** / open `/billing/subscriptions/new` for that household.
3. Select the program, plan, and members covered.
4. Save and confirm the subscription appears on household billing.

### Expected outcome

A subscription record is attached to the household billing profile and ready for billing operations.

### Recovery

If required fields are missing, complete them before saving. If the profile is missing, create it first.

### Related workflows

- BILL-001
- PROG-006
- MEM-004

## BILL-004 — Review and reconcile billing operations

- Area: Billing operations
- Surface: staff-console
- Route: `/billing`
- Roles: organization-owner, manager, staff
- Required access: billing.manage
- Maturity: Published and verified on 2026-07-29 against deployed commit `beb3f3c`.

### Who this is for

This guide is for **staff, managers, and owners** using the studio **Billing** workspace to review operations across households.

### Before you begin

- Select the location at the top of the console.

### Steps

1. Click **Billing** in the left navigation.
2. Review the operations overview, queues, and filters available on the page.
3. Open household or item links when you need detail.
4. Use related actions for one-off charges, reviews, or approvals when those queues show work.

### Expected outcome

You can reconcile studio-level billing activity without guessing which household is involved.

### Recovery

If the page fails to load, confirm location selection and retry. Empty queues are normal when nothing is pending.

### Related workflows

- BILL-002
- BILL-006
- BILL-007

## BILL-005 — Create and refund one-off charges

- Area: Billing operations
- Surface: staff-console
- Route: `/billing`
- Roles: organization-owner, manager
- Required access: billing.manage
- Maturity: Published and verified on 2026-07-29 against deployed commit `beb3f3c`.

### Who this is for

This guide is for **staff, managers, and owners** posting one-off charges or refunds from studio billing.

### Before you begin

- Know the household and reason for the charge or refund.

### Steps

1. Open **Billing**.
2. Start a one-off charge or refund action from the operations workspace.
3. Select the household/profile, amount, and reason fields the form requires.
4. Confirm the result in the operations list.

### Expected outcome

The one-off charge or refund is recorded and visible in billing operations.

### Recovery

If the action is refused, read the on-page reason (permissions, missing profile, invalid amount). Do not double-submit.

### Related workflows

- BILL-004
- BILL-008

## BILL-006 — Resolve billing reviews

- Area: Billing reviews
- Surface: staff-console
- Route: `/billing/reviews/:id`
- Roles: organization-owner, manager
- Required access: billing.manage
- Maturity: Unverified draft. State that the path is provisional, do not claim the current screen was verified, and stop rather than inventing missing details.

### Who this is for

Staff, managers, and owners resolving billing reviews or approvals.

### Before you begin

- An item must exist in the corresponding billing queue.

### Steps

1. Open **Billing**.
2. Open the review or approval item.
3. Take the decision action shown and confirm the outcome.

### Expected outcome

The item leaves the queue in the decided state.

### Recovery

If the item is missing, return to billing operations and refresh filters.

### Related workflows

- BILL-004
- BILL-007

## BILL-007 — Approve billing changes

- Area: Billing approvals
- Surface: staff-console
- Route: `/billing/approvals/:id`
- Roles: studio-owner, organization-owner
- Required access: billing.approve
- Maturity: Unverified draft. State that the path is provisional, do not claim the current screen was verified, and stop rather than inventing missing details.

### Who this is for

Staff, managers, and owners resolving billing reviews or approvals.

### Before you begin

- An item must exist in the corresponding billing queue.

### Steps

1. Open **Billing**.
2. Open the review or approval item.
3. Take the decision action shown and confirm the outcome.

### Expected outcome

The item leaves the queue in the decided state.

### Recovery

If the item is missing, return to billing operations and refresh filters.

### Related workflows

- BILL-006
- BILL-008

## BILL-008 — Configure billing policies and catalog

- Area: Billing setup
- Surface: staff-console
- Route: `/settings/billing`
- Roles: organization-owner, manager
- Required access: billing.manage
- Maturity: Published and verified on 2026-07-29 against deployed commit `beb3f3c`.

### Who this is for

This guide is for **owners and managers** configuring studio billing settings.

### Before you begin

- You need settings access for the organization.

### Steps

1. Open **Settings**, then **Billing** (`/settings/billing`).
2. Review policies and catalog controls on the page.
3. Update the settings you intend to change and save.
4. Return to **Billing** operations to confirm customer-facing effects when relevant.

### Expected outcome

Studio billing configuration matches the policies you set.

### Recovery

If save fails, keep the form open and correct the fields named in the error.

### Related workflows

- BILL-004
- BILL-005
- INT-001

## BILL-009 — Manage the studio subscription

- Area: PositiveForm subscription
- Surface: staff-console
- Route: `/settings/subscription`
- Roles: studio-owner, organization-owner
- Required access: organization.owner
- Maturity: Published and verified on 2026-07-29 against deployed commit `beb3f3c`.

### Who this is for

This guide is for **organization owners** managing the PositiveForm studio subscription.

### Before you begin

- You need owner access for the organization.

### Steps

1. Open **Settings**, then **Subscription** (`/settings/subscription`).
2. Review the current plan and status.
3. Use the available actions to change plan, update payment method, or open the billing portal as shown.
4. Confirm the page reflects the new state after any change.

### Expected outcome

You can see and manage the studio’s PositiveForm subscription from settings.

### Recovery

If the portal or plan change fails, stay on the page and retry after confirming network connectivity. Contact PositiveForm support if the plan state looks wrong for a live studio.

### Related workflows

- ONB-001

## COMMS-001 — Work the conversation inbox

- Area: Communications
- Surface: staff-console
- Route: `/inbox`
- Roles: organization-owner, manager, staff
- Required access: email.direct.send
- Maturity: Unverified draft. State that the path is provisional, do not claim the current screen was verified, and stop rather than inventing missing details.

### Who this is for

Staff working inbound conversations for the active studio.

You need `email.direct.send` (and a connected email channel).

### Before you begin

- Email connection setup when needed (INT-002).

### Steps

1. Open **Inbox** (`/inbox`).
2. Use filters: **All**, **Unread**, **Triage**, **Unassigned**, **Mine**.
3. Adjust **Unread notification frequency** when needed.
4. Open a conversation from the **Conversations** list.
5. Assign, mark, or **Reply by email**, then confirm the thread reflects the change.

### Expected outcome

Replies and assignment/read state land on the correct tenant conversation.

### Recovery

If the list is empty, confirm the email connection and that traffic has arrived.
Filters can hide open work; try **All**.

### Related workflows

- INT-002 — email connection.
- CRM-002 — lead context.

## CRM-001 — Create a lead and schedule a trial

- Area: Leads and trials
- Surface: staff-console
- Route: `/leads/new`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `a15afd7`.

### Who this is for

This guide is for **sales staff, managers, and owners** capturing a new inquiry
and booking their first trial class.

You need permission to change members/leads for the selected location.

### Before you begin

- Select the location at the top of the console. New leads are created there.
- Have at least a first name. Email, phone, and source help follow-up later.

### Steps

1. Click **Leads** in the left navigation.
2. Open **New lead** (or go to `/leads/new`). The page heading is **New lead**.
3. Fill **First name *** (required). Optionally fill **Last name**, **Email**,
   and **Phone**.
4. Under **How did this inquiry arrive?**, choose the source from the combobox.
5. Optionally add **Conversation notes**.
6. If a trial is already planned, set the trial date with the date fields or
   **Show date picker**, and tick the program checkboxes that apply (for example
   **Youth Martial Arts** or **Little Dragons**).
7. Click **Save lead**. Use **Cancel** to leave without saving.

### Expected outcome

The lead appears in the pipeline with any scheduled trial date and program. You
can open it from **Leads** or **All leads** and continue follow-up in
CRM-002.

### Recovery

If **Save lead** refuses, check **First name *** and location selection. Nothing
is created until save succeeds. If you created a duplicate, archive one from
CRM-005 rather than deleting history.

### Related workflows

- CRM-002
- CRM-004

## CRM-002 — Work the lead pipeline

- Area: Leads and trials
- Surface: staff-console
- Route: `/leads`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Unverified draft. State that the path is provisional, do not claim the current screen was verified, and stop rather than inventing missing details.

### Who this is for

This guide is for **sales staff** moving leads through follow-up and trial, and
recording what was said.

### Before you begin

- Select the location. The board only shows leads for that location.
- Open from **Leads** for the active board, or **All leads** for the full table.

### Steps

1. Click **Leads**. The pipeline board lists cards such as trials and inquiries
   with follow-up dates.
2. Optionally narrow with **Stage filter** or date range controls.
3. Click a card (for example a trial with a follow-up date) to open the lead.
4. Use **Edit** / **Save** on fields you change.
5. To email from the lead, fill **Email subject**, **Reason for email**, and
   **Email message**, then click **Preview email**. Check the final From, To,
   CC, Reply-To, subject, HTML, and plain-text version. Optionally click **Send
   test to me**; when the render is correct, click **Send email**.
6. To log an activity, type **What happened?** and click **Log**.
7. When ready to enroll, open **Convert to member** (CRM-004).
8. Use **Schedule a trial** when the lead needs a trial date, or **End** when
   the opportunity is closed without conversion.
9. Return via **All leads** or the board as needed.

### Expected outcome

Pipeline stage, follow-up dates, trial attendance notes, and communications
persist on the same lead.

### Recovery

If a card is missing, check **Stage filter**, dates, and location. If email
send fails, keep the draft text and fix the reason or address before retrying.

### Related workflows

- CRM-001
- CRM-003
- COMMS-001

## CRM-003 — Review a lead record

- Area: Leads and trials
- Surface: staff-console
- Route: `/leads/:leadId`
- Roles: organization-owner, manager, staff
- Required access: organization.membership
- Maturity: Unverified draft. State that the path is provisional, do not claim the current screen was verified, and stop rather than inventing missing details.

### Who this is for

This guide is for **staff** opening one lead to review lifecycle, activity, and
next actions.

### Before you begin

- Have a lead id from the board or **All leads**, or open a card from
  CRM-002.

### Steps

1. Open the lead (from the board, **All leads**, or a deep link
   `/leads/:leadId`).
2. Review **Overview** for contact and stage context.
3. Open **Trials** for scheduled trial classes.
4. Open **History** for logged activity.
5. Use **Edit** / **Save** for field changes. For outbound mail, choose **Send
   email**, finish the draft, click **Preview email**, verify the final render,
   and then click **Send email**. Use **Log** with **What happened?** for notes.
6. When ready, choose **Convert to member** or **End**.

### Expected outcome

The canonical lead route shows permitted details and the next action without
jumping through unrelated pages.

### Recovery

If the lead 404s, confirm the id and location. If a tab is empty, that can be
normal for a brand-new inquiry.

### Related workflows

- CRM-002
- CRM-004

## CRM-004 — Convert a lead

- Area: Leads and trials
- Surface: staff-console
- Route: `/leads/:leadId/convert`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `beb3f3c`.

### Who this is for

This guide is for **sales staff** turning a ready lead into a member without an
accidental person match.

### Before you begin

- The lead should be at the right stage (usually after trial or clear intent).
- Decide whether this is a **new** member or a **returning** member you will
  select deliberately.

### Steps

1. Open the lead and click **Convert to member** (route
   `/leads/:leadId/convert`). The heading is **Convert to member**.
2. Choose one:
   - **Create new member** (default): makes a new household/person without
     matching prior data.
   - **Reactivate existing member**: only after you find and select a returning
     member on purpose.
3. Set **Program enrollment** if the form requires it.
4. Confirm age with **This person is 18 or older** when applicable, or complete
   date of birth fields for a minor.
5. Click **Create member and enroll**.
6. Use **Back to lead** if you need to cancel.

### Expected outcome

The lead leaves the active pipeline and the resulting member opens. No implicit
person match is invented for you.

### Recovery

If create fails, read the form error and fix required fields. Do not switch to
**Reactivate** unless you have selected the correct existing member.

### Related workflows

- CRM-003
- MEM-001

## CRM-005 — Find and archive leads

- Area: Leads and trials
- Surface: staff-console
- Route: `/leads/all`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `beb3f3c`.

### Who this is for

This guide is for **staff** searching the full lead table and archiving or
restoring leads without deleting history.

### Before you begin

- Prefer the **Lead board** for day-to-day work; use **All leads** when you need
  search across stages.

### Steps

1. From **Leads**, open **All leads** (heading **All leads**).
2. Type into **Search leads** to narrow by name or contact.
3. Use **Stage filter**, and **Active** / **Archived** toggles as needed.
4. Sort with column headers such as **Name**, **Created**, or **Last activity**.
5. Page with **Previous** and **Next**.
6. Open a lead name to review it, or use **Actions for …** on a row to archive
   or restore without destroying the record.
7. Return to **Lead board** or **Recent leads** when finished.

### Expected outcome

Search and status filters keep context. Archived leads leave the active pipeline
without deleting history.

### Recovery

If results look empty, clear search and set **Active**, then confirm location.
If you archived the wrong lead, restore it from the **Archived** view.

### Related workflows

- CRM-002
- CRM-003

## DASH-001 — Review the studio dashboard

- Area: Dashboard
- Surface: staff-console
- Route: `/`
- Roles: organization-owner, manager, staff
- Required access: organization.membership
- Maturity: Published and verified on 2026-07-26 against deployed commit `ede253b`.

### Who this is for

Owners, managers, and staff who need a quick view of studio activity and setup
status for the current workspace.

Required access: membership in the selected organization.

### Before you begin

- Select the organization you intend to review.
- Select the operating location if the organization has more than one.

### Steps

1. Select **Dashboard** in the main navigation, or open `/`.
2. Confirm the organization name and, when shown, the location selector in the
   application shell.
3. Review the **Members**, **Check-ins today**, and **Trials today** summary
   cards.
4. Review **Studio check-in activity** for the last 12 weeks.
5. If **Get set up** appears, review its completion count and open the next
   incomplete task you intend to work on.

### Expected outcome

The dashboard shows organization- and location-scoped summary counts, recent
check-in activity, and any remaining setup work.

### Recovery

- If the counts belong to another workspace, stop and correct the organization
  or location selector before opening a record.
- If a card cannot load, use its retry control when one is offered. Send an escalation report if the same card still fails after one retry.
- A missing **Get set up** section means every setup task is complete; it is not
  an error.

### Related workflows

- ATT-002 — Record a member check-in from the attendance workspace.
- SET-001 — Open the full settings index for studio configuration.

## ENR-001 — Add a member to a program roster

- Area: Program enrollment
- Surface: staff-console
- Route: `/programs/:slug/members`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `40f78fa`.

### Who this is for

This guide is for **desk staff, managers, and owners** putting somebody into a
class.

Two things the page tells you that are worth taking at face value:

- **Members can be planned into inactive programs.** You can build next term's
  roster before the program opens.
- **No capacity rule blocks this roster.** PositiveForm will not stop you at a
  number. If a class is full, the waitlist is how you say so.

Enrolling somebody does **not** charge them. Money is a household subscription;
see MEM-004.

You need the `members.change` permission.

### Before you begin

- The person needs a member record; see MEM-001.
- Decide whether they are joining the roster now or waiting for a spot.

### Steps

1. Open the program and click the **Members** tab. It reads **Current program
   enrollment and roster status**.
2. Search for the person. The page is explicit that **nobody is selected until
   you pick them**, so a name in the box on its own does nothing.
3. Pick them from the results.
4. Choose the status they join with: on the roster now, or on the waitlist.
5. If they already have a current program, choose **Add this program** or **Change programs**. Change programs asks which current program to stop.
6. Read **Before you confirm**. It answers whether this replaces another enrollment, adds to another, changes billing, or enrolls anyone else in the household.
7. Finish with **Review price & enroll** when billing should start, or **Enroll without billing instead** / **Enroll member** for roster-only.

You can also start this from the program **Overview** with **Enroll**, or from
**Action items** while the roster is empty.

### Expected outcome

PositiveForm confirms with **Member added**, or **Member waitlisted** if they
are waiting for a spot, and they appear **once** on the roster.

The roster table shows them with their **Status**, the date **Enrolled**, any
**Scheduled end**, and an **Actions** menu. The program header's active member
count updates to match.

Roster-only enroll does not charge. If you continue to price review, that is
the household subscription; the two stay separate until you confirm there.

To change their status later, or to work across programs, use the enrollment
queue; see ENR-002.

### Recovery

If nothing happens when you click **Add member**, you have typed a name but not
picked anybody. The page warns about exactly this: a search is not a selection.

If the person is not in the results, they have no member record at this location
yet; see MEM-001.

If somebody is on the roster twice, they were added under two enrollments rather
than one changing status. Take one off in
ENR-002 rather than leaving both.

If you added them to the wrong program, change their status to withdrawn on the
wrong one and add them to the right one. Withdrawing records that they left,
which is more honest than deleting the trace.

If a family says they were charged for a class they are not in, or not charged
for one they are, that is the household subscription rather than this roster; see
MEM-004.

### Related workflows

- ENR-002 — change roster status, across every
  program at once.
- MEM-004 — the billing side,
  which this does not touch.

## ENR-002 — Manage the enrollment queue

- Area: Program enrollment
- Surface: staff-console
- Route: `/enrollments`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `2463703`.

### Who this is for

This guide is for **desk staff, managers, and owners** working across every
program at once: filling spots from the waitlist, moving people between classes,
and recording who has left.

The page states its own boundary at the top: this is **the operational roster
across your studio, and billing is managed separately at the household**.
Nothing you do here charges or refunds anybody.

You need the `members.change` permission.

### Before you begin

- Know which of the four statuses you mean. PositiveForm spells out the
  consequence of each before you confirm, and the difference matters at
  check-in.
- Know that only **withdrawn** is treated as strong. The others are ordinary
  changes you can reverse.

### Steps

1. Click **Enrollment** in the left navigation. The page opens as **Program
   enrollment**.
2. Narrow the list with the controls across the top:
   - **Find a member** to search by name.
   - **All programs** to focus on one class.
   - The status filter, which opens on **Active**.
   - **All member records** to include or exclude people who are no longer
     current members.
3. Read the columns: **Member** with their member status beneath, **Program**,
   **Status**, **Enrolled**, **Ended**, and **Manage**.
4. Click **Manage** beside somebody to change their roster status. Choose one of:
   - **Make active** — rejoins the active roster and is expected at check-in
     again.
   - **Move to waitlist** — leaves the active roster and waits for a spot; not
     expected at check-in until made active again.
   - **Mark inactive** — leaves the active roster and is not expected at
     check-in.
   - **Withdraw from program** — off the roster and no longer expected at any of
     its check-ins. This is the strongest status here and records that they left.
5. Read the consequence PositiveForm shows, then confirm. The first three say
   plainly that they can be changed back at any time.
6. Move through long lists with **Previous** and **Next**.

### Expected outcome

PositiveForm confirms by naming the person and what changed, for example that
they are now active, now on the waitlist, now inactive, or have been withdrawn.

The row updates to the new status, and the program's active member count follows.
Somebody made active is expected at check-in again; somebody waitlisted,
inactive, or withdrawn is not.

Your filters are held in the page address, so the queue you are working through
survives opening a member and coming back, and can be handed to a colleague.

Nothing is charged, refunded, or cancelled. A family who stops attending keeps
being billed until their household subscription changes; see
MEM-004.

### Recovery

If somebody is missing, check the status filter first. It opens on **Active**,
so anyone waitlisted, inactive, or withdrawn is hidden until you widen it. Then
check **All programs** and **All member records**.

If you see **This enrollment changed while the confirmation was open. Review the
roster and try again.**, somebody else changed the same enrollment while your
dialog was open. Nothing was applied. Close it, look at the current state, and
decide again.

If you are asked to **Choose the last day of the subscription.**, an end date is
required for what you selected.

If you withdrew somebody by mistake, make them active again. Withdrawn is the
strongest status but not irreversible; it records that they left, and the record
of that stays, which is the point.

If a family complains about being charged after leaving, look at the household
membership rather than this queue. This page is the roster; it does not move
money.

### Related workflows

- ENR-001 — put somebody on a roster in the
  first place.
- MEM-004 — stop the billing that
  a roster change does not touch.
- PROG-003 — change the program
  itself, which does not change who is enrolled.

## HH-001 — Create and find a household

- Area: Households
- Surface: staff-console
- Route: `/households`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `015f371`.

### Who this is for

This guide is for **desk staff, managers, and owners**.

A household is the family. It is what billing attaches to, what siblings share,
and what a parent belongs to even when they do not train themselves. Most people
get one automatically when they are created as a member; you come here when you
need to make one directly, or to find an existing one.

You need the `members.change` permission.

### Before you begin

- Check the location picker at the top of the console. Households are listed for
  the selected location.
- Consider whether you actually need a new household. Adding somebody as a member
  creates one for them; creating a second household for the same family is the
  common mistake, and it splits their billing.

### Steps

#### Find a household

1. Click **Households** in the left navigation. The count under the heading
   tells you how many exist at this location, for example **5 total**.
2. Type into **Search households** to narrow the list by name.
3. Use **All statuses** to include or exclude households that are no longer
   active.
4. Read the columns: **Name** with its location beneath, **Members**, **Adults**,
   **Status**, and **Last change**.
5. Click a household to open it. It opens on its **People** tab, beside
   **Billing** and **Settings**.
6. Move through long lists with **Previous** and **Next**; the counter between
   them shows where you are.

#### Create a household

7. Click **Add household**.
8. Give it a name families and staff will recognise, usually the family name.
9. Save it.

### Expected outcome

A new household opens in its **People** workspace, ready for you to add the
family; see HH-002.

It is immediately findable: it appears in this list at the selected location,
with its member and adult counts, and it can be searched for by name.

The household is what a billing profile attaches to, so it is the record you
return to when the family starts paying; see
BILL-001.

### Recovery

If a household you expect is missing, check the location picker first. The list
is scoped to one location, and a family that moved is at the other one.

Then clear the search box and set **All statuses**, in case a filter left from
earlier is hiding it.

If you find two households for the same family, do not delete either until you
have looked at both. One of them probably has the billing profile and the
history; see HH-005 before removing anything.

If creating fails, PositiveForm shows the reason and nothing is created.

If **Add household** is missing, your role does not carry `members.change`.

### Related workflows

- HH-002 — put the family into the household you just
  made.
- BILL-001 — set up who pays for this
  household.

## HH-002 — Add an existing person to a household

- Area: Households
- Surface: staff-console
- Route: `/households/:householdId/people`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `015f371`.

### Who this is for

This guide is for **desk staff, managers, and owners** putting somebody who
already has a record into a household: a sibling who started separately, a
parent added later, or a member whose family you are only now setting up.

The point of doing it this way is that it **moves** the existing person rather
than making a second copy of them. Their attendance, ranks, and history come with
them.

You need the `members.change` permission.

### Before you begin

- Search for the person first, so you know they already exist. If they do not,
  create them new instead; see MEM-001.
- Know that PositiveForm only offers people who are not already in a household.
  Somebody already placed will not appear in the search, which is what stops you
  putting one person in two families.

### Steps

1. Click **Households** in the left navigation and open the household. It opens
   on its **People** tab.
2. Click **Add**.
3. Choose **Add existing member**, which moves someone who already has a record
   into this household. The other choice, **New member**, creates a person from
   scratch and adds them here.
4. Type at least two characters into **Search by name or email**. Until then
   PositiveForm says **Start typing to search.**, and while it looks it says
   **Searching…**
5. Find the person in the results and click **Add** beside them.

### Expected outcome

PositiveForm confirms with **Added to household**, and the person appears once in
this household's people list with their household role shown.

They are moved, not copied. Their member record, attendance, ranks, and history
are unchanged and now sit inside this family, which is what makes shared billing
and sibling handling work.

The household's member and adult counts update to match. Continue in
HH-003 to set their relationships and emergency
contacts, or in MEM-002 to finish their
own profile.

### Recovery

If the search says **No unassigned members found.**, the person is already in a
household. That is the safeguard working. Open their member record to see which
household they are in, and move them from there rather than adding a duplicate
here.

If nothing happens as you type, you have entered fewer than two characters.

If adding fails, PositiveForm shows **Couldn't add that person.** with the
reason, and nothing changes.

If you added the wrong person, remove them from this household; see
HH-003. Removing them from a household does not
delete them.

If you cannot find somebody who genuinely has no household, check the location
picker: you may be looking at a different location's people.

### Related workflows

- HH-003 — set relationships, emergency contacts,
  and remove somebody added by mistake.
- MEM-002 — finish the person's own
  profile once they are in the right family.

## HH-003 — Manage household people and safety context

- Area: Households
- Surface: staff-console
- Route: `/households/:householdId/people`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `015f371`.

### Who this is for

This guide is for **desk staff, managers, and owners** keeping a family's people
correct, and recording who to call in an emergency.

Read this part before you start. An emergency contact is **information**, not
authority. Recording someone here does not let them pay, sign, collect a child,
or reach the family portal. Those are separate and are granted elsewhere. Writing
"can collect" in a contact record does not make it true in PositiveForm.

You need the `members.change` permission.

### Before you begin

- Open the right household. Everything here is scoped to this one family.
- For an external emergency contact, have their first name, last name, and phone
  number. All three are required, because a contact you cannot ring is not a
  contact.

### Steps

#### See who is in the household

1. Click **Households** in the left navigation and open the household. It opens
   on **People**, beside **Billing** and **Settings**.
2. The header shows the household name, its status, its location, and how many
   people are in it. **Needs attention** appears above the list when something is
   outstanding, naming the person and what is missing, for example a waiver.
3. Each person is listed with their household role, for example **Adult**, and
   any outstanding item against them.

#### Add somebody

4. Click **Add**, then choose **New member** to create a person from scratch, or
   **Add existing member** to move somebody who already has a record; see
   HH-002.

#### Record an emergency contact

5. Click **Add emergency contact** beside the person it is for.
6. Choose either another adult already in this household, or an external contact.
   You have to choose one; PositiveForm will not accept neither.
7. For an external contact, fill in their first name, last name, and phone
   number.
8. Choose the **relationship**. If you pick the "other" option, describe it
   briefly, so the next person reading it knows who this is.
9. Save the contact.

#### Remove somebody

10. Click **Remove** beside the person. This takes them out of this household.

### Expected outcome

Adding a person confirms with **Adult added** or **Person added** depending on
their household role, and they appear once in the list. Moving an existing person
in confirms with **Added to household**.

An emergency contact confirms with **Emergency contact added** and is attached to
the person it is for, so whoever is on the desk can find it against the child
rather than having to know the family structure.

Removing confirms with **Removed from household**. It removes them from this
family; it does not delete the person, and their attendance, ranks, and history
survive.

None of this grants authority. A recorded contact cannot pay, sign, or reach the
portal on the strength of being listed here.

### Recovery

If an external contact is refused, PositiveForm says **An external contact needs
a first name, last name, and phone number.**

If neither an in-household adult nor an external contact is chosen, it says
**Choose a household adult or an external contact.**

If you pick the "other" relationship and leave it blank, it says **Briefly
describe this relationship.**

If saving fails, you get **Couldn't add that emergency contact.** and nothing is
recorded.

If you removed the wrong person, add them back; see
HH-002. Nothing was deleted.

If **Needs attention** names a waiver, that is an agreement rather than a person
problem; see HH-004.

### Related workflows

- HH-002 — move somebody who already has a record into
  this household.
- HH-004 — clear the agreements **Needs
  attention** is asking about.

## HH-004 — Manage member agreement completion

- Area: Households
- Surface: staff-console
- Route: `/households/:householdId/people`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `2ac7ef8`.

### Who this is for

This guide is for **desk staff, managers, and owners** getting a family's
paperwork signed and on file.

There are three ways a member's agreement gets completed, and the household's
People tab offers all of them:

- **Sign here** — the family signs on your device, at the desk, now.
- **Request signatures** — PositiveForm emails a signing link they use later.
- **Upload** — you scan or photograph paper they already signed.

You need the `members.change` permission.

### Before you begin

- Publish the agreement first. Nothing can be sent or signed until a published
  agreement exists; see AGR-001.
- Set the signing sequence if you are sending a packet rather than one document;
  see AGR-002.
- For a request, have the signer's email address, usually the parent's.
- For an upload, have the signed document as a file, and know who signed it.

### Steps

1. Click **Households** in the left navigation and open the household. It opens
   on **People**.
2. Read **Needs attention** at the top. It names who is outstanding and what for,
   for example that somebody needs a waiver on file.

#### Sign at the desk

3. Click **Sign here** and hand the device to the person signing.

#### Email a signing link

4. Click **Request signatures**.
5. Choose what to send under **Send**: the configured packet, or a single
   **Agreement** picked from the published ones.
6. Under **Covers**, pick the people this signature covers. One signature can
   cover more than one child in the family.
7. Enter the signer's address under **Send to**, for example a parent's.
8. Send it. PositiveForm emails a signing link; the signer completes it in their
   own browser, as described in
   AGR-003.

#### Upload signed paper

9. Choose the upload option for the person, pick the file, and record who signed
   it.
10. Upload it.

### Expected outcome

**Needs attention** stops naming the person once their agreement is complete, and
their row shows the agreement satisfied rather than outstanding.

Uploading confirms with **Waiver uploaded**. Requesting signatures confirms that
the request was created, and the member stays outstanding until the signer
actually completes it, which is the honest state rather than an optimistic one.

The signed document itself is stored against the member. The status is visible to
staff who can see the household; the document is not broadcast more widely than
that.

### Recovery

If PositiveForm says **Configure the signing sequence first.**, no packet order
exists yet; see AGR-002.

If it says **Pick at least one published agreement.** or **Pick a document.**,
choose what to send. A draft agreement cannot be sent; publish it first in
AGR-001.

If it says **Pick at least one person.**, choose who the signature covers.

If it says **Enter the signer's email address.**, the request has nowhere to go.

If it says **Choose a file.**, or names a problem with the file, the upload was
refused before it started. The server has the final say on what it accepts, so
its message wins over anything the page guessed.

If the upload fails, you get **Couldn't upload that waiver.** and nothing is
stored.

If a family says they signed but the member is still outstanding, check whether
the signing link was actually completed. A sent request is not a signature.

### Related workflows

- AGR-001 — write and publish the
  agreement being signed.
- AGR-003 — what the family sees
  when they open the link you sent.

## HH-005 — Change or retire a household

- Area: Households
- Surface: staff-console
- Route: `/households/:householdId/settings`
- Roles: organization-owner, manager
- Required access: members.delete
- Maturity: Published and verified on 2026-07-29 against deployed commit `2ac7ef8`.

### Who this is for

This guide is for an **organization owner or manager**: renaming a family,
moving them to another location, or removing a household entirely.

These are the household actions that affect everybody in it at once, which is
why they need the `members.delete` permission rather than ordinary member access.

### Before you begin

- Know that moving a household moves **everyone in it**. It is not a way to move
  one person.
- End, or schedule the end of, any live subscription at the current location before
  moving. PositiveForm requires it; see
  MEM-004.
- Before deleting, check what is attached. A household with billing history is
  almost never something you want to remove.

### Steps

1. Click **Households** in the left navigation, open the household, then click
   the **Settings** tab.

#### Rename, restate, or annotate

2. Edit **Household name**.
3. Set **Status**, for example **Active**.
4. Use **Staff notes** for context colleagues need. These are internal.
5. Click **Save changes**.

#### Move the family to another location

6. Read the note under **Home location**. It states what moving does: everyone
   in the household moves, attendance already taken stays where it happened, and
   member numbers do not change.
7. Click **Move to another location**. The **Move household** dialog repeats that
   everyone moves with it, and that a live subscription at the current location has
   to be ended, or set to end, first.
8. Choose a **New home location**. Only other locations are offered.
9. Click **Move household**.

#### Delete the household

10. Scroll to **Delete household** and click it.
11. Confirm.

### Expected outcome

Saving confirms with **Household updated**, and the name, status, and notes
persist.

Moving confirms with **Moved <n> people**, naming how many moved, so you can
check the number against the family you expected. The household and everyone in
it now sit at the new location, and it appears there in the households list.
History does not follow: attendance already taken stays attached to the location
where it happened, and member numbers are unchanged.

Deleting removes the household. People and history that belong elsewhere are not
orphaned by it, and nothing crosses into another organization.

### Recovery

If you clear the name and save, PositiveForm refuses with **A household name is
required.**

If saving fails, you get **Couldn't update that household.** and nothing changes.

If the move fails, you get **That household could not be moved.** with the
reason. The usual reason is a live subscription at the current location: end it or
set it to end, then move again. The household stays where it is meanwhile.

If **Move household** stays disabled, you have not chosen a new location yet.

If the moved count is not the number you expected, you have moved a different
family than you meant to. Check the household name before doing anything else;
moving it back is possible but the same subscription rule applies in reverse.

If the settings are missing, your role does not carry `members.delete`. Ask an
organization owner.

### Related workflows

- HH-001 — find the household, and check whether a
  duplicate is the real problem before deleting anything.
- MEM-004 — end or schedule the
  subscription that is blocking a move.

## IMP-001 — Start a studio import

- Area: Data imports
- Surface: staff-console
- Route: `/settings/imports/new`
- Roles: organization-owner, manager
- Required access: imports.manage
- Maturity: Published and verified on 2026-07-29 against deployed commit `a15afd7`.

### Who this is for

This guide is for **migration staff** creating an isolated import job before any
operational records are written.

### Before you begin

- Download the canonical templates from the imports area when you need file
  shapes.
- Choose the destination location carefully; the job is scoped to it.

### Steps

1. Open **Settings**, then **Data imports** (breadcrumb **Data imports**).
2. Start a new job at **Start a studio import** (`/settings/imports/new`).
3. Choose **Destination location** from the combobox.
4. Click **Create import**. Use **Cancel** to leave without creating a job.
5. After create, the import job opens on its validation path so you can upload
   and map files (IMP-002).

### Expected outcome

An import job exists without creating operational members or households yet.
You are on the job’s validation workspace.

### Recovery

If **Create import** fails, confirm location selection and permissions. Discard
stray jobs from the imports list rather than leaving half-started migrations.

### Related workflows

- IMP-002
- ONB-002

## IMP-002 — Upload, map, validate, and review import data

- Area: Data imports
- Surface: staff-console
- Route: `/settings/imports/:jobId/validation`
- Roles: organization-owner, manager
- Required access: imports.manage
- Maturity: Unverified draft. State that the path is provisional, do not claim the current screen was verified, and stop rather than inventing missing details.

### Who this is for

Migration staff uploading source files into an existing import job.

### Before you begin

- Create a job first (IMP-001).
- Use only authorized studio data in sample files.

### Steps

1. Open the import job from **Data imports**.
2. On **Files**, choose a source type (for example **Programs CSV**).
3. Pick a file, set **File contents** / **Record type**, then **Upload and inspect**.
4. Continue to **Review column mapping** and clear validation issues.
5. Leave uncommitted until IMP-003.

### Expected outcome

Validation reports issues without creating operational records.

### Recovery

If **Upload and inspect** stays disabled, choose a file first.
Use **Discard import** only when abandoning the job (IMP-004).

### Related workflows

- IMP-001 — create the job.
- IMP-003 — commit after validation.
- IMP-004 — discard or roll back.

## IMP-003 — Commit and reconcile an import

- Area: Data imports
- Surface: staff-console
- Route: `/settings/imports/:jobId/reconciliation`
- Roles: organization-owner, manager
- Required access: imports.manage
- Maturity: Unverified draft. State that the path is provisional, do not claim the current screen was verified, and stop rather than inventing missing details.

### Who this is for

Migration staff finishing a validated import job.

### Before you begin

- An import job exists (IMP-001).
- Validation is clear (IMP-002).

### Steps

1. Open the import job from **Data imports**.
2. Continue to reconciliation/commit when validation is clear.
3. Review matches and new records carefully.
4. Commit only when the review snapshot is correct.

### Expected outcome

Operational records are created or matched once; activation sends no bills,
invitations, or messages.

### Recovery

If commit is blocked, return to IMP-002. If the job should
not land, use IMP-004.

### Related workflows

- IMP-002 — validate first.
- IMP-004 — discard or roll back.

## IMP-004 — Discard or safely roll back an import

- Area: Data imports
- Surface: staff-console
- Route: `/settings/imports/:jobId/completion`
- Roles: organization-owner, manager
- Required access: imports.manage
- Maturity: Published and verified on 2026-07-29 against deployed commit `a15afd7`.

### Who this is for

Migration staff abandoning a staged job or rolling back import-owned records.

### Before you begin

- An import job exists (IMP-001).
- Understand whether the job was committed.

### Steps

1. Open the import job from **Data imports** (often on the **Files** step).
2. Open **Discard import**.
3. Confirm discard when the job is still staged and should not commit.
4. For committed jobs, follow completion/rollback controls and review blockers
   before confirming.

### Expected outcome

Discard purges staged personal data; rollback removes only safe import-owned
records and preserves matched or later-used records.

### Recovery

If discard is unavailable, the job may already be committed; use rollback
controls and read blockers carefully.

### Related workflows

- IMP-002 — upload and validate.
- IMP-003 — commit path.

## INT-001 — Connect and operate Stripe

- Area: Integrations
- Surface: staff-console
- Route: `/settings/integrations/stripe`
- Roles: studio-owner, organization-owner
- Required access: integrations.manage
- Maturity: Published and verified on 2026-07-30 against deployed commit `a15afd7`.

### Who this is for

This guide is for **organization owners** connecting Stripe and inspecting
customer-visible billing readiness.

### Before you begin

- You need owner access.
- Never paste provider secrets into ChatGPT or support material.

### Steps

1. Open **Settings › Integrations › Stripe** (`/settings/integrations/stripe`).
   The heading is **Stripe**.
2. Read connection status on the page. Use **Refresh status** after changes.
3. Review product and price columns (**Product**, **Stripe ID**, **Prices**)
   when the account is connected.
4. Use links such as **Settings › Billing setup** for studio catalog policy, or
   **Integrations** to return to the integrations list.
5. **Disconnect** only when deliberately removing the connection.

### Expected outcome

Connection status, charges readiness, product sync, and webhook health are
visible without exposing secret keys in the PositiveForm console or support material.

### Recovery

If status looks stale, **Refresh status**. If disconnect was accidental,
reconnect through the same page and re-check product sync.

### Related workflows

- BILL-008
- BILL-004

## INT-002 — Connect and verify Resend

- Area: Integrations
- Surface: staff-console
- Route: `/settings/integrations/resend`
- Roles: studio-owner, organization-owner
- Required access: integrations.manage
- Maturity: Unverified draft. State that the path is provisional, do not claim the current screen was verified, and stop rather than inventing missing details.

### Who this is for

This guide is for **organization owners** managing Resend email
connection and verification.

### Before you begin

- Owner access is required.
- Create API keys in Resend using **Where to create a key** if you do not have
  one yet. Never put the key in ChatGPT or support material.
- To apply a branded layout to one-off lead and member messages, create and
  publish that template in Resend first. It must contain the `MESSAGE`
  variable. Any other variables must either be one of `RECIPIENT_NAME`,
  `CONTACT_NAME`, `STUDIO_NAME`, or `STAFF_NAME`, or have a fallback in Resend.

### Steps

1. Open **Settings › Integrations › Resend** (`/settings/integrations/resend`).
   The heading is **Resend**.
2. For connection: paste a key only into the live **API key** field in the
   PositiveForm console (not into notes), then click **Verify API key**.
3. Confirm verification success on the page for sending/receiving channels as
   shown.
4. Open **One-off email layout**. Choose a published template under **Default
   one-off message**, then click **Save one-off template**. Choose **No template
   · send plain text** to clear the layout without disabling one-off email.
5. For marketing automation (when that section is present on the same page),
   change status only after reading the acknowledgement text, then save.
6. Return via **Integrations** when finished.

### Expected outcome

PositiveForm verifies each customer-managed channel, uses the selected
published layout for one-off previews, and stores no secret in guide or run
evidence. No secret is stored in ChatGPT or support material.

### Recovery

If verification fails, regenerate the key in Resend and verify again. If the
one-off layout is missing or incompatible, publish it with `MESSAGE` in Resend,
then reselect it and preview the message again. If marketing will not activate,
complete any required connection steps first.

### Related workflows

- COMMS-001
- INT-003

## INT-003 — Configure organization email marketing

- Area: Integrations
- Surface: staff-console
- Route: `/settings/integrations/resend`
- Roles: studio-owner, organization-owner
- Required access: email.marketing.send, email.marketing.manage_consent
- Maturity: Published and verified on 2026-07-29 against deployed commit `beb3f3c`.

### Who this is for

This guide is for **organization owners** managing Resend email
marketing automation.

### Before you begin

- Owner access is required.
- Create API keys in Resend using **Where to create a key** if you do not have
  one yet. Never put the key in ChatGPT or support material.

### Steps

1. Open **Settings › Integrations › Resend** (`/settings/integrations/resend`).
   The heading is **Resend**.
2. For connection: paste a key only into the live **API key** field in the
   PositiveForm console (not into notes), then click **Verify API key**.
3. Confirm verification success on the page for sending/receiving channels as
   shown.
4. For marketing automation (when that section is present on the same page),
   change status only after reading the acknowledgement text, then save.
5. Return via **Integrations** when finished.

### Expected outcome

Marketing status and automation settings change only after explicit acknowledgement and validation.

### Recovery

If verification fails, regenerate the key in Resend and verify again. If
marketing will not activate, complete any required connection steps first.

### Related workflows

- INT-002
- COMMS-001

## LOC-001 — Create a location

- Area: Locations
- Surface: staff-console
- Route: `/locations/new`
- Roles: organization-owner, manager
- Required access: locations.manage
- Maturity: Published and verified on 2026-07-28 against deployed commit `9096f81`.

### Who this is for

This guide is for an **organization owner** or a **manager**: the people who
open a new studio address and need it available to schedules, check-in, and
billing.

You need the `locations.manage` permission. If **Add location** is not on your
Locations page, your role does not carry it. Ask an organization owner rather
than working around it; the server enforces this permission whether or not the
button is visible.

### Before you begin

- Have the street address, city, state, postal code, phone number, and contact
  email for the new site. PositiveForm asks for all of them before it will save.
- Know which time zone the site runs on. Class schedules at this location use
  it, so a wrong choice moves every class.
- Work in the organization the location belongs to. The name in the top-left
  corner is the organization you are adding to.

### Steps

1. Click **Settings** in the left navigation, then click **Locations**.
2. Click **Add location** in the top right. The page changes to a focused setup
   screen titled **Add a location** showing **Step 1 of 2**.
3. Type the name families and staff will recognize into **Location name**, for
   example `Downtown`. **Continue** stays disabled until the name has content.
4. Click **Continue**. **Location details** opens as **Step 2 of 2**.
5. Fill in **Street address**, **City**, **State**, and **Postal code**. Use
   **Address line 2** for a suite, unit, or floor, or leave it empty.
6. Check **Time zone**. It starts on your organization's time zone; change it
   only when this site actually runs on a different one.
7. Fill in **Phone number** and **Contact email**. These are the details
   families see for this site, not your personal contact details.
8. Click **Add location**. It stays disabled until every field above except
   **Address line 2** is filled and the contact email contains an `@`.

Your answers are saved on this device as you type, so you can close the page
part-way through and pick the same draft back up. **Cancel setup** discards that
draft on purpose.

### Expected outcome

PositiveForm confirms with **Location added** and returns you to
**Settings › Locations**, where the new site is listed with its address.

The location is immediately real: it appears in the location picker at the top
of the console, in location filters on records pages, and as a choice when you
schedule programs or check members in.

Because you filled in the address, phone, and contact email during setup, the
new location is listed with its address and no setup reminder. A location still
missing any of those three is listed with a counter such as **0/3 set up**
instead. Continue in LOC-002 to add hours of operation
and staff.

### Recovery

If **Add location** stays greyed out, one required field is still empty or the
contact email has no `@`. Check **Street address**, **City**, **State**,
**Postal code**, **Phone number**, and **Contact email**. **Address line 2** is
the only optional field.

If saving fails, PositiveForm shows **Could not add location** with the reason
from the server, and nothing is created. Correct what the message names and
click **Add location** again. Do not start over from **Settings › Locations**;
your answers are still on the step.

To stop without creating anything, click **Cancel setup** in the top left. That
clears the saved draft and returns you to **Settings › Locations**.

If the location was created but the details are wrong, do not add a second one.
Open it from **Settings › Locations** and correct it there, as described in
LOC-002.

### Related workflows

- LOC-002 — finish this location's profile, set its
  hours, assign staff, or retire it later.
- WORK-002 — switch the console to the new
  location so you see its members, check-ins, and billing.

## LOC-002 — Maintain or retire a location

- Area: Locations
- Surface: staff-console
- Route: `/settings/locations/:publicId`
- Roles: organization-owner, manager
- Required access: locations.manage
- Maturity: Published and verified on 2026-07-29 against deployed commit `9096f81`.

### Who this is for

This guide is for an **organization owner** or a **manager** keeping a site's
details current, and for an **organization owner** closing a site down.

You need the `locations.manage` permission to edit a location. Retiring and
reopening a location are owner-only, so the **Close this location** card appears
only for an organization owner. Retiring also asks you to re-enter your password
or sign-in code before it runs, because it moves live records.

### Before you begin

- Know which location you are changing. One organization can run several, and
  each has its own address, hours, and staff.
- To retire a location, first know where its active work should continue.
  PositiveForm asks you to name a replacement location for the members,
  households, staff assignments, and future programs that live there.
- An organization must keep at least one active location. The last one cannot be
  retired.

### Steps

#### Open the location

1. Click **Settings** in the left navigation, then click **Locations**.
2. Click the location you want to change. Its page opens with a
   **Settings › Locations** breadcrumb and the location name as the heading. The
   organization's default location is marked **Default**.

A location that still needs details shows **Setup · 0 of 3 complete** near the
top, listing what is missing: **Business address**, **Phone number**, and
**Contact email**.

#### Update the profile

3. In the **Profile** card, edit **Name**, **Address**, **Address line 2**,
   **City**, **State**, **ZIP**, **Phone**, and **Contact email**.
4. Set **Scheduling time zone**. Leave it on **Use organization time zone** when
   this site runs on the same clock as the rest of the business, or pick a
   specific zone when it does not. Program schedules at this location follow
   this setting.
5. Click **Save profile**.

#### Set the hours families see

6. In **Hours of operation**, type each day's hours in plain language, for
   example `4:00 PM - 8:30 PM`, or `Closed` for a day you are shut.
7. Click **Save hours**.

#### Check who works here

8. Read the **Staff working here** card. Each person is listed as either
   **Home location** or **Additional work location**, and **Open profile** takes
   you to their staff record.
9. To change who works here, go to **Settings › Staff** and edit the person
   there. A work location says where someone normally teaches; it does not
   grant or restrict their role.

#### Retire a location you are closing

10. Scroll to **Close this location** and click **Retire location**. If the
    button is greyed out with **An organization needs at least one active
    location**, this is your only active site and cannot be retired.
11. Read the **Retire** dialog. It first shows what is still active here, for
    example the number of members, households, staff assignments, and future
    programs. If nothing needs to move it says so.
12. If a replacement is required, click **Continue**, then choose a
    **Replacement location**. Active members and households are rehomed there,
    staff assignments move, and future programs, testing, and open leads
    continue there.
13. Click **Retire location** in the dialog and complete the sign-in check
    PositiveForm asks for.

#### Reopen a location you closed

14. Open the retired location from **Settings › Locations**. Its card now reads
    **Archived location**.
15. Click **Reopen location**.

### Expected outcome

Saving the profile confirms with **Location updated**, and saving hours confirms
with **Hours updated**. Both persist: reload the page and the new values are
still there, and the setup checklist at the top drops the items you completed.
The location's address and hours are what families and staff see for that site.

Retiring confirms with **<location> retired**. The location disappears from
location pickers and page filters and stops being offered on new forms, and the
records you moved now sit at the replacement location. Nothing historical is
rewritten: past attendance, promotions, and invoices stay attached to the
location where they happened. Reopening confirms with **<location> reopened**
and makes the site selectable again.

### Recovery

If saving the profile fails, PositiveForm shows **Couldn't save. Check the
fields and try again.** Your typing is still on screen. Correct the fields and
click **Save profile** again. Saving hours fails with **Couldn't save hours.**
and behaves the same way.

If the fields are greyed out, your role does not carry `locations.manage`. Ask
an organization owner to make the change.

If retiring fails, PositiveForm shows **That location could not be retired.**
with the reason, and the location stays active. The most common reason is that
active work still needs a replacement location; reopen the dialog and choose one.

If you cancel the sign-in check, nothing is retired. Start again from **Retire
location** when you are ready.

Retiring the wrong location is recoverable: open it again and click **Reopen
location**. Records that were rehomed to the replacement location do not move
back on their own, so check the members and staff that were affected.

### Related workflows

- LOC-001 — add a new site before retiring an old one, so
  there is somewhere for its active work to continue.
- WORK-002 — switch the console to another
  location after retiring the one you were working in.

## MEM-001 — Create a member

- Area: Members
- Surface: staff-console
- Route: `/members`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `9096f81`.

### Who this is for

This guide is for **desk staff, managers, and owners** adding somebody who is
starting at the studio.

You need the `members.change` permission. If **Add member** is greyed out, check
you have a location selected before assuming it is a permission problem.

PositiveForm asks a different second question for a child than for an adult,
because a child needs a responsible adult on file. Answer the age question
honestly and the rest follows.

### Before you begin

- Select the location this person trains at, using the picker at the top of the
  console. A member is created at the selected location, and **Add member** will
  not proceed without one.
- Have their first and last name, and their date of birth if they are under 18.
- For a child, have the parent or guardian's first name, last name, and email.
  Those are required, because that adult becomes the household's contact.

### Steps

1. Click **Members** in the left navigation.
2. Click **Add member**. **Add a member** opens, and names the location the
   person will be created at.
3. Under **Who is this person?**, fill in **First name** and **Last name**.
4. Answer the age question. Tick **This person is 18 or older** for an adult.
   Leave it clear for a child and fill in **Date of birth**, which PositiveForm
   requires so it can collect their responsible adult.
5. Continue. The next step tells you which path you are on: **Adult — a
   household will be created automatically**, or **Child — parent or guardian
   details are required**.
6. For a **child**, fill in the parent or guardian's **First name**, **Last
   name**, **Email**, and **Relationship**. **Phone (optional)** is worth adding
   if you have it. This adult is added to the new household and linked to the
   child.
7. For an **adult**, add **Email (optional)** and **Phone (optional)** if you
   have them.
8. Save the member.

### Expected outcome

PositiveForm confirms with **Member added**, and the person opens in their member
workspace, which is where everything about them lives from now on.

They also have a household. An adult gets one created automatically; a child gets
one containing the parent or guardian you named. That household is what billing
attaches to later, which is why the parent's details are required rather than
optional.

The new member appears in the members list at the selected location, with a
member ID, a status of **Active member**, and no rank yet. Continue in
MEM-002 to fill in the rest of their profile, or
check them into a class straight away with
ATT-001.

### Recovery

If you save without both names, PositiveForm refuses with **First and last name
are required.**

If you leave the age question unanswered, it refuses with **Enter a date of birth
or confirm that this person is an adult.** There is no default: a wrong guess
about a child's age would put their record in the wrong shape.

For a child with incomplete adult details, it refuses with **Parent or guardian
first name, last name, and email are required.**

If you see **Set an active location before adding a member.**, no location is
selected. Choose one with the picker at the top and try again.

If saving fails for another reason, PositiveForm shows **Failed to add member**
with the reason, and nothing is created. Your entries stay on screen.

If you created somebody twice, do not delete the duplicate blindly; check which
one has attendance or billing attached first. Search for the name to see both;
see MEM-003.

### Related workflows

- MEM-002 — finish the profile: contact details,
  emergency contacts, rank, and notes.
- ATT-001 — check the new member into a
  class.

## MEM-002 — Maintain member profile and notes

- Area: Members
- Surface: staff-console
- Route: `/members/:memberId/overview`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Unverified draft. State that the path is provisional, do not claim the current screen was verified, and stop rather than inventing missing details.

### Who this is for

This guide is for **desk staff, managers, and owners** keeping a member's record
current, and leaving notes colleagues will need.

The **Overview** tab is the member's home. It answers the questions you have
standing at the desk with somebody in front of you: what rank are they, are they
paid up, when were they last here, who do we call in an emergency.

You need the `members.change` permission to edit. Anyone with a staff login can
read the page.

### Before you begin

- Know that notes here are **internal**. They are for staff, not for the family,
  so write them the way you would want them read back to you.
- Know that the household holds billing and the other people in the family. This
  page is about this one person.

### Steps

#### Open the member

1. Click **Members** in the left navigation, then click the person. Their
   workspace opens on **Overview**, beside **Membership** and **History**.
2. The header carries their name, whether they are **Active**, their rank, and
   the actions you reach for most: **Tag**, **Check in**, **Enroll**, **Send
   email**, **ID**, and **More**. Staff who can send direct email use **Send
   email** to write one operational message, then click **Preview email** to
   inspect the exact envelope and rendered layout. **Send test to me** is
   optional; use **Send email** in the preview only after it looks correct.
   Replies go to the studio sending address. If the member has no email, choose
   a parent who does.

#### Read the state of them

3. **Rank** shows where they are and what is next, for example **Not ranked**
   with **Next: White Belt**.
4. **Next billing cycle** shows what is coming, or says there is none.
5. **Attendance** shows their **Attendance activity** over the **Last 12 weeks**,
   with a legend for **No check-in** and **Checked in**, and **Recent check-ins**
   beneath it. **Full history** opens the complete record.
6. **Progression** names the progression they are on and offers **Set rank**.

#### Keep the details right

7. In **Member details**, click **Edit** to change their information. The card
   shows their **Member ID**, **Barcode**, **Age**, contact details, household,
   and the date they were added. Type a leftover physical card code into
   **Barcode** when that card should check this person in. A missing detail
   reads plainly, for example **No email** or **Age — Not recorded**.
8. An adult's email and phone can show separate marketing preferences as **Not
   recorded**, **Allowed**, or **Opted out**. The controls appear only for the
   channels your role may manage:
   `email.marketing.manage_consent`, `sms.marketing.manage_consent`, or
   `voice.marketing.manage_consent`. Click **Manage email preference**, **Manage
   text preference**, or **Manage call preference**, choose the evidenced
   state, and confirm it. A Resend unsubscribe or imported/provider phone
   suppression stays labeled; restore **Allowed** only after the person gives
   new permission. Enrollment and Resend audience selection do not grant
   permission. Shared family emails or phone numbers have one preference per
   channel, and dependent profiles direct you to the household adult rather
   than creating consent for a child identity.
9. Under **Emergency contacts**, click **Add contact**. An empty card says **No
   emergency contacts on file**, which is worth fixing before somebody is
   standing in front of you needing it.

#### Leave a note

10. Under **Notes**, click **Add note**, write it, and save. **View notes** opens
   the full set; the card shows the recent ones.

### Expected outcome

Edits persist against this member and this member only. Reload the page and the
new values are there.

A saved note appears in the member's **Notes** and in their activity history, so
the next person to open this record sees it in context rather than having to be
told; see MEM-005.

Setting a rank updates the **Rank** and **Progression** cards together, and the
change is recorded in their history.

Nothing here reaches the rest of the household. Contact details for a parent, and
anything about billing, belong to the household record.

### Recovery

If saving fails, PositiveForm shows the reason and keeps your entries on screen.
Correct what it names and save again.

If the edit controls are missing, your role does not carry `members.change`. You
can still read the page.

If contact details are absent for everyone rather than this one person, your
role does not carry the contact-information permission.

If you are on the wrong person, check the name in the header before saving. The
fastest way to the right one is global search; see
SEARCH-001.

If a note was wrong, add a follow-up note rather than trying to erase the record.
The history is the point.

### Related workflows

- MEM-005 — the full timeline this page summarises.
- SEARCH-001 — reach the right member quickly.

## MEM-003 — Browse and filter people

- Area: Members
- Surface: staff-console
- Route: `/members`
- Roles: organization-owner, manager, staff
- Required access: organization.membership
- Maturity: Published and verified on 2026-07-29 against deployed commit `9096f81`.

### Who this is for

This guide is for **any staff member** who needs to find the right person: at
the desk, before a class, or while answering a parent's question.

Everyone with a staff login can open this page. What contact details you can see
depends on your access.

### Before you begin

- Check which location you are working in. The picker at the top of the console
  decides which people this page lists.
- Know whether you want a member or a household adult. Members are here; parents
  and guardians who do not train are in
  PEOPLE-001.

### Steps

1. Click **Members** in the left navigation. The count under the heading tells
   you how many people are listed, for example **3 members**.
2. Choose who to include with the tabs across the top:
   - **Members** lists current members, which is where the page opens.
   - **Inactive** lists people who are no longer active.
   - **Everyone** lists both.
3. Type into **Search name, household, contact, ID, or staff** to find somebody
   directly. It matches more than the name, so a household name or a member ID
   works when a spelling does not.
4. Narrow further with the two filters beside it:
   - **All agreements** limits the list by agreement state, so you can find
     everyone whose paperwork is outstanding.
   - **All progressions** limits it to one progression, so you can work through
     a single belt system.
5. Read the columns: **Name** with the person's location beneath it,
   **Household**, **Member ID**, **Progression**, **Status**, **Agreements**,
   and **Last change**. Click **Name**, **Member ID**, or **Last change** to
   sort by it; **Last change** is the quickest way to see who was touched most
   recently.
6. Use **Check in** in the **Action** column to record attendance without
   leaving the list; see ATT-001.
7. Click a person's name to open their member workspace.
8. Move through long lists with **Previous** and **Next**. The page counter
   between them shows where you are, for example **Page 1 of 1**.

### Expected outcome

The list shows the people at your current location who match the tab and filters
you chose, with the count above and below it agreeing.

Your choices are held in the page address, so the list you are looking at can be
returned to or handed to a colleague, and going back does not lose them.

Contact details are shown only where your access allows. A person you cannot see
details for still appears; the details do not.

### Recovery

If somebody is missing, work through this in order:

1. Check the location picker at the top. The list is scoped to one location.
2. Check the tab. Someone who has left is under **Inactive**, not **Members**.
   An empty **Inactive** tab reads **No members here yet.** with **0 people**.
3. Clear the search box. A search left from earlier narrows everything below it.
4. Check the two filters. An agreement or progression filter left set from
   earlier is the usual reason a familiar name disappears.
5. Check they are a member at all. A parent or guardian who does not train is in
   PEOPLE-001.

If you know part of a name but not where the person sits, use global search
instead; see SEARCH-001.

If no contact details appear for anyone, your role does not carry the
contact-information permission. Ask an organization owner.

### Related workflows

- MEM-001 — add somebody who is genuinely not here yet.
- PEOPLE-001 — find a parent or
  guardian rather than a member.
- SEARCH-001 — search across records when you
  do not know where to look.

## MEM-004 — Manage member lifecycle and subscriptions

- Area: Members
- Surface: staff-console
- Route: `/members/:memberId/subscription`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `9096f81`.

### Who this is for

This guide is for **desk staff, managers, and owners** answering two questions
about one person: what are they paying for, and what are they enrolled in.

Those are genuinely separate, and the page says so twice. **Roster placement is
separate from household billing.** Someone can be on a class roster without a
subscription, and can be paying for a subscription without being on a roster this
term. Neither implies the other.

You need the `members.change` permission to make changes here.

### Before you begin

- Know that money belongs to the **household**, not the member. Payment methods
  and invoices live on household billing, and a subscription connects a plan to
  the people it covers and to the profile that pays.
- Know which you actually need. If a parent asks "why was I charged", that is
  billing. If an instructor asks "is she in the Tuesday class", that is
  enrollment.

### Steps

1. Click **Members** in the left navigation, click the person, then click the
   **Subscription** tab.

#### What they are paying for

2. Read **Billing subscriptions**. There is one card per subscription. With none
   set up it reads **No billing subscriptions yet**, and explains that a household
   subscription connects a plan, the people it covers, and its paying profile.
3. Click **Create subscription** to set one up; see
   BILL-003 for the full walkthrough.
4. Click **Household billing** to reach the household's payment methods and
   invoices. That is where money questions are answered, not here.

#### What they are enrolled in

5. Read **Program records**. With no enrollment it reads **This member is not
   enrolled in a program.**
6. Click **Manage enrollment** to place them on, or take them off, a roster; see
   ENR-002.

### Expected outcome

The tab shows this member's current state on both sides: every billing
subscription that covers them, and every program they are enrolled in, each with
its resulting state rather than a promise.

The boundaries hold. Creating a subscription does not enroll anybody, and enrolling
somebody does not charge anybody. Payment methods and invoices stay on the
household, so a change here never quietly moves money.

### Recovery

If a subscription you expected is missing, check the household rather than this
member. A subscription covers people, and it may be attached to a sibling's
household or to a profile you are not looking at.

If a family says they are paying but the member shows no subscription, follow
**Household billing** before creating a second one. Creating a duplicate
subscription is a money-moving mistake that is harder to unwind than to avoid.

If somebody is in class but shows no program record, they are attending without
being enrolled. Fix it through **Manage enrollment** so rosters and attendance
agree.

If the actions are missing, your role does not carry `members.change`. You can
still read both sides.

### Related workflows

- BILL-003 — create the household
  subscription that covers this person.
- ENR-002 — place them on a roster,
  or take them off one.

## MEM-005 — Review member history

- Area: Members
- Surface: staff-console
- Route: `/members/:memberId/history`
- Roles: organization-owner, manager, staff
- Required access: organization.membership
- Maturity: Published and verified on 2026-07-29 against deployed commit `9096f81`.

### Who this is for

This guide is for **any staff member** who needs to know what actually happened
with a member: when they last trained, when their subscription changed, what a
colleague noted, and in what order.

Everyone with a staff login can read this. It is the record you reach for when
somebody says "but I thought" and you need to know rather than guess.

### Before you begin

- Open the right member first. The history is theirs alone.
- Know that a note you add here is **internal**, for staff rather than the
  family.

### Steps

1. Click **Members** in the left navigation, click the person, then click the
   **History** tab.
2. Read **Activity history**. Entries are newest first, so what happened most
   recently is what you see without scrolling.
3. Narrow it with the filters across the top:
   - **All** shows everything, which is where the page opens.
   - **Notes** shows what staff have written.
   - **Attendance** shows check-ins.
   - **Membership** shows changes to what they are paying for.
   - **System** shows changes PositiveForm recorded itself.
4. To leave a note as you read, use **Add an internal note**, write it, and click
   **Add note**.

### Expected outcome

The timeline loads newest first for this member, and filtering narrows it without
taking you off their record. You stay in their workspace the whole time, so the
name in the header is always the person you are reading about.

A note you add appears immediately in the timeline and on the member's
**Overview**; see MEM-002. Attendance entries match
what check-in recorded; see ATT-001.

A member with nothing recorded yet says so plainly, rather than showing an empty
table you might mistake for a loading failure.

### Recovery

If the history looks empty, check the filter before concluding there is nothing.
**Notes** on a member who has only ever been checked in shows nothing, and that
is correct.

If an expected check-in is missing, the attendance was not recorded, or was
recorded against a different person. Confirm the name in the header, then check
the class it should have been on.

If a note you just added is not there, reload the page. If it is still missing,
it did not save; write it again from **Overview**.

If you cannot see entries you expect a colleague can, your access carries fewer
permissions than theirs. Protected details are hidden rather than blanked, so ask
an organization owner rather than assuming the record is incomplete.

### Related workflows

- MEM-002 — the profile these entries belong to,
  and the other place notes are added.
- ATT-001 — the check-in that produces the
  attendance entries.

## MEM-006 — Print member barcode labels

- Area: Members
- Surface: staff-console
- Route: `/members/barcode-labels`
- Roles: organization-owner, manager, staff
- Required access: organization.membership
- Maturity: Unverified draft. State that the path is provisional, do not claim the current screen was verified, and stop rather than inventing missing details.

### Who this is for

This guide is for **desk staff, managers, and owners** printing a new or
replacement barcode sticker for a member card. Any active staff member who can
open the Members directory can print labels; printing does not change a member
or record attendance.

### Before you begin

- Select the studio location whose members you are working with.
- Use US Letter stock laid out as 12 portrait stickers: 3 columns by 4 rows,
  each sticker 2 by 2.5 inches. These are the white labels for the green
  attendance cards, not Avery 5160 address labels.
- Load no more than one visible Members page at a time. PositiveForm bounds a
  label run to 25 selected members.

### Steps

1. Open **Members** and use the location, search, and filters to reach the
   people who need stickers.
2. Check the selection box beside each member. The header checkbox selects all
   member rows on the current visible page; it does not select staff-only rows
   or people on another page.
3. Click **Print barcode labels (N)**. The barcode travels in browser memory,
   not in the page address or browser storage.
4. Review **Preview**. Each eligible sticker shows the Code 128-B barcode, the
   unique scan code, **Last, First**, and **BLACK BELT CLUB**. Change **Program**
   to print a different program on every sticker, or edit one sticker's program
   line in the preview. Member ID and student number stay off the sticker. No
   email, phone, address, or household contact data is printed.
5. Read **needs attention** before printing. Missing codes, duplicate selected
   codes, unsupported characters, and values longer than 24 characters are
   excluded rather than rendered as blank or unreliable stickers.
6. Click **Print calibration sheet** first. Print it on plain US Letter paper,
   overlay it behind a label sheet, and confirm all 12 outlines align.
7. In the browser print dialog choose **Letter**, **100% scale**, **no margins**,
   and turn browser headers and footers off. Do not choose Fit to page.
8. Load the label stock in the direction required by the printer, click
   **Print N labels**, and apply each sticker to the green attendance card.
9. Scan one finished sticker into **Check-in** before printing a large batch;
   see ATT-002.

### Printing one member's sticker from their profile

You do not need to go through the Members directory selection flow for a
single replacement. From the person's own record:

1. Open the member and click **ID**; see MEM-002.
2. Under **Operational barcode**, PositiveForm shows the scan value distinct
   from **Member ID**, rendered as the same Code 128-B symbology used here. A
   QR code never appears.
3. If the barcode is missing, duplicated, unsupported, or longer than 24
   characters, the dialog names the same reason this page would give under
   **needs attention**, and **Print sticker** stays disabled until an
   authorized operator corrects it.
4. To attach a leftover physical card (for example a Spark code such as
   MS5824584), close this dialog, click **Edit** on Member details, and type
   the scan code into **Barcode**. If that person already has a physical card,
   check **Replace the current physical card with this code** and save.
5. Click **Print sticker**. PositiveForm opens this same barcode-labels page
   preloaded with just that one member, selected exactly as if you had
   checked their row in the directory. Continue from step 4 above.

### Expected outcome

The browser prints one local Code 128-B sticker for every eligible selected
member. The sheet is US Letter with 0.5-inch top and bottom margins, 1.0625-inch
left and right margins, and 0.1875-inch gaps between the three 2-inch columns.
Sheets break after 12 labels. The Members selection is capped at 25.

Printing makes no API write, does not change a barcode, does not check anyone
in, and sends no member or barcode data to a print provider. Only the browser
and the printer selected in its native print dialog receive the rendered page.

### Recovery

If **Print barcode labels** is disabled, select at least one member row and wait
for the directory to finish updating.

If a member appears under **needs attention**, do not make a sticker for that
person. Return to the member record and have an authorized operator correct or
associate the operational barcode. For a duplicate, resolve ownership before
printing either label.

If the calibration outlines do not align, confirm Letter paper, 100% scale, no
margins, and disabled headers/footers. Printer-driver scaling must also be off.
Repeat the plain-paper overlay before using label stock.

If a printed label does not scan, compare the human-readable value with the
member's operational barcode, then repeat calibration and print a single test
label. You can always return to MEM-003 without saving or
changing anything.

### Related workflows

- ATT-002 — scan the finished member
  card through the staffed check-in desk.
- MEM-003 — select a bounded, location-scoped member set.

## ONB-001 — Create a studio organization

- Area: Organization onboarding
- Surface: staff-console
- Route: `/organizations/new`
- Roles: studio-owner, organization-owner
- Required access: clerk.authentication
- Maturity: Published and verified on 2026-07-28 against deployed commit `9096f81`.

### Who this is for

This guide is for a **studio owner** setting PositiveForm up for their business
for the first time. Finishing it makes you the **organization owner**.

You need a PositiveForm account, which you create by signing up. You do not need
an invitation; you are the first person here.

If you are joining a studio that already uses PositiveForm, this is the wrong
guide. Ask them to invite you, then sign in.

### Before you begin

- Sign up and sign in. A signed-in account that belongs to no studio sees
  **You're not part of a studio yet**, with **Setting up a new studio instead?**
  underneath.
- Decide the business name. This is the organization, the whole business, not
  one address.
- Have the street address, city, state, postal code, phone number, and contact
  email for your first location, and know its time zone.
- Have a payment method ready. Setup finishes in Stripe Checkout, and the
  organization is not usable until that payment completes.

### Steps

1. Sign in. On **You're not part of a studio yet**, click **Setting up a new
   studio instead?**. The focused setup screen **Create an organization** opens
   at **Step 1 of 3**.
2. Type your business name into **Organization name**, for example
   `Elite Martial Arts`. **Continue** stays disabled until it has content.
3. Click **Continue**. **Main location** opens as **Step 2 of 3**. This is your
   first studio address; you can add more later.
4. Fill in **Street address**, **City**, **State**, and **Postal code**. Use
   **Address line 2** for a suite, unit, or floor, or leave it empty.
5. Check **Time zone**. PositiveForm guesses from your device, so confirm it is
   the studio's time zone and not yours.
6. Fill in **Phone number** and **Contact email**. These are the studio's
   details that families see.
7. Click **Continue**. **Billing plan** opens as **Step 3 of 3**, showing the
   plan name, the monthly price, and the money-back guarantee period. There is
   one flat plan for the whole organization.
8. Click **Continue to Stripe**. PositiveForm creates the organization and hands
   you to Stripe Checkout.
9. Pay in Stripe. Stripe returns you to PositiveForm, which confirms the payment
   and opens the migration offer described in
   ONB-002.

Your answers are saved on this device as you go, so you can close the page
part-way through and come back to the same draft. **Cancel setup** discards it
on purpose.

### Expected outcome

The organization exists, you are its owner, and the address you entered is its
first location. PositiveForm switches you into the new organization, so its name
is in the top-left corner and the left navigation is available.

After Stripe confirms the payment, PositiveForm takes you to **Bring your data
with you**, the migration offer. Continue from there in
ONB-002, then finish setting the studio up.

### Recovery

If **Continue** stays greyed out on **Main location**, a required field is still
empty or the contact email has no `@`. **Address line 2** is the only optional
field.

If **Billing plan** reads **The PositiveForm plan is unavailable. Please try
again.**, the plan could not be loaded and setup cannot continue. Wait a moment
and reload the page; your answers are saved.

If you cancel in Stripe, PositiveForm tells you **Checkout canceled. Your
organization setup is saved.** and returns you to this wizard. Click **Continue
to Stripe** again when you are ready.

If handing off to Stripe fails, PositiveForm shows **Could not continue to
Stripe Checkout.** Nothing was charged. Try again; if the organization was
already created on the first attempt, PositiveForm reuses it rather than making
a second one.

If Stripe took the payment but PositiveForm could not confirm it, you see
**Could not confirm checkout. Please try again.** Do not pay a second time.
Reload the page, and contact PositiveForm support if the migration offer still
does not appear.

To stop without creating anything, click **Cancel setup** in the top left.

### Related workflows

- ONB-003 — try the product and build a studio
  workspace first, without creating an account.
- ONB-002 — the migration offer and service
  questions that come straight after checkout.
- LOC-001 — add your second and later
  locations once the organization exists.

## ONB-002 — Record migration and service needs

- Area: Organization onboarding
- Surface: staff-console
- Route: `/migration-offer`
- Roles: studio-owner, organization-owner
- Required access: organization.owner
- Maturity: Published and verified on 2026-07-28 against deployed commit `9096f81`.

### Who this is for

This guide is for the **organization owner** who has just created a studio
organization and is deciding how their existing data gets into PositiveForm.

PositiveForm shows you this straight after checkout. You are choosing between
having us move your data for you, and moving it yourself. Neither choice traps
you: you can import your own data later either way.

### Before you begin

- Finish ONB-001 first. This offer opens on its own
  once Stripe confirms your first payment.
- Know roughly what you are moving from, for example another studio management
  system, spreadsheets, or paper.
- Understand the offer is a real one-time charge. Read the amount on screen
  before you click; it is not part of your monthly plan.

### Steps

#### Choose how your data moves

1. Read **Bring your data with you**. It shows the one-time price and what it
   covers: a full white-glove migration from your current system, your next 12
   months of PositiveForm included, and the money-back guarantee extended across
   all 13 covered months. Underneath, it states how much of the price is the
   migration work once the included plan is accounted for.
2. To have PositiveForm do the migration, click **Add migration for
   <amount>**. You are handed to Stripe Checkout. Pay there, and Stripe returns
   you to PositiveForm.
3. To move your own data instead, click **I'll migrate my data myself**. That
   takes you to your dashboard and charges nothing. You can import your own data
   whenever you are ready; see IMP-001.

#### Tell us what to take off your plate

4. After a paid migration, PositiveForm opens **What should we take off your
   plate?**. Answering this is how the handoff gets planned; nothing here is a
   purchase.
5. In **What system are you moving from?**, name your current system, for
   example `Spark Membership`.
6. Under **Where do you need the most help?**, tick any of **Phone answering
   service**, **Social media automation**, **Social content creation and
   management**, and **Advertising campaign management**. Ticking an option
   registers interest; it is not a purchase or a promise that the service is
   available yet.
7. Use **What else would make the biggest difference?** for anything the
   checkboxes miss.
8. Click **Save and continue**, or click **Skip** to answer later.

### Expected outcome

If you paid for migration, PositiveForm records the purchase and opens the
service questions. Returning to the migration offer afterwards shows **Continue
migration intake** instead of the price, so you cannot be charged twice.

Saving the questions confirms with **Migration preferences saved** and takes you
to your dashboard. Your answers reach the PositiveForm team so they can plan the
handoff.

If you chose to migrate your own data, you land on the dashboard with nothing
charged and nothing recorded, and the studio is ready to set up.

### Recovery

If starting the migration checkout fails, PositiveForm shows **Could not start
migration checkout.** Nothing was charged. Try again.

If you cancel in Stripe, PositiveForm tells you **Migration checkout canceled.**
and returns you to the offer.

If your payment is still settling, PositiveForm says **Your migration payment is
still processing.** and returns you to the offer. Do not pay again. Check back
shortly; once it settles the offer shows **Continue migration intake**.

If PositiveForm cannot confirm a completed payment, you see **Could not confirm
migration checkout.** Do not pay a second time. Reload the page, and contact
support if the offer still shows the price.

If saving the questions fails, PositiveForm shows **Could not save your
preferences.** with the reason and keeps your answers on screen.

To leave either screen without answering, click **Cancel setup** in the top
left, or **Skip** on the questions. You keep whatever you have already paid for,
and you can answer later.

### Related workflows

- ONB-001 — the organization setup this offer follows.
- IMP-001 — import your own members and households
  when you are migrating your data yourself.

## ONB-003 — Build a studio workspace before creating an account

- Area: Organization onboarding
- Surface: public
- Route: `/setup`
- Roles: studio-owner
- Required access: public
- Maturity: Unverified draft. State that the path is provisional, do not claim the current screen was verified, and stop rather than inventing missing details.

### Who this is for

Studio owners deciding whether PositiveForm fits, who want to see their own
studio in it before committing to anything.

Required access: none. This workflow deliberately has no sign-in step.

### Before you begin

- Nothing to set up, and no account to create.
- Work in one browser on one device. The workspace belongs to that browser
  until you save it, so it will not follow you to your phone.
- Use made-up student details. Real member information belongs in a saved
  workspace, and PositiveForm will ask you to save before it accepts any.

### Steps

1. Open `/setup`. The page explains that no login or plan is required and that
   this browser receives revocable access to one isolated draft.
2. Select **Start setting up your studio**. PositiveForm creates the workspace
   at this point, not when the page loads.
3. Name the studio and work through the setup sections: locations, programs
   and their class times, and the ranks students move through.
4. Add a few sample students to see rosters and ranks populated.
5. Watch the checklist as you go. It tracks what a working studio still needs,
   so you can stop at any point and see how far along you are.
6. When PositiveForm offers to save your progress, choose whether to create an
   account now or keep exploring. It explains which of your actions prompted
   the offer.

### Expected outcome

The studio you built is still there after a reload in the same browser, with
its locations, programs, class times, ranks, and sample students intact. Saving
your progress turns it into a real organization tied to your account; until
then it stays a draft that only this browser can reach.

### Recovery

- If the page reports that draft setup is unavailable, the environment you are
  on has not been configured to provision workspaces. Nothing you can do in the
  browser will get past it; the studio must be created from an account instead
  (ONB-001). This is the state the deployed demo is in, recorded as a guide mismatch
  against this story.
- If the page says the browser cannot securely create a workspace, the browser
  is blocking the storage this workflow needs. Try a normal (non-private)
  window.
- If your studio is missing after a reload, the browser discarded its storage.
  A draft cannot be recovered once that happens; save your progress earlier
  next time to keep it.
- If a limit stops you adding another location, program, class time,
  progression, or student, that is the deliberate ceiling on an unsaved
  workspace, not a fault. Save your progress to continue.
- If you want the studio gone, use the option to discard it rather than
  clearing browser data, so the workspace is revoked on the server too.

### Related workflows

- ONB-001 — Create a studio organization from an account you already have.
- ONB-002 — Tell PositiveForm what moving your existing studio would involve.

## ORG-001 — Maintain organization details and branding

- Area: Organization settings
- Surface: staff-console
- Route: `/settings/organization`
- Roles: studio-owner, organization-owner
- Required access: organization.owner
- Maturity: Published and verified on 2026-07-29 against deployed commit `9096f81`.

### Who this is for

This guide is for the **organization owner**: the person responsible for the
business as a whole, not for one site.

Only an organization owner can open this page. Everyone else sees **Only an
organization owner can view or change these details**, and the server refuses
the change even if the page is reached another way.

These are business-wide details. A single site's address and opening hours are
not here; they live on that location's page under **Settings › Locations**.

### Before you begin

- Have your business phone, contact email, website, and EIN or Tax ID to hand.
  All of them are optional, but a blank one is a blank one; PositiveForm does
  not fill them in for you.
- For images, have an icon and a banner ready as JPEG, PNG, or WebP, each up to
  10 MB. You crop them in PositiveForm, so an oversized image is fine.
- Know that this page does not touch Stripe. Renaming your organization here
  changes the name in PositiveForm only.

### Steps

#### Open the page

1. Click **Settings** in the left navigation, then click **Organization**. The
   page opens as **Organization details**.

#### Change the business name

2. Edit **Organization name** in the **Identity** card. This is the name staff
   and families see across the console.

#### Set the icon and banner

3. In **Organization images**, click **Choose icon** (or **Replace icon** if one
   is already set) and pick an image file. A **Crop organization icon** window
   opens.
4. Drag the image to position it and use **Zoom** until it sits the way you
   want, then click the save button in that window. The icon is square.
5. Choose **Rounded** or **Square** under **Icon shape**. The change applies as
   soon as you click, without saving the rest of the page.
6. Click **Choose banner** and repeat for the wide studio image. The banner is
   cropped to a 3:1 shape.
7. To take an image back off, click **Remove** next to it and confirm.

#### Set the time zone, contact details, and legal identity

8. Under **Scheduling time zone**, pick your **Organization time zone**. Program
   schedules use it unless a location overrides it on its own page.
9. Fill in **Phone**, **Contact email**, and **Website** in the **Contact** card.
10. Fill in **EIN / Tax ID** in the **Legal** card. One EIN covers the whole
    organization; every location operates under it.
11. Click **Save changes**. It stays disabled until you have actually changed
    something.

### Expected outcome

PositiveForm confirms with **Organization updated**, and **Save changes** goes
back to being disabled because there is nothing left unsaved. Reload the page
and the new values are still there.

The name and icon change everywhere the console shows your organization,
including the top-left corner. Images confirm separately as **Organization icon
updated** or **Organization banner updated** as soon as the crop is uploaded, so
they do not wait on **Save changes**.

Nothing here reaches Stripe. Your Stripe account name, customers, and payment
history are unchanged.

### Recovery

If you clear the name and save, PositiveForm refuses with **The organization
needs a name.** Type a name and save again.

If **Contact email** or **Website** is malformed, the field itself is marked
with what is wrong. Fix the marked field, then click **Save changes**. Leaving
either one empty is allowed; only a filled-in malformed value is refused.

If saving fails for another reason, PositiveForm shows **Couldn't save. Check
the fields and try again.** and keeps your typing on screen.

If an image will not upload, the crop window shows the reason, for example that
the file is the wrong format or over 10 MB. Close the window with **Cancel**,
prepare a smaller JPEG, PNG, or WebP, and choose it again. The previous image
stays in place until a new one uploads successfully.

If someone else changes the organization while you have edits open, PositiveForm
tells you on the page rather than overwriting one of you silently. If you try to
navigate away with unsaved edits, it asks before discarding them.

### Related workflows

- SET-001 — the settings home page these
  business-wide details sit under.
- ORG-002 — close the organization down entirely,
  which is the one action on this page that cannot be undone.

## ORG-002 — Delete an organization

- Area: Organization settings
- Surface: staff-console
- Route: `/settings/organization`
- Roles: studio-owner, organization-owner
- Required access: organization.owner
- Maturity: Published and verified on 2026-07-28 against deployed commit `9096f81`.

### Who this is for

This guide is for the **organization owner** who is closing the business, or
leaving PositiveForm, and wants the organization's records removed.

Only an organization owner can do this, and PositiveForm asks you to sign in
again part-way through. This is the most destructive action in the product.

> Deleting removes staff access immediately. After the restore window closes,
> the organization's data is permanently destroyed and cannot be recovered.
> Read this whole guide before you start.

### Before you begin

- Export or record anything you need to keep. Member records, attendance,
  promotions, and invoices go with the organization.
- Cancel active subscriptions in Stripe first. PositiveForm deletes its own
  records only. Your Stripe account, its customers, payment methods, and payment
  history live in Stripe and are not touched, so any active subscription keeps
  charging families until someone cancels it in Stripe.
- Tell your staff. Their access ends the moment the deletion is requested, not
  at the end of the restore window.
- Know the organization's exact name. You have to type it to confirm.
- If you only want to close one site, this is the wrong workflow. Retire that
  location instead, as described in
  LOC-002.

### Steps

1. Click **Settings** in the left navigation, then click **Organization**.
2. Scroll to the bottom of the page, to the red **Danger zone** card. Read both
   paragraphs there. They state what is deleted and what stays in Stripe.
3. Click **Delete organization**. Nothing is deleted yet; this opens the
   confirmation.
4. Tick **I understand this cannot be undone and will begin permanently deleting
   all organization data**.
5. In **Type <organization name> to confirm**, type the organization's name
   exactly as shown in bold on the label. A near miss keeps the final button
   disabled.
6. Click **Permanently delete organization**.
7. Complete the sign-in check PositiveForm asks for. It requires a recently
   re-verified session before it will act.

To stop at any point before step 6, click **Cancel**. That closes the
confirmation and clears the tick and the typed name. Cancelling the sign-in
check also stops the deletion.

### Expected outcome

PositiveForm takes you to a dedicated confirmation screen rather than dropping
you back into the console.

If the organization held member records, the screen reads **<organization> is
scheduled for deletion** and names the date its data is retained until. Until
that date the deletion can still be cancelled by contacting support. After it,
the data is permanently destroyed. Returning to **Settings › Organization**
during this window shows a **Pending deletion** card with the request date and
the restore cutoff, instead of the danger zone.

If the organization held no member records, it is deleted immediately and the
screen reads **<organization> is deleted**. Only a non-identifying offboarding
receipt remains.

Either way the screen repeats the Stripe boundary, and from there you choose
where to go: **Continue to <name>** for another organization you belong to, or
**Set up a new organization instead?** to start a fresh one.

### Recovery

If **Permanently delete organization** stays greyed out, either the
acknowledgement is not ticked or the typed name does not match exactly. The
match is exact, including capitals and spacing.

If you cancel the sign-in check, PositiveForm tells you **Re-authentication was
cancelled. The organization was not deleted.** Nothing changed.

If the request fails, PositiveForm shows the reason from the server and the
organization stays as it was.

If the organization is scheduled for deletion and you have changed your mind,
contact PositiveForm support before the restore window closes. The date is on
the confirmation screen and on the **Pending deletion** card. There is no
self-service undo, and after that date there is no recovery at all.

If you reach this page by accident, leave it. Nothing on this page changes
anything until you click **Permanently delete organization** and pass the
sign-in check.

### Related workflows

- ORG-001 — change the organization's name,
  branding, or contact details instead of deleting it.
- WORK-001 — move to another organization
  you belong to after this one is gone.

## PEOPLE-001 — Review parents and guardians

- Area: People
- Surface: staff-console
- Route: `/people/adults`
- Roles: organization-owner, manager, staff
- Required access: organization.membership
- Maturity: Published and verified on 2026-07-29 against deployed commit `9096f81`.

### Who this is for

This guide is for **any staff member**: owners, managers, and front-desk staff
who need to find the adult attached to a household, usually to answer a question
or to reach a parent.

These are household adults who are **not** direct members themselves. Someone
who trains with you appears as a member, not here, and an adult who both trains
and is a parent appears in both places with the relationship shown.

You need membership in the organization. What contact details you can see
depends on your access.

### Before you begin

- Know what you are looking for: a name, an email, or a phone number. All three
  are searchable.
- Know that this is a directory, not a place to edit. Changes belong on the
  household record.

### Steps

1. Click **People** in the left navigation, then open **Parents & guardians**.
2. Read the columns: **Adult**, **Location**, **Relationships**, and **Status**.
   - **Adult** shows the person's name with their contact details underneath, or
     **No contact details** when none are on file.
   - **Relationships** shows one chip per connection. A parent who does not
     train shows **Adult** alone; someone who is both a household adult and a
     member shows **Adult** and **Member** side by side.
   - **Status** shows **active** for a current member, or **not a member** for a
     parent or guardian who only appears through their household.
3. Type into **Search name, email, or phone** to narrow the list. Your search
   stays in the page address, so you can return to it or share it.
4. Click a person to open their operational record, where their contact
   information and household role are changed.

### Expected outcome

The directory lists the household adults you are permitted to see, with the
identity context your access allows, and each row opens the correct household
record.

Contact details shown here are the ones on file. A blank is a genuine blank, not
something hidden from you, unless your role does not carry the
contact-information permission, in which case those details do not appear at all.

Nothing on this page changes a record. It is a way to find the right person and
get to the place where their details are maintained.

### Recovery

If a search returns **No adults match this search.**, widen it. The search covers
name, email, and phone, so a partial name is often better than a full one, and a
misremembered email finds nothing.

If the page reads **No household adults yet.**, no adult has been added to any
household at this location. Add the household first; see
HH-001.

If someone you expect is missing, check that you are working in the right
location using the location picker at the top, and that they are actually a
household adult rather than a member in their own right; members are in
MEM-003.

If contact details are missing for everyone, your role does not carry the
contact-information permission. Ask an organization owner.

### Related workflows

- HH-001 — create the household an adult
  belongs to.
- MEM-003 — find someone who trains with you
  rather than a household contact.

## PROG-001 — Create a program

- Area: Programs
- Surface: staff-console
- Route: `/programs`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `40f78fa`.

### Who this is for

This guide is for **desk staff, managers, and owners** setting up a class,
course, or camp.

The page states the order it expects: create the offering first, then configure
its people and schedule when ready. You are not asked for everything at once,
and a program with no schedule and no roster is a normal state to leave it in.

You need the `members.change` permission.

### Before you begin

- Select the location the program runs at, using the picker at the top of the
  console. A program belongs to a location, and creating one without a location
  selected is refused.
- Have a name families will recognise. Everything else can wait.

### Steps

1. Click **Programs** in the left navigation. The list shows each program with
   its **Location**, **Status**, **Members**, **Schedule**, **Progression**, and
   an **Actions** menu.
2. Click **Create program**. **Create a program** opens.
3. Fill in **Program name**.
4. Use **Description (optional)** if it helps staff tell similar programs apart.
5. Save it.

### Expected outcome

The program is created at the selected location and opens at its own stable
route, which is what you can bookmark or send to a colleague.

It starts with nothing configured, and the workspace says so rather than hiding
it: **Next meetings** is empty until a schedule exists, **Action items** offers
**Assign staff** and **Enroll member**, and **Pricing** reads **No plans yet.**

In the programs list it shows **Not scheduled** with **No program dates** until
you add class times. That is accurate rather than a warning.

Continue in PROG-002 to give it class times, or
PROG-003 to set its status, dates, and
progression.

### Recovery

If saving is refused with **A program name and location are required.**, either
the name is blank or no location is selected. The location comes from the picker
at the top of the console rather than from the dialog, which is the part people
miss.

If the program was created with the wrong name, rename it on its **Settings**
tab; see PROG-003. Do not create a second one.

If you meant to copy an existing program rather than start fresh, duplicating is
faster and carries the schedule, staff, and tags across; see
PROG-004.

### Related workflows

- PROG-002 — give the program its class times.
- PROG-003 — set its status, start and end dates,
  and advancement progression.

## PROG-002 — Manage program schedules

- Area: Programs
- Surface: staff-console
- Route: `/programs/:slug/schedule/new`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `2463703`.

### Who this is for

This guide is for **desk staff, managers, and owners** setting when a program
actually meets.

There are two kinds of schedule, and picking the right one saves work:

- **Weekly** for a class that repeats, for example every Monday and Wednesday at
  4:30.
- A one-time session for a one-off, for example a seminar on a single date.

Separately from either, you can leave a **staff note on one specific date**, for
example "belt gradings tonight" or "no class, public holiday", without touching
the recurring pattern. Those notes live on an existing schedule rather than on
the form that creates one.

You need the `members.change` permission.

### Before you begin

- Know the start and end time. Both are required; a class with no end time
  cannot be scheduled.
- For a weekly schedule, know which weekdays it runs.
- Remember the program's location time zone decides what these times mean; see
  LOC-002 if that is not what you expect.

### Steps

#### Add a schedule

1. Open the program and click **Manage schedule**. **Add a schedule** opens, for
   creating a repeating class or a one-time session.
2. Use **Label (optional)** to name it, for example `After school`. This is how
   staff tell two schedules on the same program apart.
3. Choose a **Schedule type**. **Weekly** repeats; the other option is for a
   one-time session.
4. For a weekly schedule, pick the **Days**. You can pick several at once, or
   **All**, and the page says what that does: choosing multiple days creates one
   schedule for each day.
5. Set **Start** and **End**.
6. Choose **Expected staff (optional)** if you know who normally runs this
   sitting. This is per schedule, and is separate from the program's assigned
   staff in PROG-005.
7. Use **Internal staff note (optional)** for anything staff should see about
   this schedule.
8. Click **Add schedule**, or **Cancel** to back out.

#### Change an existing schedule

9. Open the schedule from the program. It opens as **Edit schedule**.
10. The fields are the same with one difference: **Day** is singular here, and
    the page says why. Each weekly schedule belongs to one day, so the multiple
    day picker exists only when creating.
11. Click **Save schedule**.

#### Leave a note on one date

Meeting notes live on an existing schedule, not on the form that creates one, so
create the schedule first.

12. On **Edit schedule**, scroll to **Meeting notes**, which holds
    date-specific staff reminders for individual class occurrences. With none
    recorded it reads **No date-specific notes yet.**
13. Choose a **Meeting date** and write a **Staff note**, for example a birthday
    or a one-time detail.
14. Click **Add meeting note**. To change one later, use **Edit** beside it,
    which turns the button into **Save meeting note**; **Cancel edit** backs out.
15. Use **Delete** beside a note to remove it. You are asked to confirm.

### Expected outcome

The schedule persists on the program, and the program workspace reflects it
immediately: **Next meetings** lists the next three scheduled class times with
their date, time, label, and whether they repeat.

In the programs list, the program stops reading **Not scheduled** and starts
showing its dates.

A meeting note is attached to that one date. It does not change the recurring
pattern, and it does not create or cancel a class; it is a note staff see against
that day.

Scheduled times are what attendance is taken against; see
ATT-004.

### Recovery

If saving is refused with **Choose both a start and end time.**, one of the two
is empty.

For a weekly schedule, **Choose at least one weekday.** means no days are
selected.

For an ad hoc schedule, **Choose the date for an ad hoc schedule.** means the
date is missing.

For a meeting note, **Choose a date and add the staff note.** means one of the
two is blank. A note with no text is not a note.

If a note will not delete, PositiveForm says **Could not delete meeting note**
and it stays.

If the class times look wrong by a fixed number of hours, the program's location
time zone is not what you assumed. Fix it on the location rather than shifting
every schedule; see LOC-002.

### Related workflows

- PROG-001 — create the program these times belong to.
- ATT-004 — take attendance against the
  scheduled meetings.

## PROG-003 — Manage program lifecycle

- Area: Programs
- Surface: staff-console
- Route: `/programs/:slug/settings`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `40f78fa`.

### Who this is for

This guide is for **desk staff, managers, and owners** deciding what a program
is called, when it runs, whether it is currently offered, and which belt system
it feeds.

The thing worth knowing before you change anything: **an inactive program keeps
its roster**. Members can even be planned into one. Making a program inactive
stops it being offered; it does not unenroll anybody.

You need the `members.change` permission.

### Before you begin

- Know the difference between ending a program and making it inactive. Ending
  is a date; inactive is a status. Neither removes members.
- If you are changing the **URL slug**, know that it is what the program's
  address is built from, so existing links stop working.

### Steps

1. Open the program and click the **Settings** tab.
2. Edit **Program name**.
3. Check the **URL slug**. It is used in the friendly program URL and must be
   unique within your organization.
4. Use **Description (optional)** for context staff need.
5. Add **Program tags (optional)** from your reusable tag list, for example
   `Summer Camp` or `Homeschool Group`. The page says what they are for: they
   are staff labels and **do not limit who can join** the program.
6. Set **Status**, for example **Active**.
7. Set **Starts on (optional)** and **Ends on (optional)** if the program runs
   for a defined period.
8. Choose an **Advancement progression (optional)** to link the belt or level
   system this program feeds. A progression can be reused by several programs,
   and rank changes stay auditable testing work rather than happening silently
   here.
9. Save the settings.

### Expected outcome

The settings persist and the program workspace header reflects them: the name,
the status, the location, and the member and schedule counts.

Changing status changes what is offered, not who is enrolled. An inactive
program keeps its roster and still appears in the programs list with its member
count. Members can be planned into an inactive program, which is how you set up
next term before it opens.

Setting a progression links the program to that belt system without promoting
anybody. Rank changes remain testing work with their own record.

To see who is enrolled after a lifecycle change, use the enrollment queue; see
ENR-002.

### Recovery

If the slug is refused, another program in this organization already uses it.
Slugs are unique per organization; pick a different one.

If saving fails, PositiveForm shows the reason and your edits stay on screen.

If you changed the slug and links now break, change it back. The address is built
from the slug, so an old bookmark or a link you sent a colleague points at the
previous one.

If you made a program inactive expecting the roster to clear, it has not, and
that is deliberate. Change the enrollments themselves in
ENR-002.

If tags are not narrowing who can enrol, they are not meant to. The page says so:
tags are reusable staff labels.

### Related workflows

- PROG-002 — set or change when the program meets.
- ENR-002 — change who is on the
  roster, which a status change does not do.

## PROG-004 — Duplicate a program

- Area: Programs
- Surface: staff-console
- Route: `/programs/:slug/overview`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `40f78fa`.

### Who this is for

This guide is for **desk staff, managers, and owners** setting up next term, a
second time slot, or the same class at a different level.

Duplicating carries the setup across so you are not retyping it. The copy
**always starts inactive**, which is the safeguard: nothing you duplicate can
accidentally start being offered or charged for before you have checked it.

You need the `members.change` permission.

### Before you begin

- Decide whether you want the roster. By default the copy has no members, and
  you can choose to bring the active ones across.
- Know what does not come with it: history never does, and neither do
  waitlisted, inactive, or withdrawn records.

### Steps

1. Open the program you want to copy.
2. Click **Duplicate**. The dialog is titled **Duplicate <program>** and states
   what it does: copy this program's setup into a new one, with members and
   history not copied.
3. Read what the copy includes: program details, schedule, staff, tags, and
   training groups. It always starts inactive.
4. Decide about **Include active members**. Ticking it copies the current active
   roster; waitlisted, inactive, and withdrawn records stay with the original.
   Leave it clear for a genuinely fresh intake.
5. Click **Duplicate program**.

### Expected outcome

PositiveForm confirms with **Inactive program copy created** and opens the copy.

The copy has the original's details, schedule, staff, tags, and training groups,
and it is **inactive**. It is not offered, not charged for, and not on anyone's
list of current classes until you activate it in
PROG-003.

The original is untouched. Duplicating never changes the program you copied
from, whether or not you brought members across.

If you ticked **Include active members**, the copy has the active roster; those
members are now enrolled in both, which is what you want for a continuing class
and not what you want for a new intake.

### Recovery

If duplicating fails, PositiveForm shows **Could not duplicate program** and
nothing is created.

If you copied members and did not mean to, remove them from the copy's roster
rather than deleting the program, in case you have already configured it; see
ENR-002.

If you did not copy members and needed them, duplicating again is cleaner than
adding them by hand, provided you have not configured the first copy yet.

If the copy is not appearing where you expect, check the programs list's status
filter. A copy is inactive, so a list showing only active programs hides it.

Nothing is charged by duplicating. Pricing plans are configured separately; see
PROG-006.

### Related workflows

- PROG-001 — start from nothing instead, when the new
  program shares little with an existing one.
- PROG-003 — activate the copy once it is right.

## PROG-005 — Assign program staff and operating notes

- Area: Programs
- Surface: staff-console
- Route: `/programs/:slug/staff`
- Roles: organization-owner, manager
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `40f78fa`.

### Who this is for

This guide is for an **owner or manager** recording who usually runs a program.

Assigning somebody here says who teaches it. It does not change what they can do
in PositiveForm: permissions come from their roles and direct grants, and they
apply across the organization regardless of which programs they are on. See
STAFF-004 for access.

### Before you begin

- The people you are assigning need staff records already; see
  STAFF-001.
- Know that this is about who normally runs it, not who is rostered on a
  specific date. Date-specific detail belongs in a meeting note; see
  PROG-002.

### Steps

1. Open the program and click the **Staff** tab. It reads **Assigned staff**,
   for the staff who usually run or support this program.
2. Tick each person who runs or supports it. Everyone with a staff record at
   your organization is listed.
3. Click **Save staff**.

You can also reach this from the program **Overview**, where **Action items**
offers **Assign staff** while nobody is assigned yet.

### Expected outcome

The assigned staff persist on the program and stay visible in its workspace, so
anyone opening the program can see who runs it without asking.

The assignment also shows on each person's own profile, under **Teaches**; see
STAFF-002. It is the same fact viewed from
the other side, not a copy.

Nothing about their access changes. Assigning somebody to a program does not
grant a permission, and removing them does not take one away.

### Recovery

If saving fails, PositiveForm shows the reason and the assignment is unchanged.

If somebody you expect is not listed, they have no staff record at this
organization yet; see STAFF-001.

If an assigned instructor cannot do something you expected them to, this is not
the place to fix it. Check their roles and direct grants in
STAFF-004.

If you need to record who is covering one specific class, do not change the
assignment. Leave a meeting note on that date instead; see
PROG-002.

### Related workflows

- STAFF-002 — the same assignment seen
  from the person's profile, under **Teaches**.
- PROG-002 — record a one-off change with a
  date-specific staff note.

## PROG-006 — Manage program pricing plans

- Area: Programs
- Surface: staff-console
- Route: `/programs/:slug/overview`
- Roles: organization-owner, manager
- Required access: billing.manage
- Maturity: Published and verified on 2026-07-29 against deployed commit `40f78fa`.

### Who this is for

This guide is for an **owner or manager** with the `billing.manage` permission,
setting what this program costs.

The critical property: **building the plan catalog charges nobody**. Adding a
plan here makes it available to choose. Money only moves when a household
subscription is created against it; see
BILL-003.

If **Add plan** is not on the page, your role does not carry `billing.manage`.

### Before you begin

- Connect billing first. Plans reference real prices, so the Stripe connection
  needs to be in place; see BILL-008.
- Decide whether you are reusing a price you already have or creating a new one.
  Reusing keeps your catalog tidy and your reporting consistent.

### Steps

1. Open the program. On **Overview**, find **Pricing**, which lists the
   recurring plans available for this program. With none set up it reads **No
   plans yet. Add pricing for this program.**
2. Click **Add plan**.
3. Choose **Attach existing** to reuse a price you already have, then pick it
   under **Existing price**.
4. Or choose **Create new**, then fill in:
   - **Plan name**, for example `Kids Taekwondo Membership`. This is what staff
     see when choosing a plan.
   - **Amount**, for example `150.00`.
   - **Billed**: **Monthly** or **Yearly**.
5. Click **Add plan**.
6. Set a plan as the default if this program has an obvious usual choice.
7. Archive a plan you no longer sell.

### Expected outcome

PositiveForm confirms with **Plan added**, and the plan appears under
**Pricing** as available for this program.

Nobody is enrolled and nobody is charged. The catalog is a list of what a family
*could* be signed up to; the charge happens when a household subscription is
created against a plan.

Setting a default confirms with **Default plan updated** and only changes which
plan is preselected. Archiving confirms with **Plan archived** and stops the plan
being offered for new subscriptions; it does not cancel subscriptions already using
it, and it does not stop those families being billed.

### Recovery

If adding fails, PositiveForm shows the reason and no plan is created.

If the default will not change, you get **Could not update the default plan.**
and the previous default stands.

If archiving fails, you get **Could not archive this plan.** and it stays
available.

If you archived a plan families are still paying on, they keep being billed.
Archiving controls what can be *sold* next, not what is already sold; change
those subscriptions at the household.

If **Existing price** offers nothing, you have no prices yet. Use **Create new**,
or set up billing first; see BILL-008.

If you added a plan at the wrong amount, archive it and add the correct one
rather than editing a price families may already be on.

### Related workflows

- BILL-003 — the step that actually
  charges a family, using a plan from this catalog.
- BILL-008 — connect billing before any of this
  works.

## ROLE-001 — Manage reusable roles

- Area: User roles
- Surface: staff-console
- Route: `/settings/roles`
- Roles: studio-owner, organization-owner
- Required access: organization.owner
- Maturity: Published and verified on 2026-07-29 against deployed commit `9096f81`.

### Who this is for

This guide is for the **organization owner**. Roles decide what your staff can
reach, so only an owner can create, change, or delete them.

A role is a reusable bundle of permissions. A staff member holds everything from
all of their roles, plus any direct grants given to them individually. Roles are
the maintainable way to run this: change the role once and everyone holding it
changes with it.

### Before you begin

- Think in jobs, not people. **Front desk**, **Manager**, and **Billing
  manager** are roles; "Kim's permissions" is not.
- Know that a role never affects ownership. Owners hold every permission, and
  nothing you do here can grant or remove that.
- Locations do not narrow a role. Roles apply across the whole organization, so
  a person's home location does not restrict what their role lets them do.

### Steps

#### See the roles you have

1. Click **Settings** in the left navigation, then click **User roles**.
2. Each role is listed with its description, the permissions it carries, and how
   many staff hold it. A role nobody holds is marked **Not assigned**.

#### Create a role

3. Click **New role**.
4. Optionally pick a **Start from a job template (optional)**, or leave it on
   **Blank custom role**. Choosing a template creates or opens this studio's own
   editable copy; the template itself is never changed.
5. Give it a **Name** that describes the job, for example `Front desk`, and use
   **Description (optional)** to say what the role is for.
6. Tick the permissions it should carry under **Permissions**. They are grouped
   by area, and each one describes what it allows. Read the note above them:
   everyone with a staff login can already view the console, so a role adds
   authority rather than granting basic access.
7. Watch for the **High impact** badge. It marks the permissions worth pausing
   over, such as **Delete operational records**, **Manage studio billing**, and
   **Approve billing changes**.
8. Click **Save role**. PositiveForm checks your choices against the permissions
   the server actually defines, so a role cannot claim something that does not
   exist.

The editor also has a **JSON configuration** tab, which shows the same role as a
definition you can read or paste. Use it when moving a role between studios;
otherwise stay on **Configuration**.

#### Change or copy a role

9. Open an existing role to edit its name, description, or permissions. Saving
   changes it for everyone who holds it.
10. Each role in the list carries actions for editing, duplicating, exporting,
    previewing, and deleting it. Duplicating confirms with **Role duplicated**
    and leaves the original untouched.

#### Move a role between studios

11. Use **Import JSON** to paste a role definition, and the export action on a
    role to copy its definition out. PositiveForm validates the definition and
    shows you what it will create **before** anything is saved, so a bad paste
    is refused rather than half-applied.

#### Delete a role

12. Delete a role you no longer use. Check the assignment count first: deleting
    a role removes the permissions it was giving everyone who held it.

### Expected outcome

Saving confirms with **Role saved**, and the role appears in the list with its
permissions and assignment count. Deleting confirms with **Role deleted**.

The change takes effect for every staff member holding that role, without
touching their other roles or their direct grants. Assign a role to someone from
their profile; see STAFF-004.

Nothing here changes ownership. Owner authority is not editable from this page.

### Recovery

If you save without a name, PositiveForm refuses with **The role needs a name.**

If saving fails, you get **Could not save that role.** with the reason, and the
role is unchanged. Your edits stay on screen.

If a pasted definition is rejected, PositiveForm says **That role definition is
not valid.** or asks you to enter valid role-definition JSON first. Nothing is
created until a definition validates, so a bad paste cannot half-apply.

If deleting fails, you get **Could not delete that role.** and the role stays.

If you delete a role by mistake, recreate it and reassign it to the affected
staff. There is no undo, which is why the assignment count is worth reading
first.

### Related workflows

- ROLE-002 — see the console as a role sees it before
  you give it to anyone.
- STAFF-004 — assign roles to a person and
  inspect what they end up with.

## ROLE-002 — Preview a role safely

- Area: User roles
- Surface: staff-console
- Route: `/settings/roles`
- Roles: studio-owner, organization-owner
- Required access: organization.owner
- Maturity: Published and verified on 2026-07-29 against deployed commit `2ac7ef8`.

### Who this is for

This guide is for the **organization owner** who wants to see the console the
way a role sees it, before handing that role to a person.

A preview is read-only, and the server enforces that, not just the screen. You
cannot change anything while previewing, however the page looks.

### Before you begin

- Create the role first, in **Settings › User roles**; see
  ROLE-001.
- Know that a preview expires on its own after a short window. You do not have
  to remember to end it, though you should.

### Steps

1. Click **Settings** in the left navigation, then click **User roles**.
2. Find the role you want to check and start its preview.
3. PositiveForm confirms with **Previewing <role> in read-only mode** and takes
   you to the dashboard as that role.
4. Move around the console. Navigation, pages, and actions appear as they would
   for someone holding only that role.
5. Read the banner across the top. It names the role you are previewing, says
   the access is server-enforced and read-only, and gives the time the preview
   expires. If the role masks protected data, the banner says that too.
6. Click **End preview** in the banner when you are done.

### Expected outcome

While the preview runs, the banner is on every page, so you can never mistake a
previewed console for your own. Attempts to change anything are refused by the
server, not merely hidden, so previewing cannot damage a record.

**End preview** returns you to your own access immediately. The preview also
ends by itself at the time the banner names, so a forgotten preview does not
leave you stuck.

Nothing about the role changes by previewing it, and no staff member is
affected.

### Recovery

If the preview will not start, PositiveForm shows **Could not start that role
preview.** with the reason, and your access is unchanged.

If you are unsure whether you are previewing, look for the banner. No banner
means you are yourself.

If **End preview** fails, wait for the expiry named in the banner, or reload the
page and try again. Your own access returns either way.

If the console looks more limited than you expected, that is the point: the role
carries fewer permissions than you thought. End the preview and adjust the role
in ROLE-001.

### Related workflows

- ROLE-001 — change the role once the preview shows you
  what it actually reaches.
- STAFF-004 — give the role to a person once
  you are satisfied.

## SEARCH-001 — Find operational records

- Area: Global search
- Surface: staff-console
- Route: `/`
- Roles: organization-owner, manager, staff
- Required access: organization.membership
- Maturity: Published and verified on 2026-07-26 against deployed commit `e268ac1`.

### Who this is for

Owners, managers, and staff who need to move directly to a person, staff
record, household, or program in the current studio.

Required access: membership in the selected organization.

### Before you begin

- Select the correct organization and location.
- Know at least two characters from the name of the record you need.

### Steps

1. Select **Search records** in the application header. You can also press
   `Command-K` on macOS or `Control-K` on Windows and Linux.
2. Enter at least two characters in **Search people, households, or programs**.
3. Review the results under **People**, **Staff**, **Households**, or
   **Programs**. Only groups with matches appear.
4. Select the intended result.
5. Confirm that PositiveForm opens that record's normal detail or workspace
   page and that the organization context has not changed.

### Expected outcome

Matching records are grouped by type, and the selected result opens in its
canonical workspace without changing organizations.

### Recovery

- Enter at least two characters; shorter searches do not run.
- If no result appears, confirm the organization and location, then try a more
  specific name.
- If search reports an error, close and reopen it, then retry once. Preserve the
  visible error and send an escalation report if it continues.

### Related workflows

- MEM-003 — Review the member record opened from a People result.
- HH-001 — Review the household opened from a Households result.
- PROG-001 — Review the program opened from a Programs result.

## SET-001 — Navigate studio settings

- Area: Settings
- Surface: staff-console
- Route: `/settings`
- Roles: organization-owner, manager, staff
- Required access: organization.membership
- Maturity: Published and verified on 2026-07-26 against deployed commit `f64c5c2`.

### Who this is for

Owners, managers, and staff who need to find a customer-facing studio
configuration area allowed by their current access.

Required access: membership in the selected organization. Some destinations
also require owner or feature-specific permissions.

### Before you begin

- Select the organization you intend to configure.
- Finish or cancel any unsaved operational work before navigating away.

### Steps

1. Select **Settings** in the main navigation, or open `/settings`.
2. Review the available destinations under **General**, **Onboarding**,
   **People**, **Billing**, and **Connections**. A group is hidden when none of
   its destinations are permitted.
3. Select the row for the area you need, such as **Location details**,
   **Staff**, **Billing setup**, or **Integrations**.
4. Confirm that the destination opens under its normal route and still shows
   the intended organization context.
5. Use the Settings breadcrumb or main navigation to return to the settings
   index when you need another area.

### Expected outcome

Settings lists only the destinations permitted for the signed-in role, grouped
by purpose, and each visible row opens its canonical configuration route.

### Recovery

- Owner-only destinations such as **Organization details** and
  **Subscription** do not appear for non-owners.
- Import destinations appear only for roles with import-management access.
- If a visible row opens a missing or unauthorized page, return to Settings and
  send an escalation report with the row label and resulting route.

### Related workflows

- ORG-001 — Update organization details when signed in as an owner.
- LOC-001 — Review configured studio locations.
- STAFF-001 — Review staff access and assignments.

## STAFF-001 — Invite staff

- Area: Staff
- Surface: staff-console
- Route: `/settings/staff`
- Roles: studio-owner, organization-owner
- Required access: organization.owner
- Maturity: Published and verified on 2026-07-29 against deployed commit `9096f81`.

### Who this is for

This guide is for the **organization owner**: the only person who can add staff
to the console.

**Add staff** appears on the Staff page only for an owner. Everyone else sees
the staff list without it.

### Before you begin

- Have the person's email address. That is what the invitation goes to, and it
  is the only required field.
- Decide what they should be able to reach. Roles are easier to maintain than
  one-off grants, so set those up first in **Settings › User roles**; see
  ROLE-001.
- Decide their home location, if you have more than one. It preselects the
  workspace that opens for them; it does not limit what they can do.

### Steps

1. Click **Settings** in the left navigation, then click **Staff**. The list
   shows each person with their **Work locations**, **Permissions**, and
   **Status**.
2. Click **Add staff** in the top right. **Add staff · Step 1 of 4** opens.
3. Type the person's **Email**. Fill in **Name (optional)** if you want the list
   to show a name before they accept.
4. Click **Continue**. Step 2 asks what they can reach.
5. Under **Roles**, tick any roles that apply. Their permissions add together, so
   a person can hold several. If you have not made any roles yet, PositiveForm
   says so and the person starts view-only.
6. Under **Direct grants**, tick anything this one person needs that no role
   covers. A permission already coming from a role is ticked and greyed with
   **via role**, so you cannot grant the same thing twice. Use direct grants
   sparingly; a permission several people need belongs in a role.
7. Click **Continue**. Step 3 asks for a **Home location**. Choose one, or
   **No home location**. This preselects their workspace and form location; it
   is not a permission boundary.
8. Click **Continue**. Step 4 summarises the person, their roles, their direct
   grants, and their home location. Check it.
9. Click **Add staff**.

**Back** returns to the previous step with your answers intact, and **Cancel**
closes without creating anything.

### Expected outcome

PositiveForm confirms with **Staff profile created and invitation email sent**,
and the person appears in the staff list with their permissions and invitation
state visible.

The profile is created once. If the invitation email fails to send, the profile
still exists and PositiveForm tells you so, so a retry never produces a second
record for the same person.

They reach the console by accepting the invitation and signing in with that
email address. Everything you set here can be changed afterwards from their
profile; see STAFF-002.

### Recovery

If the email is rejected, the field itself says what is wrong. Correct it and
click **Continue** again.

If adding fails, PositiveForm shows **Could not invite that staff member.** with
the reason, and nothing is created. The most common reason is that the email
already belongs to a staff profile in this organization; open that profile
instead of making a second one.

If you see **Staff profile created, but the invitation email was not
delivered.**, the person exists but has no invitation. Open their profile from
the staff list and retry from there. Do not run **Add staff** again for the same
email.

If **Add staff** is not on the page, you are not an organization owner. Ask one
to add the person.

To stop without creating anything, click **Cancel**.

### Related workflows

- ROLE-001 — build the reusable roles you tick
  in step 5, before inviting anyone.
- STAFF-002 — change someone's identity, work
  locations, or member link after they exist.

## STAFF-002 — Maintain a staff profile

- Area: Staff
- Surface: staff-console
- Route: `/settings/staff/:staffId/identity`
- Roles: studio-owner, organization-owner
- Required access: organization.owner
- Maturity: Published and verified on 2026-07-29 against deployed commit `2ac7ef8`.

### Who this is for

This guide is for the **organization owner** keeping a staff member's record
current: their name and photo, whether they are teaching, whether they can sign
in, where they work, and whether they also train with you as a member.

Those are separate things, and PositiveForm keeps them separate on purpose.
Turning off teaching does not remove someone's login, and neither one deletes
their profile.

### Before you begin

- Know which one you are actually changing. The page groups them under
  **Profile**, **Staff status and access**, **Contact details**, **Work
  locations**, and **Member profile**.
- Contact details are protected by the contact-information permission, so what
  you can see there depends on your access.
- A login email can only be changed by the account holder, so a new address can
  be verified. You cannot change it for them.

### Steps

#### Open the profile

1. Click **Settings** in the left navigation, then click **Staff**.
2. Click the person. Their profile opens on the **Identity** tab, beside
   **Achievements** and **Permissions**.

#### Name, photo, and bio

3. Edit **Name**. Use **Replace photo** to upload a profile photo, cropping it to
   a square, or **Remove** to take it off. JPEG, PNG, or WebP up to 10 MB.
4. Use **Bio** for a short description. **Staff since** below it is the date the
   record was created and is not editable.

#### Teaching, login access, and status

5. Read **Staff status and access** before changing anything. It says plainly
   that teaching, organization login access, and the staff identity lifecycle
   are separate.
6. **Teaching at the studio** shows this person as current instructional staff.
7. **PositiveForm organization access** allows their linked account to enter this
   organization. Turning it off removes their way in without deleting anything.
8. **Staff status** is shown here and changed in the danger zone. Making someone
   inactive never deletes their profile.

#### Contact details

9. Fill in **Email**, **Phone number**, and the address fields. These are
   operational contact details for the studio's use.
10. **Login email** is shown but not editable by you. **Change my login email**
    is for the account holder changing their own.

#### Where they work

11. Choose a **Home location**. It decides which workspace opens for them by
    default.
12. Tick any other locations they work at. Location choices do not grant or
    restrict permissions; roles and direct grants apply across the organization
    and are managed under **Permissions**.

#### Link them to a member profile

13. If this person also trains with you, use **Find an existing member** to link
    their member record. The link is deliberate: matching an email never links an
    account automatically, and PositiveForm never creates a duplicate member.
14. Once linked, **Training ranks** shows that member profile's current ranks.
    Changes here appear on the member profile too, because it is one record.

#### Save

15. Click **Save profile**.

**Teaches** lower down lists the programs this person is assigned to. That is set
on each program's Staff tab, not here.

### Expected outcome

The profile saves and the staff list reflects it: name, work locations,
permissions summary, and status.

The separate concerns stay separate. Someone can stop teaching and keep their
login, lose their login and keep their profile, or be inactive and keep every
historical record attached to them. Nothing on this page deletes a person.

If you linked a member profile, training, attendance, subscriptions, and ranks stay
on that member record rather than being copied here.

### Recovery

If saving fails, PositiveForm shows the reason and keeps your edits on screen.
Correct what it names and click **Save profile** again.

If a photo will not upload, check it is a JPEG, PNG, or WebP under 10 MB. The
existing photo stays until a new one succeeds.

If you cannot see or edit contact details, your access does not carry the
contact-information permission.

If you linked the wrong member profile, unlink it and search again. Linking does
not merge the two records, so nothing is lost by correcting it.

If someone cannot sign in, check **PositiveForm organization access** here before
changing their roles. An account without organization access cannot enter
whatever permissions it holds.

### Related workflows

- STAFF-001 — create the profile in the first place.
- STAFF-003 — record ranks, certifications, and
  awards that belong to the person.
- STAFF-004 — change what they can reach.

## STAFF-003 — Record staff achievements and ranks

- Area: Staff
- Surface: staff-console
- Route: `/settings/staff/:staffId/achievements`
- Roles: studio-owner, organization-owner
- Required access: organization.owner
- Maturity: Published and verified on 2026-07-29 against deployed commit `2ac7ef8`.

### Who this is for

This guide is for the **organization owner** recording what an instructor has
earned: ranks from other schools, certifications, awards, and other recognition.

These belong to the **person**, not to one of your programs. A rank a student
earns in your curriculum lives on their member record and comes from advancement;
see ADV-001. This page is for
everything else the person brings with them.

Nothing recorded here grants any permission. An achievement is a credential you
are noting, not access you are giving.

### Before you begin

- Have the details to hand: what it is, who issued it, when it was earned, and
  a link if the credential can be verified online.
- Know the difference from training ranks. If the person also trains with you and
  their member profile is linked, their **Training ranks** appear on the
  **Identity** tab and are managed there.

### Steps

1. Click **Settings** in the left navigation, then click **Staff**, then click
   the person.
2. Click the **Achievements** tab. With nothing recorded it reads **No
   achievements yet**.
3. Click **Add achievement**.
4. Fill in **Title**, for example `Black belt`. This is the only required field.
5. Choose a **Type**. For a belt or rank, set the **Belt or rank color** so it
   shows the way your studio expects.
6. Fill in **Discipline or field**, for example `Taekwondo`, and **Issuer or
   organization** for whoever awarded it.
7. Add a **Credential link** if the award can be verified online. It must be a
   full web address.
8. Set **Date earned**, and **Expiration (optional)** for anything that lapses,
   such as a first-aid certificate.
9. Use **Description** for context the other fields do not capture.
10. Save the achievement.

To change one later, open it from the list and edit it. To take one off, remove
it.

### Expected outcome

PositiveForm confirms with **Achievement added**, or **Achievement updated** when
you edited an existing one, and the achievement appears on the person's profile.

It is part of their staff identity from then on: visible to anyone who can view
the profile, and unchanged by their roles, their teaching status, or their login
access. Removing one confirms with **Achievement removed**.

Recording an achievement changes nothing about what this person can do in
PositiveForm. Access is managed under **Permissions**; see
STAFF-004.

### Recovery

If you save without a title, PositiveForm refuses with **Give this achievement a
title.**

If the credential link is malformed, the field is marked with what is wrong. Fix
it, or clear it, and save again.

If saving fails, you get **Could not save this achievement.** with the reason,
and your entries stay on screen.

If removing fails, you get **Could not remove this achievement.** and it stays on
the profile.

If you meant to record a rank the person earned in **your** curriculum, this is
the wrong page. Link their member profile on the **Identity** tab and manage
training ranks there, so the rank sits with their training history.

### Related workflows

- STAFF-002 — the identity these achievements sit
  on, and where a linked member profile's training ranks are managed.
- ADV-001 — the progressions that
  produce ranks inside your own programs.

## STAFF-004 — Manage staff access

- Area: Staff
- Surface: staff-console
- Route: `/settings/staff/:staffId/permissions`
- Roles: studio-owner, organization-owner
- Required access: organization.owner
- Maturity: Published and verified on 2026-07-29 against deployed commit `2ac7ef8`.

### Who this is for

This guide is for the **organization owner** deciding what one staff member can
reach.

A person's access is the sum of two things: the **roles** you give them, and any
**direct grants** for that person alone. Roles are the maintainable half; direct
grants are the exception.

Access applies across the whole organization. Home and work locations do **not**
narrow it. Someone with billing permissions has them at every location, whatever
workspace opens for them by default.

### Before you begin

- Build the roles first, in **Settings › User roles**; see
  ROLE-001. Assigning a role you already trust
  is safer than ticking permissions one at a time.
- Consider previewing the role before assigning it, so you see what it actually
  reaches; see ROLE-002.
- Owners are not editable here. An owner holds every permission, and the page
  says so instead of offering switches that would not work.

### Steps

1. Click **Settings** in the left navigation, then click **Staff**, then click
   the person.
2. Click the **Permissions** tab. It opens on **Organization permissions**,
   which restates that roles and direct grants apply across the organization and
   that locations do not narrow authorization.
3. Under **Roles**, tick every role this person should hold. They get the
   combined permissions of all of them. If none exist yet, PositiveForm says
   **No roles exist yet** and links you to where to make one.
4. Under **Direct grants**, tick anything this one person needs that no role
   covers. Each label says what it lets someone do, and the riskiest carry a
   **High impact** badge.
5. A permission already arriving through a role is ticked and greyed, labelled
   **via** the role granting it. You cannot grant the same thing twice, and you
   cannot remove it here; change the role instead.
6. Save the change.

#### Move a set of permissions between people or studios

7. Use **Portable permission definition** to export this person's local role
   names and direct grants, or to apply a definition you have. Identity and
   locations are never included, so a definition carries access and nothing
   personal. PositiveForm validates it and shows what it will apply before
   anything changes.

### Expected outcome

PositiveForm confirms with **Permissions updated**, and the tab shows what this
person now holds: roles ticked, direct grants ticked, and anything arriving via a
role marked as such.

The result stays inspectable. You can see not just *what* someone can do but
*why*, because a permission coming from a role names that role. That is what
makes access reviewable later, when whoever set it up is not in the room.

Their identity, work locations, teaching status, and login access are untouched;
those live on the **Identity** tab. If someone cannot sign in at all, check
**PositiveForm organization access** there before changing anything here.

For an owner, the tab shows **Owner permissions** and nothing to edit.

### Recovery

If saving fails, PositiveForm shows **Could not update permissions.** and the
person's access is unchanged.

If a pasted permission definition is rejected, you get **That staff permissions
JSON is not valid.** Nothing is applied until a definition validates, so a bad
paste cannot half-apply.

If a permission will not untick, it is arriving through a role, and the label
names which one. Either remove that role from this person, or change the role in
ROLE-001 if nobody should have it.

If someone has more access than you intended, look at their roles first. A single
role can carry a lot, and a direct grant is often not the cause.

If the tab shows **Owner permissions**, this person is an owner, and ownership is
not changed from here.

### Related workflows

- ROLE-001 — create and edit the reusable roles
  you assign here.
- ROLE-002 — see what a role reaches before you
  give it to someone.

## TAG-001 — Manage the tag catalog

- Area: Tags
- Surface: staff-console
- Route: `/settings/tags`
- Roles: organization-owner, manager
- Required access: tags.manage
- Maturity: Published and verified on 2026-07-29 against deployed commit `beb3f3c`.

### Who this is for

This guide is for **authorized staff** creating, renaming, merging, or archiving
reusable tags used on people and households.

### Before you begin

- Open **Settings › Tags** (`/settings/tags`).
- Prefer merge over delete when two tags mean the same thing, so member
  assignments stay coherent.

### Steps

1. Open **Settings**, then **Tags**. The heading is **Tags**.
2. Use **Filter tags** to find a tag by name.
3. To add a tag, click **Add tag** to open the form, fill the name field (for
   example **Leadership**) and optional **Description**, then click **Create
   tag**. The form stays open so you can add several in a row; **Cancel**
   closes it. For a list you already have, use **Create many**.
4. The catalog is listed by name. Click a column heading (**Favorite**,
   **Tag**, or **Members**) to sort by it, and click the same heading again to
   reverse the direction. Clicking **Favorite** twice puts your starred tags on
   top.
5. Click the star on a row to add or remove a studio favorite. The row keeps
   its place in the list until you change the sort.
6. Use **Actions for …** on a row for rename, archive, or other row actions.
7. To merge duplicates, tick rows with **Select …** (or **Select all visible
   tags**), then use **Merge selected** in the bar that appears above the
   table. **Delete selected** is in the same bar.
8. Page long catalogs with **Previous** and **Next**.

### Expected outcome

Tag changes preserve member assignments and leave an audit trail. The catalog
list reflects the new names and selections.

### Recovery

If merge is disabled, select at least two tags. If the bulk bar is missing,
nothing is selected yet. If a tag seems missing, clear **Filter tags**, check
the sort you have applied, and check archived rows if your studio uses them.

### Related workflows

- MEM-002

## TEST-001 — Schedule a testing event

- Area: Testing events
- Surface: staff-console
- Route: `/testing`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-29 against deployed commit `a15afd7`.

### Who this is for

This guide is for **staff, managers, and owners** scheduling a testing day
against a progression.

You need the `members.change` permission.

### Before you begin

- Configure at least one progression (ADV-001).
- Choose the location and date for the test.

### Steps

1. Click **Testing events** in the left navigation.
2. Review the list and filters (**Progression filter**, **Status filter**,
   dates) if you are looking for an existing day first.
3. Click **New event** to open **Schedule a testing event**.
4. Name the event, set the scheduled date and time, and choose a
   **Progression**.
5. Save. Confirm **Testing event created** (or equivalent success).
6. Open the new event when you are ready to build the roster
   (TEST-002).

### Expected outcome

A testing event appears in the list with its progression and status
(for example **Scheduled**), ready for roster work.

### Recovery

If creation requires a progression and none exist, open **Settings ›
Progressions** first.

If filters hide everything, reset status and progression filters. Empty states
distinguish “no events yet” from “no matches.”

### Related workflows

- TEST-002 — run the roster and finish testing.
- ADV-001 — progression setup.

## TEST-002 — Run and finish testing

- Area: Testing events
- Surface: staff-console
- Route: `/testing/:eventId/roster`
- Roles: organization-owner, manager, staff
- Required access: members.change
- Maturity: Published and verified on 2026-07-30 against deployed commit `5dab2df`.

### Who this is for

This guide is for **staff, managers, and owners** running a scheduled testing
day: building the roster, recording results, and finishing the event.

You need the `members.change` permission.

### Before you begin

- Schedule a testing event first (TEST-001).
- Know which progression and candidates belong on the roster.

### Steps

1. Open **Testing events** and select the scheduled event.
2. Open the **Roster** tab.
3. Under **Add candidates**, search members and click **Register**. Each row
   shows the member's current rank and the rank they would test for, such as
   `White Belt` to `Orange Belt`.
4. In the **Roster** section, record each candidate's result with **Pass**,
   **Fail**, or **Absent**. The selected result stays highlighted, and
   **Remove** takes a candidate off the roster.
5. Click **Finish testing** when the day is complete. The confirmation names
   how many students it promotes and how many did not pass, and warns that
   finishing cannot be undone. Click **Finish testing** in the dialog to
   confirm.

### Expected outcome

The event's badge changes to **Completed**, and the roster shows the final
result for each candidate, such as a **Passed** badge. A notice explains that
the event is finished and results are final and reflected in each member's
advancement. Registration, result, and remove controls are no longer offered,
so the finished event cannot be edited by accident.

### Recovery

If the roster shows **This page hit an error**, use **Try again** or
**Reload**, then reopen the event from **Testing events**. Report a product
defect if it persists.

If you registered the wrong member, click **Remove** on their roster row
before finishing. Results and promotions become final once the event is
finished, so review the confirmation's promotion count before confirming.

### Related workflows

- TEST-001 — schedule the day.
- ADV-003 — record promotions outside a
  testing event.

## WORK-001 — Switch organizations

- Area: Workspace navigation
- Surface: staff-console
- Route: `/`
- Roles: organization-owner, manager, staff
- Required access: organization.membership
- Maturity: Unverified draft. State that the path is provisional, do not claim the current screen was verified, and stop rather than inventing missing details.

### Who this is for

Owners, managers, and staff whose account belongs to more than one
PositiveForm organization.

Required access: membership in both the current and destination organizations.

### Before you begin

- Confirm that the account belongs to at least two active organizations.
- Finish or cancel any unsaved form before switching.

### Steps

1. In the application sidebar, open the organization selector directly below
   the organization name and logo.
2. Under **Organizations**, select the destination organization. An
   organization marked **Pending deletion** cannot be selected.
3. Wait for PositiveForm to reload the workspace.
4. Confirm that the sidebar brand and organization selector show the selected
   organization.
5. Before editing anything, confirm that dashboard counts and visible records
   belong to the selected organization.

### Expected outcome

The console reloads in the selected tenant. The shell and organization-scoped
data identify the destination organization, with no records retained from the
previous tenant.

### Recovery

- If the organization is missing, ask its owner to confirm your staff access.
- If it is marked **Pending deletion**, do not try to work around the disabled
  option; contact the organization owner.
- If the brand changes but old organization data remains visible, stop work,
  switch back, and send an urgent escalation report.

### Related workflows

- WORK-002 — Narrow the selected organization to a different location.
- DASH-001 — Confirm the destination organization's dashboard context.

## WORK-002 — Switch operating locations

- Area: Workspace navigation
- Surface: staff-console
- Route: `/`
- Roles: organization-owner, manager, staff
- Required access: organization.membership
- Maturity: Published and verified on 2026-07-26 against deployed commit `33bf3b3`.

### Who this is for

Owners, managers, and staff who work across multiple locations in one
organization.

Required access: membership in the selected organization and access to its
locations.

### Before you begin

- Select the correct organization first.
- Confirm that at least two active locations are available to the account.
- Finish or cancel any unsaved form before switching.

### Steps

1. In the application sidebar, open the control labeled **Location**.
2. Under **Locations**, select the destination location. The check mark shows
   the current selection.
3. Wait for the current page's location-scoped data to refresh.
4. Confirm that the location control shows the destination while the
   organization name remains unchanged.
5. Before editing anything, confirm that the visible records belong to the
   selected location.

### Expected outcome

Location-scoped pages refresh for the selected location while the active
organization remains unchanged.

### Recovery

- If the selector is hidden, the account has only one available location and
  cannot perform this workflow.
- If the destination is missing, ask an owner or manager to confirm that the
  location is active and available to your staff account.
- If the selector changes but the page retains records from the prior
  location, stop work, switch back, and send an urgent escalation report.

### Related workflows

- WORK-001 — Change to a different organization before choosing its location.
- LOC-002 — Maintain the selected location's profile, hours, and staff
  assignments when your access permits it.
