# API Updates — Category Flattening, Groups, Subscription Level Removal **Release:** v2 schema change (migration `20260821000000_flat_categories_groups_drop_subscription_levels`) **Status:** Breaking changes. Clients must be updated. ## Summary of changes | Endpoint | Change | | ----------------------------------------- | ------------------------------------------------------------------------------------ | | `GET /api/v2/categories` | **Breaking**: `parentId` removed from every record. Categories are now a flat list. | | `GET /api/v2/groups` | **New**: returns the group list (former sub-categories). | | `GET /api/v2/proclamations` | **Breaking**: `groupId` added; `subscriptionLevelId` removed. | | `GET /api/v2/subscription-levels` | **Removed**: subscription levels no longer exist. | | `POST /api/v2/license` | **Breaking**: response no longer contains `subscription_level`. | | `POST /api/v2/automations/create/account` | **Breaking**: `subscription_level` is no longer accepted or required in the payload. | | `POST /api/v1/license` | **Breaking**: response no longer contains `subscription_level`. | | `POST /api/v1/automations/create/account` | **Breaking**: `subscription_level` is no longer accepted or required in the payload. | | `GET /api/v1/categories` | Payload implicitly changed: rows no longer contain `parentId`. | ### Data model mapping - Former **parent** categories (`Spiritual`, `Physical`, `Mental`, `Emotional`, `Financial`, `Social`, `Biblical`, ...) became the flat **Category** list. - Former **child** sub-categories (`General`, `Specific`, `Precise`, `Prayers`, `Scripture`) became **Groups**. - A proclamation's former child category becomes its `group`; its former parent category becomes its `category`. A proclamation may move between categories without changing its group. - Proclamations that had no sub-category (e.g. under `Custom (Client Specific)`) were assigned to the `General` group. - Every proclamation now has exactly one category and one group. --- ## OpenAPI 3.0 specification ```yaml openapi: 3.0.3 info: title: Rhemawave Portal API version: 2.1.0 description: | Changes in this release: * ProclamationCategory hierarchy flattened; parentId removed everywhere. * New ProclamationGroup resource replaces sub-categories as a movable filter. * SubscriptionLevel resource and all level-based gating removed. paths: /api/v2/categories: get: summary: List categories (flat) description: > Returns all proclamation categories as a flat, ordered list. BREAKING: the previous version returned a hierarchical list where each record carried a nullable parentId linking children to parents. That field no longer exists. tags: [Categories] responses: '200': description: Flat category list. content: application/json: schema: type: array items: $ref: '#/components/schemas/Category' examples: newResponse: summary: New response value: - id: 4 name: Spiritual sortOrder: 0 - id: 2 name: Physical sortOrder: 1 oldResponseDeprecated: summary: Old (removed) response shape value: - id: 5 name: General sortOrder: 0 parentId: 4 - id: 9 name: Specific sortOrder: 1 parentId: 4 /api/v2/groups: get: summary: List groups description: > NEW ENDPOINT. Returns all proclamation groups ordered by sortOrder. Groups replace the former sub-categories and act as a movable filter: a proclamation can be moved from one category to another while keeping its group. tags: [Groups] responses: '200': description: Group list. content: application/json: schema: type: array items: $ref: '#/components/schemas/Group' example: - id: 1 name: General sortOrder: 0 - id: 2 name: Specific sortOrder: 1 /api/v2/proclamations: get: summary: List proclamations description: > Returns proclamations with their canonical frequency id and optional color palette. BREAKING: each record now carries groupId (required, always present) and no longer carries subscriptionLevelId. When the optional entitlement headers are supplied, only the caller's entitled proclamations are returned; there is no longer any per-level gating. tags: [Proclamations] parameters: - in: header name: x-license-key required: false schema: { type: string } description: License key. Must be paired with x-email. - in: header name: x-email required: false schema: { type: string } description: Account email. Must be paired with x-license-key. responses: '200': description: Proclamation list. content: application/json: schema: type: array items: $ref: '#/components/schemas/Proclamation' examples: newResponse: summary: New response value: - id: b40fdcb9-703c-420e-af32-a081964c6f08 name: Peace & Relaxation content:
The text content
categoryId: 4 groupId: 1 solfeggioFrequencyId: 5 colorPalette: [ '#32CD32', '#3CB371', '#90EE90', '#98FB98', '#ADFFA9', '#C8FFD2', '#F0FFF0' ] oldResponseDeprecated: summary: Old (removed) response shape value: - id: b40fdcb9-703c-420e-af32-a081964c6f08 name: Peace & Relaxation content:The text content
categoryId: 5 solfeggioFrequencyId: 5 subscriptionLevelId: 1 '400': description: Entitlement headers incomplete or invalid. /api/v2/subscription-levels: get: summary: REMOVED deprecated: true description: > This endpoint was removed. Subscription levels no longer exist in the data model; clients must stop calling it. tags: [Removed] responses: '404': description: Route no longer exists. /api/v2/license: post: summary: Validate a license key description: > Validates a key/email pair. BREAKING: the response no longer contains a subscription_level object. tags: [License] requestBody: required: true content: application/json: schema: type: object required: [license_key, email_address] properties: license_key: { type: string, example: ABCD-EFGH-IJKL-MNOP } email_address: { type: string, format: email } responses: '200': description: Key is valid. content: application/json: schema: $ref: '#/components/schemas/LicenseResponse' examples: newResponse: summary: New response value: key: ABCD-EFGH-IJKL-MNOP email_address: user@example.com oldResponseDeprecated: summary: Old (removed) response shape value: key: ABCD-EFGH-IJKL-MNOP email_address: user@example.com subscription_level: id: 2 name: Advanced description: General + Specific '400': description: Validation failure (missing fields, unknown key, wrong owner). /api/v2/automations/create/account: post: summary: Create account + license key (GoHighLevel automation) description: > Creates a portal account when needed and issues a license key. BREAKING: subscription_level was removed from the request contract. The field is neither required nor validated anymore; sending it is ignored. Signature headers (x-timestamp, x-nonce, x-signature) are unchanged. tags: [Automations] requestBody: required: true content: application/json: schema: type: object required: [email] properties: email: { type: string, format: email } name: { type: string } responses: '200': description: Account and license key created. '400': description: Missing/invalid email. '401': description: Invalid signature. components: schemas: Category: type: object required: [id, name, sortOrder] properties: id: { type: integer } name: { type: string } sortOrder: { type: integer } Group: type: object required: [id, name, sortOrder] properties: id: { type: integer } name: { type: string } sortOrder: { type: integer } Proclamation: type: object required: [id, name, content, categoryId, groupId, solfeggioFrequencyId] properties: id: type: string format: uuid name: { type: string } content: { type: string, description: HTML formatted content. } categoryId: type: integer description: Flat category this proclamation belongs to. groupId: type: integer description: > Group membership (movable filter). Independent of categoryId; changing the category does not change the group. solfeggioFrequencyId: type: integer minimum: 1 maximum: 9 description: Canonical kiosk frequency id (not the DB autoincrement id). colorPalette: type: array items: { type: string } minItems: 7 maxItems: 7 description: Present only when a palette exists for the frequency. LicenseResponse: type: object required: [key, email_address] properties: key: { type: string } email_address: { type: string, format: email } ``` --- ## Client migration checklist 1. **Kiosk / API consumers** - Stop reading `parentId`; treat `/api/v2/categories` as flat. - Fetch `/api/v2/groups` once at startup for the filter list. - Read `groupId` from each proclamation record for filtering. - Remove all references to `subscriptionLevelId` and `/api/v2/subscription-levels`. - Remove `subscription_level` handling from license validation responses. 2. **GoHighLevel automation** - Remove `subscription_level` from the account-creation webhook payload (optional but recommended; the field is now ignored). 3. **Admin portal** - Manage categories under Admin → Proclamation Categories (flat list, drag to reorder). - Manage groups under Admin → Proclamation Groups (drag to reorder). - Proclamations now select one Category and one Group independently.