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

# Member Access

> Perform subscription lifecycle actions or fully remove a member from a board

Use this endpoint when the member already exists and you need to change access state or fully remove the member from this board.

## Supported Actions

<ParamField body="action" type="string" required>
  `activate`, `expire_now`, `cancel_now`, `cancel_at_period_end`, `reactivate`, `mark_past_due`, or `delete_member`.
</ParamField>

`delete_member` removes the member's `job_board_users` row for this organization and deletes their org-scoped `user_subscriptions` so paywall access is revoked immediately. It does not delete the shared Supabase `auth.users` account.

<ParamField body="email" type="string">
  Member email. Required if `userId` is not provided.
</ParamField>

<ParamField body="userId" type="string">
  Member UUID. Required if `email` is not provided.
</ParamField>

<ParamField body="stripeSubscriptionId" type="string">
  Existing external subscription identifier to target. Not needed for `delete_member`.
</ParamField>

<ParamField body="planId" type="string">
  Needed for `activate` if you do not send another plan identifier.
</ParamField>

<ParamField body="effectiveAt" type="string">
  Optional timestamp for a scheduled cancel or expiration. Ignored for `delete_member`.
</ParamField>

## Examples

### Expire access immediately

```bash cURL theme={null}
curl --request POST \
  --url https://api.kardow.com/members/access \
  --header "Content-Type: application/json" \
  --header "x-api-key: your-api-key-here" \
  --data '{
    "email": "member@example.com",
    "action": "expire_now",
    "stripeSubscriptionId": "sub_123"
  }'
```

### Cancel at period end

```bash cURL theme={null}
curl --request POST \
  --url https://api.kardow.com/members/access \
  --header "Content-Type: application/json" \
  --header "x-api-key: your-api-key-here" \
  --data '{
    "email": "member@example.com",
    "action": "cancel_at_period_end",
    "stripeSubscriptionId": "sub_123",
    "effectiveAt": "2026-04-09T12:00:00.000Z"
  }'
```

### Activate a new plan

```bash cURL theme={null}
curl --request POST \
  --url https://api.kardow.com/members/access \
  --header "Content-Type: application/json" \
  --header "x-api-key: your-api-key-here" \
  --data '{
    "email": "member@example.com",
    "action": "activate",
    "stripePriceId": "price_paywall_monthly",
    "startsAt": "2026-03-10T12:00:00.000Z"
  }'
```

### Reactivate a paused or canceled subscription

```bash cURL theme={null}
curl --request POST \
  --url https://api.kardow.com/members/access \
  --header "Content-Type: application/json" \
  --header "x-api-key: your-api-key-here" \
  -d '{
    "email": "member@example.com",
    "action": "reactivate",
    "stripeSubscriptionId": "sub_123"
  }'
```

### Delete a member from this board

```bash cURL theme={null}
curl --request POST \
  --url https://api.kardow.com/members/access \
  --header "Content-Type: application/json" \
  --header "x-api-key: your-api-key-here" \
  --data '{
    "email": "member@example.com",
    "action": "delete_member"
  }'
```

## Response Shape

<ResponseField name="data" type="object" required>
  The updated subscription record after a subscription action, or a deletion summary after `delete_member`.
</ResponseField>

### Example response

```json theme={null}
{
  "data": {
    "id": "22222222-2222-2222-2222-222222222222",
    "organizationId": 42,
    "userId": "11111111-1111-1111-1111-111111111111",
    "planId": "33333333-3333-3333-3333-333333333333",
    "status": "active",
    "stripeSubscriptionId": "sub_123",
    "currentPeriodStart": "2026-03-10T12:00:00.000Z",
    "currentPeriodEnd": "2026-04-10T12:00:00.000Z",
    "cancelAt": null,
    "canceledAt": null,
    "jobsRemaining": null,
    "metadata": null
  }
}
```

### Example delete response

```json theme={null}
{
  "data": {
    "deleted": true,
    "userId": "11111111-1111-1111-1111-111111111111",
    "organizationId": 42,
    "removedSubscriptionCount": 1
  }
}
```


## OpenAPI

