> ## Documentation Index
> Fetch the complete documentation index at: https://docs.1club.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Update a pay rate policy

> Edits a pay rate policy. Send only what changes; an omitted field is left alone. `instructorIds`, `classTypeIds` and `entranceScopes` each **replace** the whole list when sent - there is no element-wise merge for a set, so read the policy first and send the list you want.

`earnings.kind` cannot change once the policy exists: an individual policy and a class policy earn against different units of work, so switching one would silently re-price everything it covers. Create the other kind instead. Sending `classTypeIds` to an individual policy is refused for the same reason it is on create. Both are 400s.

A change that puts a coach on two overlapping policies is a 409 with code `PAY_RATE_POLICY_COLLISION`. Changing rates re-resolves the coach's not-yet-finalized pay from today onward; days already closed stay frozen.




## OpenAPI

````yaml /openapi-platform.json patch /v1/platform/pay-rate-policies/{id}
openapi: 3.0.0
info:
  title: 1club Platform API
  version: 1.0.0
  description: >-
    The 1club Platform API lets you programmatically access and manage your
    organization's data.


    ## Official API Contract


    This documentation is the official source of truth for the 1club Platform
    API.

    If an integration relies on undocumented endpoints, fields, response shapes,
    or internal behavior outside this spec, we can't guarantee backward
    compatibility.

    Build against what's documented here to stay stable as the platform evolves.


    ## Authentication


    All requests require a customer API key passed as a Bearer token:


    ```

    Authorization: Bearer 1club_sk_live_...

    ```


    Generate API keys from the admin portal under **Settings > API Tokens**.

    The key is tied to your organization - all responses are scoped to your
    org's data.


    ## Rate Limiting


    - **100 requests per minute** per API key

    - When exceeded, the API returns `429 Too Many Requests` with a
    `Retry-After` header

    - Rate limit headers are included in every response:
      - `X-RateLimit-Limit` - max requests per window
      - `X-RateLimit-Remaining` - requests remaining
      - `X-RateLimit-Reset` - seconds until the window resets

    ## Errors


    | Status | Meaning |

    |--------|---------|

    | `400` | Invalid request parameters |

    | `401` | Missing or invalid API key |

    | `404` | Resource not found (or doesn't belong to your organization) |

    | `429` | Rate limit exceeded |

    | `500` | Internal server error |
  contact:
    name: 1club API Support
    email: support@1club.ai
servers:
  - url: https://api.1club.ai
    description: Production API
security:
  - customerApiAuth: []
tags: []
paths:
  /v1/platform/pay-rate-policies/{id}:
    patch:
      tags:
        - Pay Rate Policies
      summary: Update a pay rate policy
      description: >
        Edits a pay rate policy. Send only what changes; an omitted field is
        left alone. `instructorIds`, `classTypeIds` and `entranceScopes` each
        **replace** the whole list when sent - there is no element-wise merge
        for a set, so read the policy first and send the list you want.


        `earnings.kind` cannot change once the policy exists: an individual
        policy and a class policy earn against different units of work, so
        switching one would silently re-price everything it covers. Create the
        other kind instead. Sending `classTypeIds` to an individual policy is
        refused for the same reason it is on create. Both are 400s.


        A change that puts a coach on two overlapping policies is a 409 with
        code `PAY_RATE_POLICY_COLLISION`. Changing rates re-resolves the coach's
        not-yet-finalized pay from today onward; days already closed stay
        frozen.
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
          description: Pay rate policy id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PlatformPayRatePolicyUpdate'
      responses:
        '200':
          description: The updated pay rate policy
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlatformPayRatePolicy'
        '400':
          $ref: '#/components/responses/PlatformBadRequest'
        '401':
          $ref: '#/components/responses/PlatformUnauthorized'
        '403':
          description: API key is missing the required `instructors:write` scope
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlatformError'
        '404':
          $ref: '#/components/responses/PlatformNotFound'
        '409':
          description: The change puts an instructor on two overlapping policies
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlatformError'
        '429':
          $ref: '#/components/responses/PlatformRateLimited'
      security:
        - customerApiAuth: []
components:
  schemas:
    PlatformPayRatePolicyUpdate:
      type: object
      description: >-
        Fields accepted when updating a pay rate policy. Send only what changes;
        an omitted field is left alone, and any list that IS sent replaces the
        whole set. `earnings.kind` must match the policy's existing kind.
      minProperties: 1
      properties:
        name:
          type: string
          maxLength: 255
        earnings:
          $ref: '#/components/schemas/PlatformPayRatePolicyEarnings'
        requirePaidBooking:
          type: boolean
          default: false
          description: >-
            When true, attendance that was never paid for earns the coach
            nothing.
        instructorIds:
          type: array
          items:
            type: integer
          description: >-
            Coaches this policy covers. An empty list makes it the organization
            default, applying to every coach with no policy of their own.
        classTypeIds:
          type: array
          items:
            type: integer
          description: >-
            Class types it covers, from GET /v1/platform/class-types. Empty
            covers every class type. Rejected on an `individual` policy, which
            has no classes to scope.
        entranceScopes:
          type: array
          description: How the attendee got in. Empty covers every route in.
          items:
            $ref: '#/components/schemas/PlatformPayRateEntranceScope'
    PlatformPayRatePolicy:
      type: object
      description: >-
        A rule for what the club pays a coach. Not an instructor's `hourlyRate`,
        which is what a member pays for an hour of their time. Each coverage
        list means "everything" when empty: no `instructors` makes it the
        organization default, no `classTypes` covers every class type, no
        `entranceScopes` covers every way in.
      properties:
        policyId:
          type: string
          format: uuid
        name:
          type: string
        kind:
          type: string
          enum:
            - individual
            - class
        earnings:
          $ref: '#/components/schemas/PlatformPayRatePolicyEarnings'
        requirePaidBooking:
          type: boolean
        instructors:
          type: array
          description: >-
            Coaches this policy covers. Empty means every coach with no policy
            of their own.
          items:
            type: object
            properties:
              id:
                type: integer
              name:
                type: string
                nullable: true
        classTypes:
          type: array
          description: Class types it covers. Empty means every class type.
          items:
            type: object
            properties:
              id:
                type: integer
              name:
                type: string
        entranceScopes:
          type: array
          description: How the attendee got in. Empty means every route in.
          items:
            $ref: '#/components/schemas/PlatformPayRateEntranceScope'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    PlatformError:
      type: object
      properties:
        error:
          type: string
    PlatformPayRatePolicyEarnings:
      type: object
      description: >-
        What the coach earns, and for what unit of work. `kind` decides which of
        the rate fields apply and which are rejected: an `individual` policy
        takes `perSession` and `sessionRevenuePercentage`, a `class` policy
        takes `perClass`, `perBooking` and `classRevenuePercentage`. At least
        one of the applicable rates must be greater than zero, and `kind` cannot
        be changed once the policy exists.
      required:
        - kind
      properties:
        kind:
          type: string
          enum:
            - individual
            - class
        perSession:
          type: number
          nullable: true
          minimum: 0
          description: 'Individual policies: a flat amount for each private session.'
        sessionRevenuePercentage:
          type: number
          nullable: true
          minimum: 0
          maximum: 100
          description: 'Individual policies: a share of what the session took.'
        perClass:
          type: number
          nullable: true
          minimum: 0
          description: 'Class policies: a flat amount for running the class at all.'
        perBooking:
          type: number
          nullable: true
          minimum: 0
          description: 'Class policies: a flat amount for each attendee.'
        classRevenuePercentage:
          type: number
          nullable: true
          minimum: 0
          maximum: 100
          description: 'Class policies: a share of what the class took.'
    PlatformPayRateEntranceScope:
      type: object
      description: >-
        One way an attendee got in, narrowing what a policy pays for.
        `membershipPlanId` applies only to `membership`, and `externalProgramId`
        only to `external_program`; sending the wrong pairing is a 400.
      required:
        - entranceMethod
      properties:
        entranceMethod:
          type: string
          enum:
            - direct_payment
            - membership
            - external_program
        membershipPlanId:
          type: integer
          nullable: true
          description: Narrow to one membership plan, from GET /v1/platform/plans.
        externalProgramId:
          type: integer
          nullable: true
          description: Narrow to one external program.
  responses:
    PlatformBadRequest:
      description: Invalid request (bad parameters, or a body that fails validation)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PlatformError'
    PlatformUnauthorized:
      description: Invalid or missing API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PlatformError'
    PlatformNotFound:
      description: The requested resource does not exist in this organization
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PlatformError'
    PlatformRateLimited:
      description: Rate limit exceeded
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PlatformError'
  securitySchemes:
    customerApiAuth:
      type: http
      scheme: bearer
      description: >-
        Organization-scoped bearer credential: a customer API key
        (1club_sk_live_...) or an MCP OAuth access token.

````