openapi: 3.0.3
info:
  title: 'SocialHive Promo API Documentation'
  description: 'API for managing promotions, receipts, entries, members, products, locations, and more.'
  version: 1.0.0
servers:
  -
    url: 'https://unknown-headline-volunteer-brochure.trycloudflare.com'
tags:
  -
    name: 'Promotion Participants'
    description: ''
  -
    name: 'Promotion Entries'
    description: ''
  -
    name: 'Promotion Codes'
    description: ''
  -
    name: 'Promotion Votes'
    description: ''
  -
    name: 'Promotion Promoters'
    description: ''
  -
    name: Tools
    description: ''
  -
    name: Webhooks
    description: ''
components:
  securitySchemes:
    default:
      type: http
      scheme: bearer
      description: 'Authenticate using a Sanctum API token. You can create tokens from the **API Tokens** page in your dashboard. Pass the token as a Bearer token in the `Authorization` header.'
security:
  -
    default: []
paths:
  '/api/v1/promotions/{promotion_slug}/participants':
    get:
      summary: 'List promotion participants'
      operationId: listPromotionParticipants
      description: 'Return a paginated list of participants for the given promotion.'
      parameters:
        -
          in: query
          name: 'filter[name]'
          description: 'Filter participants by name.'
          example: Alice
          required: false
          schema:
            type: string
            description: 'Filter participants by name.'
            example: Alice
        -
          in: query
          name: 'filter[phone]'
          description: 'Filter participants by phone number.'
          example: '18765554444'
          required: false
          schema:
            type: string
            description: 'Filter participants by phone number.'
            example: '18765554444'
        -
          in: query
          name: 'filter[id]'
          description: 'Filter participants by internal member ID.'
          example: 42
          required: false
          schema:
            type: integer
            description: 'Filter participants by internal member ID.'
            example: 42
        -
          in: query
          name: 'filter[email]'
          description: 'Filter participants by email address.'
          example: alice@example.com
          required: false
          schema:
            type: string
            description: 'Filter participants by email address.'
            example: alice@example.com
        -
          in: query
          name: sort
          description: 'Sort by name or joined_at. Use -joined_at for descending order.'
          example: '-joined_at'
          required: false
          schema:
            type: string
            description: 'Sort by name or joined_at. Use -joined_at for descending order.'
            example: '-joined_at'
        -
          in: query
          name: page
          description: 'The page number.'
          example: 1
          required: false
          schema:
            type: integer
            description: 'The page number.'
            example: 1
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  data:
                    -
                      id: 45
                      phone: '18765580001'
                      name: 'E2E Voter'
                      email: eve@example.com
                      country: JM
                      status: active
                      pivot:
                        joined_at: '2026-05-01T10:15:30.000000Z'
                        vote_status: complete
                        vote_rounds_completed: 1
                        voted_at: '2026-05-01T10:15:30.000000Z'
                  links:
                    first: 'http://promo-next.test/api/v1/promotions/community-vote-2026/participants?page=1'
                    last: 'http://promo-next.test/api/v1/promotions/community-vote-2026/participants?page=1'
                    prev: null
                    next: null
                  meta:
                    current_page: 1
                    from: 1
                    last_page: 1
                    path: 'http://promo-next.test/api/v1/promotions/community-vote-2026/participants'
                    per_page: 15
                    to: 1
                    total: 1
                properties:
                  data:
                    type: array
                    example:
                      -
                        id: 45
                        phone: '18765580001'
                        name: 'E2E Voter'
                        email: eve@example.com
                        country: JM
                        status: active
                        pivot:
                          joined_at: '2026-05-01T10:15:30.000000Z'
                          vote_status: complete
                          vote_rounds_completed: 1
                          voted_at: '2026-05-01T10:15:30.000000Z'
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          example: 45
                        phone:
                          type: string
                          example: '18765580001'
                        name:
                          type: string
                          example: 'E2E Voter'
                        email:
                          type: string
                          example: eve@example.com
                        country:
                          type: string
                          example: JM
                        status:
                          type: string
                          example: active
                        pivot:
                          type: object
                          properties:
                            joined_at:
                              type: string
                              example: '2026-05-01T10:15:30.000000Z'
                            vote_status:
                              type: string
                              example: complete
                            vote_rounds_completed:
                              type: integer
                              example: 1
                            voted_at:
                              type: string
                              example: '2026-05-01T10:15:30.000000Z'
                  links:
                    type: object
                    properties:
                      first:
                        type: string
                        example: 'http://promo-next.test/api/v1/promotions/community-vote-2026/participants?page=1'
                      last:
                        type: string
                        example: 'http://promo-next.test/api/v1/promotions/community-vote-2026/participants?page=1'
                      prev:
                        type: string
                        example: null
                        nullable: true
                      next:
                        type: string
                        example: null
                        nullable: true
                  meta:
                    type: object
                    properties:
                      current_page:
                        type: integer
                        example: 1
                      from:
                        type: integer
                        example: 1
                      last_page:
                        type: integer
                        example: 1
                      path:
                        type: string
                        example: 'http://promo-next.test/api/v1/promotions/community-vote-2026/participants'
                      per_page:
                        type: integer
                        example: 15
                      to:
                        type: integer
                        example: 1
                      total:
                        type: integer
                        example: 1
      tags:
        - 'Promotion Participants'
    post:
      summary: 'Create or attach a participant'
      operationId: createOrAttachAParticipant
      description: 'Create a member if needed, attach them to the promotion, and optionally record voting responses.'
      parameters: []
      responses:
        201:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  status: success
                  message: 'Participant added to promotion successfully.'
                  data:
                    id: 45
                    phone: '18765552222'
                    name: 'API Participant'
                    email: api@test.com
                    country: JM
                    status: active
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                    example: 'Participant added to promotion successfully.'
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        example: 45
                      phone:
                        type: string
                        example: '18765552222'
                      name:
                        type: string
                        example: 'API Participant'
                      email:
                        type: string
                        example: api@test.com
                      country:
                        type: string
                        example: JM
                      status:
                        type: string
                        example: active
      tags:
        - 'Promotion Participants'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                member_id:
                  type: string
                  description: 'The participant identifier, stored as the member phone number.'
                  example: '18765552222'
                name:
                  type: string
                  description: 'The participant name.'
                  example: 'API Participant'
                email:
                  type: string
                  description: 'The participant email address.'
                  example: api@test.com
                  nullable: true
                dob:
                  type: date
                  description: 'The participant date of birth.'
                  example: '1990-06-15'
                  nullable: true
                country:
                  type: string
                  description: 'The participant country.'
                  example: JM
                  nullable: true
                location:
                  type: string
                  description: 'The participant location.'
                  example: Kingston
                  nullable: true
                opt_in:
                  type: boolean
                  description: 'Whether the participant opted in to marketing.'
                  example: true
                  nullable: true
                metadata:
                  type: object
                  description: 'Additional promotion-specific metadata to store on the pivot record.'
                  example:
                    source: manual-entry
                  properties: {  }
                  nullable: true
                responses:
                  type: object
                  description: 'Voting responses keyed by question key for voting promotions.'
                  example:
                    district: Kingston
                    community: 'Harbour View'
                  properties: {  }
              required:
                - member_id
                - name
    parameters:
      -
        in: path
        name: promotion_slug
        description: 'The promotion slug.'
        example: community-vote-2026
        required: true
        schema:
          type: string
  '/api/v1/promotions/{promotion_slug}/entries':
    get:
      summary: 'List promotion entries'
      operationId: listPromotionEntries
      description: 'Return a paginated list of entries for the given promotion.'
      parameters:
        -
          in: query
          name: 'filter[member_id]'
          description: 'Filter entries by member ID.'
          example: 42
          required: false
          schema:
            type: integer
            description: 'Filter entries by member ID.'
            example: 42
        -
          in: query
          name: 'filter[status]'
          description: 'Filter entries by entry status.'
          example: valid
          required: false
          schema:
            type: string
            description: 'Filter entries by entry status.'
            example: valid
        -
          in: query
          name: 'filter[source_type]'
          description: 'Filter entries by source type.'
          example: App\Models\Receipt
          required: false
          schema:
            type: string
            description: 'Filter entries by source type.'
            example: App\Models\Receipt
        -
          in: query
          name: sort
          description: 'Sort by created_at. Use -created_at for descending order.'
          example: '-created_at'
          required: false
          schema:
            type: string
            description: 'Sort by created_at. Use -created_at for descending order.'
            example: '-created_at'
        -
          in: query
          name: page
          description: 'The page number.'
          example: 1
          required: false
          schema:
            type: integer
            description: 'The page number.'
            example: 1
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  data:
                    -
                      id: 123
                      member:
                        id: 45
                        phone: '18765580001'
                        name: 'E2E Voter'
                        email: eve@example.com
                        country: JM
                        status: active
                      status: valid
                      source_type: App\Models\Receipt
                      source_id: 987
                      created_at: '2026-05-01T10:15:30.000000Z'
                  links:
                    first: 'http://promo-next.test/api/v1/promotions/summer-cashback/entries?page=1'
                    last: 'http://promo-next.test/api/v1/promotions/summer-cashback/entries?page=1'
                    prev: null
                    next: null
                  meta:
                    current_page: 1
                    from: 1
                    last_page: 1
                    path: 'http://promo-next.test/api/v1/promotions/summer-cashback/entries'
                    per_page: 15
                    to: 1
                    total: 1
                properties:
                  data:
                    type: array
                    example:
                      -
                        id: 123
                        member:
                          id: 45
                          phone: '18765580001'
                          name: 'E2E Voter'
                          email: eve@example.com
                          country: JM
                          status: active
                        status: valid
                        source_type: App\Models\Receipt
                        source_id: 987
                        created_at: '2026-05-01T10:15:30.000000Z'
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          example: 123
                        member:
                          type: object
                          properties:
                            id:
                              type: integer
                              example: 45
                            phone:
                              type: string
                              example: '18765580001'
                            name:
                              type: string
                              example: 'E2E Voter'
                            email:
                              type: string
                              example: eve@example.com
                            country:
                              type: string
                              example: JM
                            status:
                              type: string
                              example: active
                        status:
                          type: string
                          example: valid
                        source_type:
                          type: string
                          example: App\Models\Receipt
                        source_id:
                          type: integer
                          example: 987
                        created_at:
                          type: string
                          example: '2026-05-01T10:15:30.000000Z'
                  links:
                    type: object
                    properties:
                      first:
                        type: string
                        example: 'http://promo-next.test/api/v1/promotions/summer-cashback/entries?page=1'
                      last:
                        type: string
                        example: 'http://promo-next.test/api/v1/promotions/summer-cashback/entries?page=1'
                      prev:
                        type: string
                        example: null
                        nullable: true
                      next:
                        type: string
                        example: null
                        nullable: true
                  meta:
                    type: object
                    properties:
                      current_page:
                        type: integer
                        example: 1
                      from:
                        type: integer
                        example: 1
                      last_page:
                        type: integer
                        example: 1
                      path:
                        type: string
                        example: 'http://promo-next.test/api/v1/promotions/summer-cashback/entries'
                      per_page:
                        type: integer
                        example: 15
                      to:
                        type: integer
                        example: 1
                      total:
                        type: integer
                        example: 1
      tags:
        - 'Promotion Entries'
    parameters:
      -
        in: path
        name: promotion_slug
        description: 'The promotion slug.'
        example: summer-cashback
        required: true
        schema:
          type: string
  '/api/v1/promotions/{promotion_slug}/entries/{entry_id}':
    get:
      summary: 'Show a promotion entry'
      operationId: showAPromotionEntry
      description: 'Return a single entry that belongs to the given promotion.'
      parameters: []
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  data:
                    id: 123
                    member:
                      id: 45
                      phone: '18765580001'
                      name: 'E2E Voter'
                      email: eve@example.com
                      country: JM
                      status: active
                    status: valid
                    source_type: App\Models\Receipt
                    source_id: 987
                    created_at: '2026-05-01T10:15:30.000000Z'
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        example: 123
                      member:
                        type: object
                        properties:
                          id:
                            type: integer
                            example: 45
                          phone:
                            type: string
                            example: '18765580001'
                          name:
                            type: string
                            example: 'E2E Voter'
                          email:
                            type: string
                            example: eve@example.com
                          country:
                            type: string
                            example: JM
                          status:
                            type: string
                            example: active
                      status:
                        type: string
                        example: valid
                      source_type:
                        type: string
                        example: App\Models\Receipt
                      source_id:
                        type: integer
                        example: 987
                      created_at:
                        type: string
                        example: '2026-05-01T10:15:30.000000Z'
        404:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  message: 'Not Found'
                properties:
                  message:
                    type: string
                    example: 'Not Found'
      tags:
        - 'Promotion Entries'
    parameters:
      -
        in: path
        name: promotion_slug
        description: 'The promotion slug.'
        example: summer-cashback
        required: true
        schema:
          type: string
      -
        in: path
        name: entry_id
        description: 'The ID of the entry.'
        example: 3
        required: true
        schema:
          type: integer
      -
        in: path
        name: entry
        description: 'The entry ID.'
        example: 123
        required: true
        schema:
          type: integer
  '/api/v1/promotions/{promotion_slug}/codes':
    get:
      summary: 'List promotion codes'
      operationId: listPromotionCodes
      description: 'Return a paginated list of codes for the given promotion.'
      parameters:
        -
          in: query
          name: page
          description: 'The page number.'
          example: 1
          required: false
          schema:
            type: integer
            description: 'The page number.'
            example: 1
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  current_page: 1
                  data:
                    -
                      id: 12
                      promotion_id: 5
                      code: WINCODE123
                      used_at: null
                      used_by: null
                      expired_at: null
                      active: true
                      metadata: null
                      created_at: '2026-05-01T10:15:30.000000Z'
                      updated_at: '2026-05-01T10:15:30.000000Z'
                  first_page_url: 'http://promo-next.test/api/v1/promotions/win-big-2026/codes?page=1'
                  from: 1
                  last_page: 1
                  last_page_url: 'http://promo-next.test/api/v1/promotions/win-big-2026/codes?page=1'
                  links:
                    -
                      url: null
                      label: '&laquo; Previous'
                      active: false
                    -
                      url: 'http://promo-next.test/api/v1/promotions/win-big-2026/codes?page=1'
                      label: '1'
                      active: true
                    -
                      url: null
                      label: 'Next &raquo;'
                      active: false
                  next_page_url: null
                  path: 'http://promo-next.test/api/v1/promotions/win-big-2026/codes'
                  per_page: 50
                  prev_page_url: null
                  to: 1
                  total: 1
                properties:
                  current_page:
                    type: integer
                    example: 1
                  data:
                    type: array
                    example:
                      -
                        id: 12
                        promotion_id: 5
                        code: WINCODE123
                        used_at: null
                        used_by: null
                        expired_at: null
                        active: true
                        metadata: null
                        created_at: '2026-05-01T10:15:30.000000Z'
                        updated_at: '2026-05-01T10:15:30.000000Z'
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          example: 12
                        promotion_id:
                          type: integer
                          example: 5
                        code:
                          type: string
                          example: WINCODE123
                        used_at:
                          type: string
                          example: null
                          nullable: true
                        used_by:
                          type: string
                          example: null
                          nullable: true
                        expired_at:
                          type: string
                          example: null
                          nullable: true
                        active:
                          type: boolean
                          example: true
                        metadata:
                          type: string
                          example: null
                          nullable: true
                        created_at:
                          type: string
                          example: '2026-05-01T10:15:30.000000Z'
                        updated_at:
                          type: string
                          example: '2026-05-01T10:15:30.000000Z'
                  first_page_url:
                    type: string
                    example: 'http://promo-next.test/api/v1/promotions/win-big-2026/codes?page=1'
                  from:
                    type: integer
                    example: 1
                  last_page:
                    type: integer
                    example: 1
                  last_page_url:
                    type: string
                    example: 'http://promo-next.test/api/v1/promotions/win-big-2026/codes?page=1'
                  links:
                    type: array
                    example:
                      -
                        url: null
                        label: '&laquo; Previous'
                        active: false
                      -
                        url: 'http://promo-next.test/api/v1/promotions/win-big-2026/codes?page=1'
                        label: '1'
                        active: true
                      -
                        url: null
                        label: 'Next &raquo;'
                        active: false
                    items:
                      type: object
                      properties:
                        url:
                          type: string
                          example: null
                          nullable: true
                        label:
                          type: string
                          example: '&laquo; Previous'
                        active:
                          type: boolean
                          example: false
                  next_page_url:
                    type: string
                    example: null
                    nullable: true
                  path:
                    type: string
                    example: 'http://promo-next.test/api/v1/promotions/win-big-2026/codes'
                  per_page:
                    type: integer
                    example: 50
                  prev_page_url:
                    type: string
                    example: null
                    nullable: true
                  to:
                    type: integer
                    example: 1
                  total:
                    type: integer
                    example: 1
      tags:
        - 'Promotion Codes'
    parameters:
      -
        in: path
        name: promotion_slug
        description: 'The promotion slug.'
        example: win-big-2026
        required: true
        schema:
          type: string
  '/api/v1/promotions/{promotion_slug}/codes/check':
    post:
      summary: 'Check a promotion code'
      operationId: checkAPromotionCode
      description: 'Validate a code for the given promotion without redeeming it.'
      parameters: []
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  message: 'Code is valid and available.'
                  status: available
                properties:
                  message:
                    type: string
                    example: 'Code is valid and available.'
                  status:
                    type: string
                    example: available
        404:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  message: 'Invalid code for this promotion.'
                  status: invalid
                properties:
                  message:
                    type: string
                    example: 'Invalid code for this promotion.'
                  status:
                    type: string
                    example: invalid
      tags:
        - 'Promotion Codes'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                code:
                  type: string
                  description: 'The promotion code to check.'
                  example: VALIDCODE
              required:
                - code
    parameters:
      -
        in: path
        name: promotion_slug
        description: 'The promotion slug.'
        example: win-big-2026
        required: true
        schema:
          type: string
  '/api/v1/promotions/{promotion_slug}/codes/redeem':
    post:
      summary: 'Redeem a promotion code'
      operationId: redeemAPromotionCode
      description: 'Validate and redeem a code for a member in the given promotion.'
      parameters: []
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  message: 'Code redeemed successfully.'
                  data:
                    code: REDEEMME
                    prize: 'Free Drink'
                properties:
                  message:
                    type: string
                    example: 'Code redeemed successfully.'
                  data:
                    type: object
                    properties:
                      code:
                        type: string
                        example: REDEEMME
                      prize:
                        type: string
                        example: 'Free Drink'
        409:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  message: 'This code has already been used.'
                properties:
                  message:
                    type: string
                    example: 'This code has already been used.'
      tags:
        - 'Promotion Codes'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                code:
                  type: string
                  description: 'The promotion code to redeem.'
                  example: REDEEMME
                phone:
                  type: string
                  description: 'The member phone number. Required when member_id is not supplied.'
                  example: '18765551234'
                member_id:
                  type: string
                  description: 'The member identifier fallback, treated as a phone number. Required when phone is not supplied.'
                  example: '18765551234'
              required:
                - code
    parameters:
      -
        in: path
        name: promotion_slug
        description: 'The promotion slug.'
        example: win-big-2026
        required: true
        schema:
          type: string
  '/api/v1/promotions/{promotion_slug}/votes':
    get:
      summary: 'List votes'
      operationId: listVotes
      description: 'Return a paginated, filterable list of individual votes for a voting promotion.'
      parameters:
        -
          in: query
          name: 'filter[question_key]'
          description: 'Filter votes by question key.'
          example: community
          required: false
          schema:
            type: string
            description: 'Filter votes by question key.'
            example: community
        -
          in: query
          name: 'filter[answer]'
          description: 'Filter votes by a partial answer match.'
          example: 'Harbour View'
          required: false
          schema:
            type: string
            description: 'Filter votes by a partial answer match.'
            example: 'Harbour View'
        -
          in: query
          name: 'filter[member_id]'
          description: 'Filter votes by member ID.'
          example: 45
          required: false
          schema:
            type: integer
            description: 'Filter votes by member ID.'
            example: 45
        -
          in: query
          name: 'filter[vote_round]'
          description: 'Filter votes by vote round.'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter votes by vote round.'
            example: 1
        -
          in: query
          name: 'filter[district]'
          description: 'Filter votes by another configured question answer.'
          example: Belize
          required: false
          schema:
            type: string
            description: 'Filter votes by another configured question answer.'
            example: Belize
        -
          in: query
          name: include
          description: 'Include related resources. Supported value: member.'
          example: member
          required: false
          schema:
            type: string
            description: 'Include related resources. Supported value: member.'
            example: member
        -
          in: query
          name: sort
          description: 'Sort by created_at, question_key, answer, or vote_round.'
          example: '-created_at'
          required: false
          schema:
            type: string
            description: 'Sort by created_at, question_key, answer, or vote_round.'
            example: '-created_at'
        -
          in: query
          name: page
          description: 'The page number.'
          example: 1
          required: false
          schema:
            type: integer
            description: 'The page number.'
            example: 1
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  data:
                    -
                      id: 91
                      member_id: 45
                      member:
                        id: 45
                        phone: '18765580001'
                        name: 'E2E Voter'
                        email: eve@example.com
                        country: JM
                        status: active
                      question_key: community
                      answer: 'Harbour View'
                      vote_round: 1
                      created_at: '2026-05-01T10:15:30.000000Z'
                      updated_at: '2026-05-01T10:15:30.000000Z'
                  links:
                    first: 'http://promo-next.test/api/v1/promotions/community-vote-2026/votes?page=1'
                    last: 'http://promo-next.test/api/v1/promotions/community-vote-2026/votes?page=1'
                    prev: null
                    next: null
                  meta:
                    current_page: 1
                    from: 1
                    last_page: 1
                    path: 'http://promo-next.test/api/v1/promotions/community-vote-2026/votes'
                    per_page: 15
                    to: 1
                    total: 1
                    question_labels:
                      district: 'District?'
                      community: 'Community?'
                properties:
                  data:
                    type: array
                    example:
                      -
                        id: 91
                        member_id: 45
                        member:
                          id: 45
                          phone: '18765580001'
                          name: 'E2E Voter'
                          email: eve@example.com
                          country: JM
                          status: active
                        question_key: community
                        answer: 'Harbour View'
                        vote_round: 1
                        created_at: '2026-05-01T10:15:30.000000Z'
                        updated_at: '2026-05-01T10:15:30.000000Z'
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          example: 91
                        member_id:
                          type: integer
                          example: 45
                        member:
                          type: object
                          properties:
                            id:
                              type: integer
                              example: 45
                            phone:
                              type: string
                              example: '18765580001'
                            name:
                              type: string
                              example: 'E2E Voter'
                            email:
                              type: string
                              example: eve@example.com
                            country:
                              type: string
                              example: JM
                            status:
                              type: string
                              example: active
                        question_key:
                          type: string
                          example: community
                        answer:
                          type: string
                          example: 'Harbour View'
                        vote_round:
                          type: integer
                          example: 1
                        created_at:
                          type: string
                          example: '2026-05-01T10:15:30.000000Z'
                        updated_at:
                          type: string
                          example: '2026-05-01T10:15:30.000000Z'
                  links:
                    type: object
                    properties:
                      first:
                        type: string
                        example: 'http://promo-next.test/api/v1/promotions/community-vote-2026/votes?page=1'
                      last:
                        type: string
                        example: 'http://promo-next.test/api/v1/promotions/community-vote-2026/votes?page=1'
                      prev:
                        type: string
                        example: null
                        nullable: true
                      next:
                        type: string
                        example: null
                        nullable: true
                  meta:
                    type: object
                    properties:
                      current_page:
                        type: integer
                        example: 1
                      from:
                        type: integer
                        example: 1
                      last_page:
                        type: integer
                        example: 1
                      path:
                        type: string
                        example: 'http://promo-next.test/api/v1/promotions/community-vote-2026/votes'
                      per_page:
                        type: integer
                        example: 15
                      to:
                        type: integer
                        example: 1
                      total:
                        type: integer
                        example: 1
                      question_labels:
                        type: object
                        properties:
                          district:
                            type: string
                            example: 'District?'
                          community:
                            type: string
                            example: 'Community?'
      tags:
        - 'Promotion Votes'
    parameters:
      -
        in: path
        name: promotion_slug
        description: 'The promotion slug.'
        example: community-vote-2026
        required: true
        schema:
          type: string
  '/api/v1/promotions/{promotion_slug}/votes/leaderboard':
    get:
      summary: 'Show the leaderboard'
      operationId: showTheLeaderboard
      description: "Return ranked tallies for the promotion's configured tally field."
      parameters:
        -
          in: query
          name: 'filter[district]'
          description: 'Filter the leaderboard by another configured question answer.'
          example: Belize
          required: false
          schema:
            type: string
            description: 'Filter the leaderboard by another configured question answer.'
            example: Belize
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  status: success
                  message: null
                  data:
                    question_key: community
                    question_label: 'Community?'
                    total_entries: 3
                    leaderboard:
                      -
                        rank: 1
                        name: 'Harbour View'
                        votes: 2
                      -
                        rank: 2
                        name: 'Bull Bay'
                        votes: 1
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                    example: null
                    nullable: true
                  data:
                    type: object
                    properties:
                      question_key:
                        type: string
                        example: community
                      question_label:
                        type: string
                        example: 'Community?'
                      total_entries:
                        type: integer
                        example: 3
                      leaderboard:
                        type: array
                        example:
                          -
                            rank: 1
                            name: 'Harbour View'
                            votes: 2
                          -
                            rank: 2
                            name: 'Bull Bay'
                            votes: 1
                        items:
                          type: object
                          properties:
                            rank:
                              type: integer
                              example: 1
                            name:
                              type: string
                              example: 'Harbour View'
                            votes:
                              type: integer
                              example: 2
      tags:
        - 'Promotion Votes'
    parameters:
      -
        in: path
        name: promotion_slug
        description: 'The promotion slug.'
        example: community-vote-2026
        required: true
        schema:
          type: string
  '/api/v1/promotions/{promotion_slug}/votes/stats':
    get:
      summary: 'Show voting statistics'
      operationId: showVotingStatistics
      description: 'Return summary counts for participants and responses in a voting promotion.'
      parameters: []
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  status: success
                  message: null
                  data:
                    total_participants: 2
                    completed_votes: 1
                    partial_votes: 1
                    total_responses: 3
                    questions_configured: 2
                    per_question:
                      -
                        question_key: district
                        question_label: 'District?'
                        respondents: 2
                      -
                        question_key: community
                        question_label: 'Community?'
                        respondents: 1
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                    example: null
                    nullable: true
                  data:
                    type: object
                    properties:
                      total_participants:
                        type: integer
                        example: 2
                      completed_votes:
                        type: integer
                        example: 1
                      partial_votes:
                        type: integer
                        example: 1
                      total_responses:
                        type: integer
                        example: 3
                      questions_configured:
                        type: integer
                        example: 2
                      per_question:
                        type: array
                        example:
                          -
                            question_key: district
                            question_label: 'District?'
                            respondents: 2
                          -
                            question_key: community
                            question_label: 'Community?'
                            respondents: 1
                        items:
                          type: object
                          properties:
                            question_key:
                              type: string
                              example: district
                            question_label:
                              type: string
                              example: 'District?'
                            respondents:
                              type: integer
                              example: 2
      tags:
        - 'Promotion Votes'
    parameters:
      -
        in: path
        name: promotion_slug
        description: 'The promotion slug.'
        example: community-vote-2026
        required: true
        schema:
          type: string
  '/api/v1/promotions/{promotion_slug}/votes/options/{question_key}':
    get:
      summary: 'Get question options'
      operationId: getQuestionOptions
      description: 'Return the available options for a question, optionally resolved from context or member history.'
      parameters:
        -
          in: query
          name: 'filter[district]'
          description: 'Provide explicit context using another question answer.'
          example: Belize
          required: false
          schema:
            type: string
            description: 'Provide explicit context using another question answer.'
            example: Belize
        -
          in: query
          name: 'filter[member_id]'
          description: 'Resolve context from a member ID or phone number.'
          example: '18765559001'
          required: false
          schema:
            type: string
            description: 'Resolve context from a member ID or phone number.'
            example: '18765559001'
        -
          in: query
          name: 'filter[member_phone]'
          description: 'Resolve context from a member phone number.'
          example: '18765559002'
          required: false
          schema:
            type: string
            description: 'Resolve context from a member phone number.'
            example: '18765559002'
        -
          in: query
          name: api_key
          description: 'Alternative API token for bot callers.'
          example: 1|abcdefghijklmnopqrstuvwx
          required: false
          schema:
            type: string
            description: 'Alternative API token for bot callers.'
            example: 1|abcdefghijklmnopqrstuvwx
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  status: success
                  message: null
                  data:
                    question_key: community
                    question_label: 'Community?'
                    allow_other: true
                    options:
                      - 'Belize City'
                      - 'Burrell Boom'
                      - Ladyville
                    total: 4
                    options_whatsapp: "1. Belize City\n2. Burrell Boom\n3. Ladyville\n4. Other"
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                    example: null
                    nullable: true
                  data:
                    type: object
                    properties:
                      question_key:
                        type: string
                        example: community
                      question_label:
                        type: string
                        example: 'Community?'
                      allow_other:
                        type: boolean
                        example: true
                      options:
                        type: array
                        example:
                          - 'Belize City'
                          - 'Burrell Boom'
                          - Ladyville
                        items:
                          type: string
                      total:
                        type: integer
                        example: 4
                      options_whatsapp:
                        type: string
                        example: "1. Belize City\n2. Burrell Boom\n3. Ladyville\n4. Other"
        404:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  status: error
                  message: "Question 'community' not found in config."
                  errors: null
                properties:
                  status:
                    type: string
                    example: error
                  message:
                    type: string
                    example: "Question 'community' not found in config."
                  errors:
                    type: string
                    example: null
                    nullable: true
      tags:
        - 'Promotion Votes'
    parameters:
      -
        in: path
        name: promotion_slug
        description: 'The promotion slug.'
        example: community-vote-2026
        required: true
        schema:
          type: string
      -
        in: path
        name: question_key
        description: 'The question key to resolve options for.'
        example: community
        required: true
        schema:
          type: string
  '/api/v1/promotions/{promotion_slug}/promoters':
    get:
      summary: 'List promoters'
      operationId: listPromoters
      description: 'Return a paginated list of promoters for the given promotion.'
      parameters:
        -
          in: query
          name: 'filter[name]'
          description: 'Filter promoters by name.'
          example: Jane
          required: false
          schema:
            type: string
            description: 'Filter promoters by name.'
            example: Jane
        -
          in: query
          name: 'filter[phone]'
          description: 'Filter promoters by phone number.'
          example: '18761234567'
          required: false
          schema:
            type: string
            description: 'Filter promoters by phone number.'
            example: '18761234567'
        -
          in: query
          name: 'filter[identifier]'
          description: 'Filter promoters by external identifier.'
          example: PROMO001
          required: false
          schema:
            type: string
            description: 'Filter promoters by external identifier.'
            example: PROMO001
        -
          in: query
          name: 'filter[status]'
          description: 'Filter promoters by status.'
          example: active
          required: false
          schema:
            type: string
            description: 'Filter promoters by status.'
            example: active
        -
          in: query
          name: sort
          description: 'Sort by name, created_at, or status.'
          example: '-created_at'
          required: false
          schema:
            type: string
            description: 'Sort by name, created_at, or status.'
            example: '-created_at'
        -
          in: query
          name: page
          description: 'The page number.'
          example: 1
          required: false
          schema:
            type: integer
            description: 'The page number.'
            example: 1
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  data:
                    -
                      id: 11
                      promotion_id: 5
                      identifier: PROMO001
                      name: 'Jane Promoter'
                      phone: '18761234567'
                      status: active
                      metadata:
                        region: 'Belize City'
                      created_at: '2026-05-01T10:15:30.000000Z'
                      updated_at: '2026-05-01T10:15:30.000000Z'
                  links:
                    first: 'http://promo-next.test/api/v1/promotions/retailer-drive-2026/promoters?page=1'
                    last: 'http://promo-next.test/api/v1/promotions/retailer-drive-2026/promoters?page=1'
                    prev: null
                    next: null
                  meta:
                    current_page: 1
                    from: 1
                    last_page: 1
                    path: 'http://promo-next.test/api/v1/promotions/retailer-drive-2026/promoters'
                    per_page: 15
                    to: 1
                    total: 1
                properties:
                  data:
                    type: array
                    example:
                      -
                        id: 11
                        promotion_id: 5
                        identifier: PROMO001
                        name: 'Jane Promoter'
                        phone: '18761234567'
                        status: active
                        metadata:
                          region: 'Belize City'
                        created_at: '2026-05-01T10:15:30.000000Z'
                        updated_at: '2026-05-01T10:15:30.000000Z'
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          example: 11
                        promotion_id:
                          type: integer
                          example: 5
                        identifier:
                          type: string
                          example: PROMO001
                        name:
                          type: string
                          example: 'Jane Promoter'
                        phone:
                          type: string
                          example: '18761234567'
                        status:
                          type: string
                          example: active
                        metadata:
                          type: object
                          properties:
                            region:
                              type: string
                              example: 'Belize City'
                        created_at:
                          type: string
                          example: '2026-05-01T10:15:30.000000Z'
                        updated_at:
                          type: string
                          example: '2026-05-01T10:15:30.000000Z'
                  links:
                    type: object
                    properties:
                      first:
                        type: string
                        example: 'http://promo-next.test/api/v1/promotions/retailer-drive-2026/promoters?page=1'
                      last:
                        type: string
                        example: 'http://promo-next.test/api/v1/promotions/retailer-drive-2026/promoters?page=1'
                      prev:
                        type: string
                        example: null
                        nullable: true
                      next:
                        type: string
                        example: null
                        nullable: true
                  meta:
                    type: object
                    properties:
                      current_page:
                        type: integer
                        example: 1
                      from:
                        type: integer
                        example: 1
                      last_page:
                        type: integer
                        example: 1
                      path:
                        type: string
                        example: 'http://promo-next.test/api/v1/promotions/retailer-drive-2026/promoters'
                      per_page:
                        type: integer
                        example: 15
                      to:
                        type: integer
                        example: 1
                      total:
                        type: integer
                        example: 1
      tags:
        - 'Promotion Promoters'
    post:
      summary: 'Create a promoter'
      operationId: createAPromoter
      description: 'Create a new promoter for the given promotion.'
      parameters: []
      responses:
        201:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  status: success
                  message: 'Promoter created.'
                  data:
                    id: 11
                    promotion_id: 5
                    identifier: PROMO001
                    name: 'API Promoter'
                    phone: '18761234567'
                    status: active
                    metadata: null
                    created_at: '2026-05-01T10:15:30.000000Z'
                    updated_at: '2026-05-01T10:15:30.000000Z'
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                    example: 'Promoter created.'
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        example: 11
                      promotion_id:
                        type: integer
                        example: 5
                      identifier:
                        type: string
                        example: PROMO001
                      name:
                        type: string
                        example: 'API Promoter'
                      phone:
                        type: string
                        example: '18761234567'
                      status:
                        type: string
                        example: active
                      metadata:
                        type: string
                        example: null
                        nullable: true
                      created_at:
                        type: string
                        example: '2026-05-01T10:15:30.000000Z'
                      updated_at:
                        type: string
                        example: '2026-05-01T10:15:30.000000Z'
      tags:
        - 'Promotion Promoters'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                identifier:
                  type: string
                  description: 'External promoter identifier.'
                  example: PROMO001
                  nullable: true
                name:
                  type: string
                  description: 'The promoter name.'
                  example: 'API Promoter'
                phone:
                  type: string
                  description: 'The promoter phone number.'
                  example: '18761234567'
                  nullable: true
                status:
                  type: string
                  description: 'The promoter status. Allowed values: active, inactive.'
                  example: active
                metadata:
                  type: object
                  description: 'Additional promoter metadata.'
                  example:
                    region: 'Belize City'
                  properties: {  }
                  nullable: true
              required:
                - name
    parameters:
      -
        in: path
        name: promotion_slug
        description: 'The promotion slug.'
        example: retailer-drive-2026
        required: true
        schema:
          type: string
  '/api/v1/promotions/{promotion_slug}/promoters/{promoter_id}':
    get:
      summary: 'Show a promoter'
      operationId: showAPromoter
      description: 'Return a single promoter record for the given promotion.'
      parameters: []
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  status: success
                  message: null
                  data:
                    id: 11
                    promotion_id: 5
                    identifier: PROMO001
                    name: 'Jane Promoter'
                    phone: '18761234567'
                    status: active
                    metadata:
                      region: 'Belize City'
                    created_at: '2026-05-01T10:15:30.000000Z'
                    updated_at: '2026-05-01T10:15:30.000000Z'
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                    example: null
                    nullable: true
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        example: 11
                      promotion_id:
                        type: integer
                        example: 5
                      identifier:
                        type: string
                        example: PROMO001
                      name:
                        type: string
                        example: 'Jane Promoter'
                      phone:
                        type: string
                        example: '18761234567'
                      status:
                        type: string
                        example: active
                      metadata:
                        type: object
                        properties:
                          region:
                            type: string
                            example: 'Belize City'
                      created_at:
                        type: string
                        example: '2026-05-01T10:15:30.000000Z'
                      updated_at:
                        type: string
                        example: '2026-05-01T10:15:30.000000Z'
      tags:
        - 'Promotion Promoters'
    put:
      summary: 'Update a promoter'
      operationId: updateAPromoter
      description: 'Update a promoter record for the given promotion.'
      parameters: []
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  status: success
                  message: 'Promoter updated.'
                  data:
                    id: 11
                    promotion_id: 5
                    identifier: PROMO002
                    name: 'Changed Name'
                    phone: '18761234567'
                    status: inactive
                    metadata:
                      region: 'Orange Walk'
                    created_at: '2026-05-01T10:15:30.000000Z'
                    updated_at: '2026-05-01T11:00:00.000000Z'
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                    example: 'Promoter updated.'
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        example: 11
                      promotion_id:
                        type: integer
                        example: 5
                      identifier:
                        type: string
                        example: PROMO002
                      name:
                        type: string
                        example: 'Changed Name'
                      phone:
                        type: string
                        example: '18761234567'
                      status:
                        type: string
                        example: inactive
                      metadata:
                        type: object
                        properties:
                          region:
                            type: string
                            example: 'Orange Walk'
                      created_at:
                        type: string
                        example: '2026-05-01T10:15:30.000000Z'
                      updated_at:
                        type: string
                        example: '2026-05-01T11:00:00.000000Z'
      tags:
        - 'Promotion Promoters'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                identifier:
                  type: string
                  description: 'External promoter identifier.'
                  example: PROMO002
                  nullable: true
                name:
                  type: string
                  description: 'The promoter name. Required when present.'
                  example: 'Changed Name'
                phone:
                  type: string
                  description: 'The promoter phone number.'
                  example: '18761234567'
                  nullable: true
                status:
                  type: string
                  description: 'The promoter status. Allowed values: active, inactive.'
                  example: inactive
                metadata:
                  type: object
                  description: 'Additional promoter metadata.'
                  example:
                    region: 'Orange Walk'
                  properties: {  }
                  nullable: true
    delete:
      summary: 'Delete a promoter'
      operationId: deleteAPromoter
      description: 'Permanently delete a promoter from the given promotion.'
      parameters: []
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  status: success
                  message: 'Promoter deleted.'
                  data: null
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                    example: 'Promoter deleted.'
                  data:
                    type: string
                    example: null
                    nullable: true
      tags:
        - 'Promotion Promoters'
    parameters:
      -
        in: path
        name: promotion_slug
        description: 'The promotion slug.'
        example: retailer-drive-2026
        required: true
        schema:
          type: string
      -
        in: path
        name: promoter_id
        description: 'The ID of the promoter.'
        example: 4
        required: true
        schema:
          type: integer
      -
        in: path
        name: promoter
        description: 'The promoter ID.'
        example: 11
        required: true
        schema:
          type: integer
  /api/v1/tools:
    get:
      summary: 'List available tools'
      operationId: listAvailableTools
      description: 'Return all registered API tools and their input/output schemas.'
      parameters: []
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  status: success
                  message: null
                  data:
                    -
                      slug: date-formatter
                      name: 'Date Formatter & Age Gate'
                      description: 'Formats a date string and checks if the person meets a minimum age requirement. Auto-detects the input date format, or accepts an explicit input_format for ambiguous dates.'
                      input_schema:
                        -
                          name: date
                          type: string
                          required: true
                          description: 'Date string (e.g., "28/11/1972", "1972-11-28", "Nov 28, 1972")'
                      output_schema:
                        -
                          name: formatted_date
                          type: string
                          description: 'Date in the requested output format'
                    -
                      slug: phone-country
                      name: 'Phone Country Lookup'
                      description: 'Parses a phone number and returns the normalized number, country calling code, country code, and country name.'
                      input_schema:
                        -
                          name: phone
                          type: string
                          required: true
                          description: 'Phone number in any format'
                      output_schema:
                        -
                          name: country_code
                          type: string
                          description: 'ISO 3166-1 alpha-2 country code'
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                    example: null
                    nullable: true
                  data:
                    type: array
                    example:
                      -
                        slug: date-formatter
                        name: 'Date Formatter & Age Gate'
                        description: 'Formats a date string and checks if the person meets a minimum age requirement. Auto-detects the input date format, or accepts an explicit input_format for ambiguous dates.'
                        input_schema:
                          -
                            name: date
                            type: string
                            required: true
                            description: 'Date string (e.g., "28/11/1972", "1972-11-28", "Nov 28, 1972")'
                        output_schema:
                          -
                            name: formatted_date
                            type: string
                            description: 'Date in the requested output format'
                      -
                        slug: phone-country
                        name: 'Phone Country Lookup'
                        description: 'Parses a phone number and returns the normalized number, country calling code, country code, and country name.'
                        input_schema:
                          -
                            name: phone
                            type: string
                            required: true
                            description: 'Phone number in any format'
                        output_schema:
                          -
                            name: country_code
                            type: string
                            description: 'ISO 3166-1 alpha-2 country code'
                    items:
                      type: object
                      properties:
                        slug:
                          type: string
                          example: date-formatter
                        name:
                          type: string
                          example: 'Date Formatter & Age Gate'
                        description:
                          type: string
                          example: 'Formats a date string and checks if the person meets a minimum age requirement. Auto-detects the input date format, or accepts an explicit input_format for ambiguous dates.'
                        input_schema:
                          type: array
                          example:
                            -
                              name: date
                              type: string
                              required: true
                              description: 'Date string (e.g., "28/11/1972", "1972-11-28", "Nov 28, 1972")'
                          items:
                            type: object
                            properties:
                              name:
                                type: string
                                example: date
                              type:
                                type: string
                                example: string
                              required:
                                type: boolean
                                example: true
                              description:
                                type: string
                                example: 'Date string (e.g., "28/11/1972", "1972-11-28", "Nov 28, 1972")'
                        output_schema:
                          type: array
                          example:
                            -
                              name: formatted_date
                              type: string
                              description: 'Date in the requested output format'
                          items:
                            type: object
                            properties:
                              name:
                                type: string
                                example: formatted_date
                              type:
                                type: string
                                example: string
                              description:
                                type: string
                                example: 'Date in the requested output format'
      tags:
        - Tools
  '/api/v1/tools/{tool}':
    post:
      summary: 'Execute a tool'
      operationId: executeATool
      description: 'Run the selected tool with the supplied input. Body fields depend on the tool slug.'
      parameters: []
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  status: success
                  message: null
                  data:
                    formatted_date: 28.11.1972
                    status: approved
                    age: 53
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                    example: null
                    nullable: true
                  data:
                    type: object
                    properties:
                      formatted_date:
                        type: string
                        example: 28.11.1972
                      status:
                        type: string
                        example: approved
                      age:
                        type: integer
                        example: 53
        404:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  status: error
                  message: 'Tool not found.'
                  errors: null
                properties:
                  status:
                    type: string
                    example: error
                  message:
                    type: string
                    example: 'Tool not found.'
                  errors:
                    type: string
                    example: null
                    nullable: true
      tags:
        - Tools
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                date:
                  type: string
                  description: 'Required when tool is `date-formatter`. The date to format.'
                  example: 28/11/1972
                input_format:
                  type: string
                  description: 'Optional input format for `date-formatter`.'
                  example: d/m/Y
                output_format:
                  type: string
                  description: 'Optional output format for `date-formatter`.'
                  example: Y-m-d
                min_age:
                  type: integer
                  description: 'Optional minimum age for `date-formatter`.'
                  example: 18
                phone:
                  type: string
                  description: 'Required when tool is `phone-country`. The phone number to parse.'
                  example: '5016283170'
                default_region:
                  type: string
                  description: 'Optional fallback region for `phone-country`.'
                  example: BZ
    parameters:
      -
        in: path
        name: tool
        description: 'The tool slug.'
        example: date-formatter
        required: true
        schema:
          type: string
  '/api/v1/webhooks/messenger-people/{team_uuid}/{promotion_slug}':
    post:
      summary: 'Handle a MessengerPeople webhook'
      operationId: handleAMessengerPeopleWebhook
      description: 'Accept an inbound bot payload and route it to receipt, code, sweepstakes, or voting processing.'
      parameters: []
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  status: success
                  message: 'Receipt received successfully.'
                  data:
                    type: receipt
                    receipt_id: 123
                    test_mode: false
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                    example: 'Receipt received successfully.'
                  data:
                    type: object
                    properties:
                      type:
                        type: string
                        example: receipt
                      receipt_id:
                        type: integer
                        example: 123
                      test_mode:
                        type: boolean
                        example: false
        403:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  status: error
                  message: 'This account has been temporarily suspended.'
                  errors: null
                properties:
                  status:
                    type: string
                    example: error
                  message:
                    type: string
                    example: 'This account has been temporarily suspended.'
                  errors:
                    type: string
                    example: null
                    nullable: true
      tags:
        - Webhooks
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                member_id:
                  type: string
                  description: 'The sender identifier, usually a phone number.'
                  example: '18765551234'
                name:
                  type: string
                  description: 'The sender display name.'
                  example: 'John Doe'
                image:
                  type: string
                  description: 'Receipt image URL for receipt promotions.'
                  example: 'https://example.com/receipt.jpg'
                chat:
                  type: string
                  description: 'Text message content for code and sweepstakes promotions.'
                  example: WINCODE123
                chat_time:
                  type: integer
                  description: 'Unix timestamp when the message was sent.'
                  example: 1772426400
                promoter:
                  type: string
                  description: 'Promoter identifier to attach to a receipt entry.'
                  example: PROMO001
              required:
                - member_id
      security: []
    parameters:
      -
        in: path
        name: team_uuid
        description: 'The team UUID.'
        example: 550e8400-e29b-41d4-a716-446655440000
        required: true
        schema:
          type: string
      -
        in: path
        name: promotion_slug
        description: 'The promotion slug.'
        example: summer-cashback
        required: true
        schema:
          type: string
  '/api/v1/webhooks/sendseven/{team_uuid}/{promotion_slug}':
    post:
      summary: 'Handle a SendSeven webhook'
      operationId: handleASendSevenWebhook
      description: 'Accept inbound message, delivery status, conversation, and contact events from SendSeven.'
      parameters: []
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  status: success
                  message: 'Receipt received successfully.'
                  data:
                    type: receipt
                    receipt_id: 123
                    test_mode: false
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                    example: 'Receipt received successfully.'
                  data:
                    type: object
                    properties:
                      type:
                        type: string
                        example: receipt
                      receipt_id:
                        type: integer
                        example: 123
                      test_mode:
                        type: boolean
                        example: false
        401:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  status: error
                  message: 'Invalid webhook signature.'
                  errors: null
                properties:
                  status:
                    type: string
                    example: error
                  message:
                    type: string
                    example: 'Invalid webhook signature.'
                  errors:
                    type: string
                    example: null
                    nullable: true
      tags:
        - Webhooks
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                id:
                  type: string
                  description: 'A unique webhook event ID.'
                  example: evt_123456789
                event_id:
                  type: string
                  description: 'Alternative event identifier.'
                  example: evt_123456789
                type:
                  type: string
                  description: 'The SendSeven event type.'
                  example: message.received
                created_at:
                  type: string
                  description: 'The event timestamp in ISO 8601 format.'
                  example: '2026-05-01T10:15:30Z'
                data:
                  type: object
                  description: ''
                  example:
                    message:
                      from_id: '18765551234'
                  properties:
                    message:
                      type: object
                      description: ''
                      example:
                        from_id: '18765551234'
                      properties:
                        from_id:
                          type: string
                          description: 'The sender phone number for inbound messages.'
                          example: '18765551234'
                        message_type:
                          type: string
                          description: 'The incoming message type.'
                          example: image
                        text:
                          type: string
                          description: 'The incoming text content.'
                          example: WINCODE123
                        attachments:
                          type: array
                          description: 'Attachments for image messages.'
                          example:
                            -
                              type: image
                              signed_url: 'https://cdn.sendseven.com/media/receipt.jpg'
                          items:
                            type: string
                    contact:
                      type: object
                      description: ''
                      example:
                        name: 'John Doe'
                      properties:
                        name:
                          type: string
                          description: 'The contact display name.'
                          example: 'John Doe'
                        phone:
                          type: string
                          description: 'The contact phone number.'
                          example: '18765551234'
              required:
                - type
      security: []
    parameters:
      -
        in: path
        name: team_uuid
        description: 'The team UUID.'
        example: 550e8400-e29b-41d4-a716-446655440000
        required: true
        schema:
          type: string
      -
        in: path
        name: promotion_slug
        description: 'The promotion slug.'
        example: summer-cashback
        required: true
        schema:
          type: string