````yaml POST /members/access
openapi: 3.1.0
info:
  title: Kardow Public API
  description: Public API for jobs and member automation on Kardow.
  version: 1.0.0
  contact:
    name: Kardow Support
    email: support@kardow.com
    url: https://kardow.com
servers:
  - url: https://api.kardow.com
    description: Production API
security:
  - ApiKeyAuth: []
paths:
  /members/access:
    post:
      tags:
        - Members
      summary: Member Access Action
      description: >-
        Apply a lifecycle action to a member subscription or fully remove a
        board member while preserving the shared auth account.
      operationId: memberAccessAction
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MemberAccessActionRequest'
            examples:
              expire_now:
                summary: Expire access now
                value:
                  email: member@example.com
                  action: expire_now
                  stripeSubscriptionId: sub_123
              cancel_period_end:
                summary: Cancel at period end
                value:
                  email: member@example.com
                  action: cancel_at_period_end
                  stripeSubscriptionId: sub_123
                  effectiveAt: '2026-04-09T12:00:00.000Z'
              activate_new:
                summary: Activate access
                value:
                  email: member@example.com
                  action: activate
                  stripePriceId: price_paywall_monthly
                  startsAt: '2026-03-10T12:00:00.000Z'
              delete_member:
                summary: Delete member from this board
                value:
                  email: member@example.com
                  action: delete_member
      responses:
        '200':
          description: Subscription updated or member removed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MemberAccessActionResponse'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/InternalError'
      security:
        - ApiKeyAuth: []
components:
  schemas:
    MemberAccessActionRequest:
      type: object
      required:
        - action
      properties:
        email:
          type: string
          format: email
        userId:
          type: string
          format: uuid
        action:
          type: string
          enum:
            - activate
            - delete_member
            - expire_now
            - cancel_now
            - cancel_at_period_end
            - reactivate
            - mark_past_due
        planId:
          type: string
          format: uuid
        stripePriceId:
          type: string
        paypalPlanId:
          type: string
        planName:
          type: string
        subscriptionId:
          type: string
          format: uuid
        stripeSubscriptionId:
          type: string
        effectiveAt:
          type: string
          format: date-time
        startsAt:
          type: string
          format: date-time
        endsAt:
          type: string
          format: date-time
        jobsRemaining:
          type:
            - integer
            - 'null'
        metadata:
          type:
            - object
            - 'null'
          additionalProperties: true
    MemberAccessActionResponse:
      type: object
      properties:
        data:
          oneOf:
            - $ref: '#/components/schemas/Subscription'
            - $ref: '#/components/schemas/DeleteMemberActionResult'
      required:
        - data
    Subscription:
      type: object
      properties:
        id:
          type: string
          format: uuid
        organizationId:
          type: integer
        userId:
          type: string
          format: uuid
        planId:
          type: string
          format: uuid
        status:
          type: string
        stripeSubscriptionId:
          type:
            - string
            - 'null'
        currentPeriodStart:
          type:
            - string
            - 'null'
          format: date-time
        currentPeriodEnd:
          type:
            - string
            - 'null'
          format: date-time
        cancelAt:
          type:
            - string
            - 'null'
          format: date-time
        canceledAt:
          type:
            - string
            - 'null'
          format: date-time
        jobsRemaining:
          type:
            - integer
            - 'null'
        metadata:
          type:
            - object
            - 'null'
          additionalProperties: true
    DeleteMemberActionResult:
      type: object
      properties:
        deleted:
          type: boolean
        userId:
          type: string
          format: uuid
        organizationId:
          type: integer
        removedSubscriptionCount:
          type: integer
        cleanupErrors:
          type: array
          items:
            type: string
      required:
        - deleted
        - userId
        - organizationId
        - removedSubscriptionCount
    ErrorEnvelope:
      type: object
      properties:
        error:
          type: object
          properties:
            message:
              type: string
            code:
              type: string
            details: {}
            url:
              type: string
          required:
            - message
            - code
      required:
        - error
  responses:
    ValidationError:
      description: Validation error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    UnauthorizedError:
      description: Unauthorized
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    ForbiddenError:
      description: Forbidden
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    NotFoundError:
      description: Not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    InternalError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >-
        API key for authentication. Get yours from Settings > API Keys in the
        Kardow dashboard.

````