openapi: 3.0.3
info:
  title: Engagifii CRM API
  description: |
    Comprehensive RESTful API for the Engagifii Customer Relationship Management platform. 
    This API enables external developers to integrate with core CRM functionality including 
    people management, organization hierarchies, committee structures, and advocacy relationships.
    
    ## Key Features
    - Multi-tenant architecture with secure tenant isolation
    - OAuth2 authentication with bearer tokens  
    - Comprehensive people and organization management
    - Committee and governance structure support
    - Political advocacy and relationship tracking
    - Advanced search and filtering capabilities
    - Real-time updates via SignalR integration
    
    ## Getting Started
    1. Obtain client credentials (Client ID and Secret) from your administrator
    2. Get an access token using OAuth2 client credentials flow
    3. Include the bearer token and tenant-code header in all requests
    4. Start with the /people endpoint to explore basic functionality
    
    For detailed integration guidance, see our [Getting Started Guide](./getting-started.md).
  version: 1.0.0
  contact:
    name: Engagifii API Support
    url: https://engagifii.com/support
    email: api-support@engagifii.com
  license:
    name: Proprietary
    url: https://engagifii.com/license
servers:
  - url: https://builtin-crm.azurewebsites.net/api/v1
    description: Production server
  - url: https://staging-crm.azurewebsites.net/api/v1
    description: Staging server
  - url: https://dev-crm.azurewebsites.net/api/v1
    description: Development server

security:
  - BearerAuth: []

