> ## 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.

# Archive a contact

> Retires a contact. Nothing is destroyed: the contact is archived, which drops them out of the contacts list and out of every search, while their transaction history stays intact - so the response is the contact itself, now with `type` `archived`, rather than an empty body. Archiving also releases everything they hold on the forward schedule - active memberships, bookings they organized, and bookings they are only a player on - and none of that is restored by un-archiving, which is done in 1club.

To preview the impact, `GET /v1/platform/memberships?contactId={id}` lists the memberships that will be cancelled, and `GET /v1/platform/bookings?contactId={id}` the bookings that will be. Neither is an exhaustive preview, so treat them as indicative: the bookings list matches only bookings the contact **organized** - never the ones they are an additional player on, which are released too - and it defaults to a **7-day** window, so pass `from` and `to` (up to 93 days per request, consecutive windows beyond that) to see further out.

A release can fail on an individual membership or booking without blocking the archive - the contact is retired either way, so a stuck booking cannot leave it active. That answers **409 `ARCHIVE_INCOMPLETE`** (not a 200) with the unreleased items in `details.outstanding`, precisely so the result is never cached against an `Idempotency-Key`: **repeat the same request to reconcile.** On an already archived contact the release runs again instead of short-circuiting, which is what makes the retry finish the job. A repeat call after a 200 is a no-op.

Needs its own `contacts:delete` scope for that reason; `contacts:write` alone cannot retire a member. Two contacts are refused rather than archived, because each needs a decision the call cannot make: a **staff member or owner** who can reach the admin portal (archiving would revoke that login - remove their access in 1club first); an **instructor** whose profile still has upcoming classes or recurring templates (reassign those first); and a contact **paying part of a split bill** on a future booking (settle that split first, since taking someone off a bill moves money and archiving moves none). An ordinary member with a portal login is *not* refused - that is most of the people worth archiving.




## OpenAPI

````yaml /openapi-platform.json delete /v1/platform/contacts/{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/contacts/{id}:
    delete:
      tags:
        - Contacts
      summary: Archive a contact
      description: >
        Retires a contact. Nothing is destroyed: the contact is archived, which
        drops them out of the contacts list and out of every search, while their
        transaction history stays intact - so the response is the contact
        itself, now with `type` `archived`, rather than an empty body. Archiving
        also releases everything they hold on the forward schedule - active
        memberships, bookings they organized, and bookings they are only a
        player on - and none of that is restored by un-archiving, which is done
        in 1club.


        To preview the impact, `GET /v1/platform/memberships?contactId={id}`
        lists the memberships that will be cancelled, and `GET
        /v1/platform/bookings?contactId={id}` the bookings that will be. Neither
        is an exhaustive preview, so treat them as indicative: the bookings list
        matches only bookings the contact **organized** - never the ones they
        are an additional player on, which are released too - and it defaults to
        a **7-day** window, so pass `from` and `to` (up to 93 days per request,
        consecutive windows beyond that) to see further out.


        A release can fail on an individual membership or booking without
        blocking the archive - the contact is retired either way, so a stuck
        booking cannot leave it active. That answers **409
        `ARCHIVE_INCOMPLETE`** (not a 200) with the unreleased items in
        `details.outstanding`, precisely so the result is never cached against
        an `Idempotency-Key`: **repeat the same request to reconcile.** On an
        already archived contact the release runs again instead of
        short-circuiting, which is what makes the retry finish the job. A repeat
        call after a 200 is a no-op.


        Needs its own `contacts:delete` scope for that reason; `contacts:write`
        alone cannot retire a member. Two contacts are refused rather than
        archived, because each needs a decision the call cannot make: a **staff
        member or owner** who can reach the admin portal (archiving would revoke
        that login - remove their access in 1club first); an **instructor**
        whose profile still has upcoming classes or recurring templates
        (reassign those first); and a contact **paying part of a split bill** on
        a future booking (settle that split first, since taking someone off a
        bill moves money and archiving moves none). An ordinary member with a
        portal login is *not* refused - that is most of the people worth
        archiving.
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: integer
          description: 1club contact id
      responses:
        '200':
          description: The contact is archived and everything it held was released
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlatformContact'
        '400':
          $ref: '#/components/responses/PlatformBadRequest'
        '401':
          $ref: '#/components/responses/PlatformUnauthorized'
        '403':
          description: API key is missing the required `contacts:delete` scope
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlatformError'
        '404':
          $ref: '#/components/responses/PlatformNotFound'
        '409':
          description: >
            Either the archive was refused outright (`CONFLICT` - the contact
            can reach the admin portal, their instructor profile still has
            upcoming assignments, or they are paying part of a split bill;
            resolve it in 1club, then retry), or the contact WAS archived and
            the release did not finish (`ARCHIVE_INCOMPLETE`; repeat the request
            to reconcile). Read `code` to tell them apart - one means nothing
            happened, the other means the archive did.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/PlatformError'
                  - $ref: '#/components/schemas/PlatformArchiveIncomplete'
        '429':
          $ref: '#/components/responses/PlatformRateLimited'
      security:
        - customerApiAuth: []
components:
  schemas:
    PlatformContact:
      type: object
      description: >-
        A contact (customer) in the organization. Reference the `id` as
        `contactId` when creating a booking.
      properties:
        id:
          type: integer
        uuid:
          type: string
          format: uuid
          description: >-
            Stable unguessable identifier. Prefer this over `id` when storing a
            reference.
        name:
          type: string
        firstName:
          type: string
        lastName:
          type: string
          nullable: true
        email:
          type: string
          nullable: true
        phone:
          type: string
          nullable: true
        type:
          type: string
          nullable: true
          description: >-
            Derived contact type (e.g. contact, member, lead). `archived` marks
            a retired contact - hidden from the list and from search, but still
            readable by id.
        createdAt:
          type: string
          format: date-time
    PlatformError:
      type: object
      properties:
        error:
          type: string
    PlatformArchiveIncomplete:
      type: object
      description: >-
        A 409 with code `ARCHIVE_INCOMPLETE`. The contact IS archived - only the
        release of what it held is unfinished. Repeat the same DELETE (the same
        `Idempotency-Key` is fine, a partial result is never replayed) to
        reconcile what is listed.
      properties:
        error:
          type: string
        message:
          type: string
        code:
          type: string
          enum:
            - ARCHIVE_INCOMPLETE
        details:
          type: object
          properties:
            outstanding:
              type: array
              description: What is still live on the archived contact.
              items:
                type: object
                properties:
                  kind:
                    type: string
                    enum:
                      - membership
                      - booking
                      - participation
                  id:
                    type: integer
                    description: >-
                      The membership id, or the booking id for a booking or
                      participation.
  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.

````