paths:
  /people/{id}:
    get:
      tags:
        - People
      summary: Get person details
      description: Retrieve detailed information about a specific person including their contact information, positions, and relationships.
      operationId: getPersonById
      parameters:
        - $ref: '#/components/parameters/PersonId'
        - $ref: '#/components/parameters/TenantCode'
      responses:
        '200':
          description: Person details retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PersonDetailView'
        '404':
          $ref: '#/components/responses/NotFound'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      
  /people/PeoplePagingList:
    post:
      tags:
        - People
      summary: List people with pagination and filtering
      description: Retrieve a paginated list of people with advanced filtering capabilities including search, organization, tags, and custom field filters.
      operationId: listPeople
      parameters:
        - $ref: '#/components/parameters/TenantCode'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PeopleListRequest'
            examples:
              basic:
                summary: Basic pagination
                value:
                  pageIndex: 0
                  pageSize: 50
                  sortField: lastName
                  sortDirection: asc
              filtered:
                summary: With filters
                value:
                  pageIndex: 0
                  pageSize: 25
                  filter:
                    organizationId: 123e4567-e89b-12d3-a456-426614174000
                    isActive: true
                    tags: ["vip", "board-member"]
      responses:
        '200':
          description: People list retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PeopleListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /people/CreatePersonInline/{organizationId}:
    post:
      tags:
        - People
      summary: Create a new person
      description: Create a new person record associated with the specified organization.
      operationId: createPerson
      parameters:
        - name: organizationId
          in: path
          required: true
          description: ID of the organization to associate the person with
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/TenantCode'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePersonRequest'
            example:
              firstName: John
              lastName: Doe
              email: john.doe@example.com
              phone: "+1-555-123-4567"
              title: Software Engineer
              addresses:
                - type: Primary
                  street: 123 Main St
                  city: New York
                  state: NY
                  zipCode: "10001"
                  country: USA
      responses:
        '201':
          description: Person created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PersonView'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'

  /organization:
    post:
      tags:
        - Organizations
      summary: Create a new organization
      description: Create a new organization with optional parent organization relationship.
      operationId: createOrganization
      parameters:
        - $ref: '#/components/parameters/TenantCode'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrganizationRequest'
            example:
              name: Acme Corporation
              type: Company
              description: Leading provider of innovative solutions
              website: https://acme.com
              email: info@acme.com
              phone: "+1-555-000-1234"
              addresses:
                - type: Headquarters
                  street: 100 Corporate Blvd
                  city: San Francisco
                  state: CA
                  zipCode: "94105"
                  country: USA
      responses:
        '201':
          description: Organization created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationView'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /organization/{id}:
    get:
      tags:
        - Organizations
      summary: Get organization details
      description: Retrieve detailed information about a specific organization including child organizations and member count.
      operationId: getOrganizationById
      parameters:
        - name: id
          in: path
          required: true
          description: Organization ID
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/TenantCode'
      responses:
        '200':
          description: Organization details retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrganizationDetailView'
        '404':
          $ref: '#/components/responses/NotFound'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /committee/get/{committeeId}:
    get:
      tags:
        - Committees
      summary: Get committee details
      description: Retrieve detailed information about a specific committee including positions and current members.
      operationId: getCommitteeById
      parameters:
        - name: committeeId
          in: path
          required: true
          description: Committee ID
          schema:
            type: string
        - $ref: '#/components/parameters/TenantCode'
      responses:
        '200':
          description: Committee details retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CommitteeDetailView'
        '404':
          $ref: '#/components/responses/NotFound'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /committee/createcommittee:
    post:
      tags:
        - Committees
      summary: Create a new committee
      description: Create a new committee with positions and associate it with an organization.
      operationId: createCommittee
      parameters:
        - $ref: '#/components/parameters/TenantCode'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCommitteeRequest'
      responses:
        '201':
          description: Committee created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CommitteeView'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /advocacy/GetRelationshipTypes:
    get:
      tags:
        - Advocacy
      summary: Get relationship types
      description: Retrieve all available relationship types for advocacy connections between people and officials.
      operationId: getRelationshipTypes
      parameters:
        - $ref: '#/components/parameters/TenantCode'
      responses:
        '200':
          description: Relationship types retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/RelationshipType'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /advocacy/list/people-relationships/{peopleId}:
    get:
      tags:
        - Advocacy
      summary: Get people relationships
      description: Retrieve all advocacy relationships for a specific person.
      operationId: getPeopleRelationships
      parameters:
        - name: peopleId
          in: path
          required: true
          description: Person ID
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/TenantCode'
      responses:
        '200':
          description: People relationships retrieved successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/PeopleRelationship'
        '404':
          $ref: '#/components/responses/NotFound'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |
        OAuth2 bearer token authentication. To get a token:
        1. POST to /oauth/token with client_credentials grant
        2. Include the token in the Authorization header as "Bearer {token}"
        3. Token expires after 1 hour and must be refreshed

  parameters:
    TenantCode:
      name: tenant-code
      in: header
      required: true
      description: Unique identifier for your organization/tenant
      schema:
        type: string
        example: your-organization-code
        
    PersonId:
      name: id
      in: path
      required: true
      description: Unique identifier for the person
      schema:
        type: string
        format: uuid
        example: 123e4567-e89b-12d3-a456-426614174000

  schemas:
    PersonView:
      type: object
      description: Basic person information
      properties:
        id:
          type: string
          format: uuid
          description: Unique identifier
          example: 123e4567-e89b-12d3-a456-426614174000
        firstName:
          type: string
          maxLength: 50
          description: Person's first name
          example: John
        lastName:
          type: string
          maxLength: 50
          description: Person's last name
          example: Doe
        email:
          type: string
          format: email
          maxLength: 254
          description: Primary email address
          example: john.doe@example.com
        phone:
          type: string
          maxLength: 20
          description: Primary phone number
          example: "+1-555-123-4567"
        organizationId:
          type: string
          format: uuid
          description: Associated organization ID
          example: org-456
        organizationName:
          type: string
          description: Associated organization name
          example: Acme Corporation
        isActive:
          type: boolean
          description: Whether the person is active
          example: true
        createdDate:
          type: string
          format: date-time
          description: Creation timestamp
          example: "2023-01-15T10:30:00Z"
        modifiedDate:
          type: string
          format: date-time
          description: Last modification timestamp
          example: "2024-01-20T15:45:00Z"

    PersonDetailView:
      allOf:
        - $ref: '#/components/schemas/PersonView'
        - type: object
          properties:
            middleName:
              type: string
              maxLength: 50
              example: Michael
            suffix:
              type: string
              maxLength: 10
              example: Jr.
            secondaryEmail:
              type: string
              format: email
              example: john.personal@gmail.com
            mobilePhone:
              type: string
              example: "+1-555-987-6543"
            title:
              type: string
              maxLength: 100
              example: Senior Software Engineer
            department:
              type: string
              maxLength: 100
              example: Engineering
            addresses:
              type: array
              items:
                $ref: '#/components/schemas/Address'
            positions:
              type: array
              items:
                $ref: '#/components/schemas/Position'
            tags:
              type: array
              items:
                $ref: '#/components/schemas/Tag'
            customFields:
              type: object
              additionalProperties: true
              example:
                birthDate: "1985-03-15T00:00:00Z"
                linkedInProfile: https://linkedin.com/in/johndoe
                notes: Key stakeholder in platform decisions

    CreatePersonRequest:
      type: object
      required:
        - firstName
        - lastName
        - email
      properties:
        firstName:
          type: string
          minLength: 1
          maxLength: 50
        lastName:
          type: string
          minLength: 1
          maxLength: 50
        middleName:
          type: string
          maxLength: 50
        email:
          type: string
          format: email
          maxLength: 254
        phone:
          type: string
          maxLength: 20
        title:
          type: string
          maxLength: 100
        department:
          type: string
          maxLength: 100
        addresses:
          type: array
          items:
            $ref: '#/components/schemas/CreateAddressRequest'
        customFields:
          type: object
          additionalProperties: true

    PeopleListRequest:
      type: object
      properties:
        pageIndex:
          type: integer
          minimum: 0
          default: 0
          description: Zero-based page number
        pageSize:
          type: integer
          minimum: 1
          maximum: 500
          default: 50
          description: Number of records per page
        sortField:
          type: string
          maxLength: 50
          description: Field name to sort by
          example: lastName
        sortDirection:
          type: string
          enum: [asc, desc]
          default: asc
          description: Sort direction
        searchText:
          type: string
          maxLength: 200
          description: General search query
        filter:
          $ref: '#/components/schemas/PeopleFilter'

    PeopleFilter:
      type: object
      properties:
        firstName:
          type: string
        lastName:
          type: string
        email:
          type: string
        organizationId:
          type: string
          format: uuid
        isActive:
          type: boolean
        hasEmail:
          type: boolean
        hasPhone:
          type: boolean
        dateRange:
          $ref: '#/components/schemas/DateRangeFilter'
        locationFilter:
          $ref: '#/components/schemas/LocationFilter'
        tagIds:
          type: array
          items:
            type: string
        customFields:
          type: object
          additionalProperties: true

    PeopleListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/PersonView'
        totalCount:
          type: integer
          description: Total records matching filter
        pageIndex:
          type: integer
          description: Current page number
        pageSize:
          type: integer
          description: Records per page
        hasNextPage:
          type: boolean
          description: Whether more pages exist
        hasPreviousPage:
          type: boolean
          description: Whether previous pages exist

    OrganizationView:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: org-123
        name:
          type: string
          maxLength: 200
          example: Acme Corporation
        shortName:
          type: string
          maxLength: 50
          example: ACME
        organizationType:
          type: string
          example: Company
        website:
          type: string
          format: uri
          example: https://acme.com
        email:
          type: string
          format: email
          example: info@acme.com
        phone:
          type: string
          example: "+1-555-000-1234"
        memberCount:
          type: integer
          minimum: 0
          example: 150
        isActive:
          type: boolean
          example: true
        createdDate:
          type: string
          format: date-time
        modifiedDate:
          type: string
          format: date-time

    OrganizationDetailView:
      allOf:
        - $ref: '#/components/schemas/OrganizationView'
        - type: object
          properties:
            description:
              type: string
              maxLength: 1000
            parentOrganizationId:
              type: string
              format: uuid
              nullable: true
            addresses:
              type: array
              items:
                $ref: '#/components/schemas/Address'
            childOrganizations:
              type: array
              items:
                $ref: '#/components/schemas/OrganizationView'
            relatedOrganizations:
              type: array
              items:
                $ref: '#/components/schemas/RelatedOrganization'
            tags:
              type: array
              items:
                $ref: '#/components/schemas/Tag'

    CreateOrganizationRequest:
      type: object
      required:
        - name
        - type
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 200
        type:
          type: string
          minLength: 1
        description:
          type: string
          maxLength: 1000
        parentOrganizationId:
          type: string
          format: uuid
          nullable: true
        website:
          type: string
          format: uri
        email:
          type: string
          format: email
        phone:
          type: string
        addresses:
          type: array
          items:
            $ref: '#/components/schemas/CreateAddressRequest'

    CommitteeView:
      type: object
      properties:
        id:
          type: string
          example: committee-123
        name:
          type: string
          maxLength: 200
          example: Board of Directors
        description:
          type: string
          maxLength: 1000
        committeeType:
          type: string
          example: Board
        organizationId:
          type: string
          format: uuid
        organizationName:
          type: string
        memberCount:
          type: integer
          minimum: 0
        isActive:
          type: boolean
        createdDate:
          type: string
          format: date-time
        modifiedDate:
          type: string
          format: date-time

    CommitteeDetailView:
      allOf:
        - $ref: '#/components/schemas/CommitteeView'
        - type: object
          properties:
            startDate:
              type: string
              format: date-time
            endDate:
              type: string
              format: date-time
              nullable: true
            positions:
              type: array
              items:
                $ref: '#/components/schemas/CommitteePosition'
            members:
              type: array
              items:
                $ref: '#/components/schemas/CommitteeMember'
            tags:
              type: array
              items:
                $ref: '#/components/schemas/Tag'

    CreateCommitteeRequest:
      type: object
      required:
        - name
        - organizationId
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 200
        description:
          type: string
          maxLength: 1000
        committeeTypeId:
          type: string
        organizationId:
          type: string
          format: uuid
        startDate:
          type: string
          format: date-time
        endDate:
          type: string
          format: date-time
        positions:
          type: array
          items:
            $ref: '#/components/schemas/CreateCommitteePositionRequest'

    Address:
      type: object
      properties:
        id:
          type: string
          format: uuid
        type:
          type: string
          enum: [Primary, Secondary, Headquarters, Branch, Billing, Shipping]
          example: Primary
        street:
          type: string
          maxLength: 200
          example: 123 Main Street
        street2:
          type: string
          maxLength: 200
          example: Suite 456
        city:
          type: string
          maxLength: 100
          example: New York
        state:
          type: string
          maxLength: 50
          example: NY
        zipCode:
          type: string
          maxLength: 20
          example: "10001"
        country:
          type: string
          maxLength: 50
          example: USA
        latitude:
          type: number
          format: double
          minimum: -90
          maximum: 90
        longitude:
          type: number
          format: double
          minimum: -180
          maximum: 180
        isActive:
          type: boolean
          default: true

    CreateAddressRequest:
      type: object
      required:
        - type
        - street
        - city
        - state
        - zipCode
        - country
      properties:
        type:
          type: string
          enum: [Primary, Secondary, Headquarters, Branch, Billing, Shipping]
        street:
          type: string
          minLength: 1
          maxLength: 200
        street2:
          type: string
          maxLength: 200
        city:
          type: string
          minLength: 1
          maxLength: 100
        state:
          type: string
          minLength: 1
          maxLength: 50
        zipCode:
          type: string
          minLength: 1
          maxLength: 20
        country:
          type: string
          minLength: 1
          maxLength: 50

    Position:
      type: object
      properties:
        id:
          type: string
          format: uuid
        organizationId:
          type: string
          format: uuid
        title:
          type: string
          maxLength: 100
        department:
          type: string
          maxLength: 100
        startDate:
          type: string
          format: date-time
        endDate:
          type: string
          format: date-time
          nullable: true
        isActive:
          type: boolean
        isPrimary:
          type: boolean

    Tag:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
          maxLength: 100
        description:
          type: string
          maxLength: 500
        color:
          type: string
          pattern: '^#[0-9A-Fa-f]{6}$'
          example: "#FF5733"
        category:
          type: string
          maxLength: 50
        isActive:
          type: boolean
        usageCount:
          type: integer
          minimum: 0

    RelationshipType:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
          maxLength: 100
        description:
          type: string
          maxLength: 500
        category:
          type: string
          maxLength: 50

    PeopleRelationship:
      type: object
      properties:
        id:
          type: string
          format: uuid
        personId:
          type: string
          format: uuid
        personName:
          type: string
        relatedOfficialId:
          type: string
          format: uuid
        relatedOfficialName:
          type: string
        relationshipType:
          type: string
        relationshipTypeId:
          type: string
        startDate:
          type: string
          format: date-time
        endDate:
          type: string
          format: date-time
          nullable: true
        isActive:
          type: boolean
        notes:
          type: string
          maxLength: 1000

    CommitteePosition:
      type: object
      properties:
        id:
          type: string
        title:
          type: string
          maxLength: 100
        description:
          type: string
          maxLength: 500
        sequence:
          type: integer
          minimum: 1
        isRequired:
          type: boolean
        maxOccupants:
          type: integer
          minimum: 1

    CommitteeMember:
      type: object
      properties:
        id:
          type: string
        personId:
          type: string
          format: uuid
        personName:
          type: string
        positionId:
          type: string
        positionTitle:
          type: string
        startDate:
          type: string
          format: date-time
        endDate:
          type: string
          format: date-time
          nullable: true
        isActive:
          type: boolean

    CreateCommitteePositionRequest:
      type: object
      required:
        - title
        - sequence
      properties:
        title:
          type: string
          minLength: 1
          maxLength: 100
        description:
          type: string
          maxLength: 500
        sequence:
          type: integer
          minimum: 1
        isRequired:
          type: boolean
          default: false
        maxOccupants:
          type: integer
          minimum: 1
          default: 1

    RelatedOrganization:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        relationshipType:
          type: string
        relationshipStartDate:
          type: string
          format: date-time

    DateRangeFilter:
      type: object
      properties:
        startDate:
          type: string
          format: date-time
        endDate:
          type: string
          format: date-time
        dateField:
          type: string
          enum: [createdDate, modifiedDate, startDate, endDate]
          default: createdDate
        includeNullDates:
          type: boolean
          default: false

    LocationFilter:
      type: object
      properties:
        country:
          type: string
        state:
          type: string
        city:
          type: string
        zipCode:
          type: string
        radius:
          type: number
          minimum: 0
        radiusUnit:
          type: string
          enum: [miles, kilometers]
          default: miles

    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              description: Machine-readable error code
              example: VALIDATION_ERROR
            message:
              type: string
              description: Human-readable error message
              example: The request contains invalid data
            details:
              type: array
              items:
                type: object
                properties:
                  field:
                    type: string
                    description: Field that caused the error
                  message:
                    type: string
                    description: Field-specific error message
                  code:
                    type: string
                    description: Field-specific error code
            timestamp:
              type: string
              format: date-time
              description: When the error occurred
            requestId:
              type: string
              description: Unique request identifier for support
            path:
              type: string
              description: API endpoint that generated the error
            method:
              type: string
              description: HTTP method used

    ValidationErrorResponse:
      allOf:
        - $ref: '#/components/schemas/ErrorResponse'
        - type: object
          properties:
            error:
              type: object
              properties:
                code:
                  type: string
                  example: VALIDATION_ERROR
                details:
                  type: array
                  items:
                    type: object
                    properties:
                      field:
                        type: string
                      message:
                        type: string
                      code:
                        type: string
                        enum: [REQUIRED_FIELD, INVALID_FORMAT, INVALID_LENGTH, INVALID_VALUE, DUPLICATE_VALUE]

  responses:
    BadRequest:
      description: Bad request - invalid request format or missing required fields
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: BAD_REQUEST
              message: Invalid request format
              timestamp: "2024-01-25T10:30:00Z"
              requestId: req-123

    Unauthorized:
      description: Unauthorized - missing or invalid access token
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: INVALID_TOKEN
              message: The access token has expired
              timestamp: "2024-01-25T10:30:00Z"
              requestId: req-auth-001

    Forbidden:
      description: Forbidden - invalid tenant code or insufficient permissions
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: INVALID_TENANT
              message: The specified tenant code is not valid
              timestamp: "2024-01-25T10:30:00Z"
              requestId: req-tenant-001

    NotFound:
      description: Not found - requested resource does not exist
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: RESOURCE_NOT_FOUND
              message: The requested resource was not found
              timestamp: "2024-01-25T10:30:00Z"
              requestId: req-not-found-001

    ValidationError:
      description: Validation error - request contains invalid data
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ValidationErrorResponse'
          example:
            error:
              code: VALIDATION_ERROR
              message: Request contains validation errors
              details:
                - field: firstName
                  message: First name is required
                  code: REQUIRED_FIELD
                - field: email
                  message: Email format is invalid
                  code: INVALID_FORMAT
              timestamp: "2024-01-25T10:30:00Z"
              requestId: req-validation-001

    RateLimited:
      description: Rate limit exceeded - too many requests
      headers:
        X-RateLimit-Limit:
          description: Request limit per minute
          schema:
            type: integer
            example: 1000
        X-RateLimit-Remaining:
          description: Remaining requests in current window
          schema:
            type: integer
            example: 0
        X-RateLimit-Reset:
          description: Unix timestamp when limit resets
          schema:
            type: integer
            example: 1640995260
        Retry-After:
          description: Seconds to wait before retry
          schema:
            type: integer
            example: 60
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: RATE_LIMIT_EXCEEDED
              message: Request rate limit exceeded
              timestamp: "2024-01-25T10:30:00Z"
              requestId: req-rate-001

tags:
  - name: People
    description: Person/member management operations
  - name: Organizations
    description: Organization and company management
  - name: Committees
    description: Committee and governance structure management
  - name: Advocacy
    description: Political advocacy and relationship management

externalDocs:
  description: Complete API documentation and integration guides
  url: ./README.md