openapi: 3.0.3
info:
  title: Nautix Third-Party API
  version: 1.0.0
  description: |
    Swagger (OpenAPI) specification for third-party partner APIs under `/api/thirdparty/v1`.

    All endpoints require an API key via the `API-KEY` header.
    Scope requirements are endpoint-specific.
servers:
  - url: /
    description: Relative to the current host

tags:
  - name: Partner
    description: Partner onboarding and status endpoints.
  - name: Templates
    description: Third-party template retrieval endpoints.
  - name: Messages
    description: Third-party outbound messaging endpoints.
  - name: Webhooks
    description: Webhook endpoint management and delivery audit endpoints.

paths:
  /api/thirdparty/v1/partner/onboard:
    post:
      tags:
        - Partner
      summary: Onboard a partner user
      description: |
        Creates a new Nautix user and workspace (tenant), provisions required resources,
        and returns an API key for the partner to use on behalf of the tenant.

        If the user already exists for the partner, the response includes current onboarding
        status without issuing a new API key.
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PartnerOnboardRequest'
            examples:
              basic:
                summary: Basic onboard request
                value:
                  externalUserId: "pcash-usr-123"
                  email: "user@example.com"
                  fullName: "Jane Doe"
                  phoneNumber: "+254700000000"
                  companyName: "Acme Traders"
                  businessEmail: "billing@acme.com"
                  timezone: "Africa/Nairobi"
                  country: "KE"
      responses:
        "200":
          description: User onboarded or existing user status returned.
          headers:
            X-RateLimit-Limit-Minute:
              schema:
                type: integer
              description: Allowed requests per minute.
            X-RateLimit-Remaining-Minute:
              schema:
                type: integer
              description: Remaining requests for the current minute window.
            X-RateLimit-Limit-Hour:
              schema:
                type: integer
              description: Allowed requests per hour.
            X-RateLimit-Remaining-Hour:
              schema:
                type: integer
              description: Remaining requests for the current hour window.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponsePartnerOnboardResponse'
              examples:
                created:
                  summary: New user + workspace created
                  value:
                    success: true
                    message: "User and workspace created successfully. Save the API key securely - it won't be shown again!"
                    data:
                      userId: 1234
                      externalUserId: "pcash-usr-123"
                      tenantId: "acme_1a2b3c4d"
                      workspaceName: "Acme Traders"
                      apiKey: "nautix_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
                      maskedApiKey: "nautix_live_****************************"
                      apiKeyExpiresAt: "2026-02-07T12:00:00Z"
                      onboardingStage: "WHATSAPP_ACCOUNT"
                      completionPercentage: 20
                      embeddedSignupUrl: "https://app.nautix.io/onboarding/whatsapp?tenant=acme_1a2b3c4d&token=jwt-token"
                      accessToken: "jwt-token"
                      nextStep: "Complete the WhatsApp Business embedded sign-up to connect your phone number."
                      createdAt: "2026-02-07T12:00:00Z"
                    timestamp: "2026-02-07T12:00:00Z"
                    timezone: "UTC"
                existing:
                  summary: Existing user status
                  value:
                    success: true
                    message: "User already exists with an active workspace."
                    data:
                      userId: 1234
                      externalUserId: "pcash-usr-123"
                      tenantId: "acme_1a2b3c4d"
                      workspaceName: "Acme Traders"
                      onboardingStage: "WHATSAPP_ACCOUNT"
                      completionPercentage: 20
                      nextStep: "Complete the WhatsApp Business embedded sign-up flow."
                      createdAt: "2026-02-07T12:00:00Z"
                    timestamp: "2026-02-07T12:00:00Z"
                    timezone: "UTC"
        "400":
          description: Validation or business rule error.
          headers:
            X-RateLimit-Limit-Minute:
              schema:
                type: integer
            X-RateLimit-Remaining-Minute:
              schema:
                type: integer
            X-RateLimit-Limit-Hour:
              schema:
                type: integer
            X-RateLimit-Remaining-Hour:
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseError'
              examples:
                validation:
                  summary: Missing required field
                  value:
                    success: false
                    message: "External user ID is required."
                    timestamp: "2026-02-07T12:00:00Z"
        "401":
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyErrorResponse'
              examples:
                missing:
                  summary: Missing API key
                  value:
                    message: "API-KEY header is required"
                    statusCode: 401
                invalid:
                  summary: Invalid API key
                  value:
                    message: "Invalid API Key"
                    statusCode: 401
        "429":
          description: API key rate limit exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyErrorResponse'
              examples:
                rate_limit:
                  value:
                    message: "Rate limit exceeded. Please try again later."
                    statusCode: 429
        "500":
          description: Unexpected server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseError'
              examples:
                server_error:
                  value:
                    success: false
                    message: "An unexpected error occurred. Please contact support if the problem persists."
                    timestamp: "2026-02-07T12:00:00Z"

  /api/thirdparty/v1/partner/onboard/status/{externalUserId}:
    get:
      tags:
        - Partner
      summary: Get onboarding status
      description: Returns the current onboarding stage and next steps for an existing partner user.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: externalUserId
          in: path
          required: true
          description: The user's unique identifier in the partner system.
          schema:
            type: string
      responses:
        "200":
          description: Onboarding status retrieved.
          headers:
            X-RateLimit-Limit-Minute:
              schema:
                type: integer
              description: Allowed requests per minute.
            X-RateLimit-Remaining-Minute:
              schema:
                type: integer
              description: Remaining requests for the current minute window.
            X-RateLimit-Limit-Hour:
              schema:
                type: integer
              description: Allowed requests per hour.
            X-RateLimit-Remaining-Hour:
              schema:
                type: integer
              description: Remaining requests for the current hour window.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponsePartnerOnboardResponse'
              examples:
                status:
                  value:
                    success: true
                    message: "Onboarding status retrieved successfully."
                    data:
                      userId: 1234
                      externalUserId: "pcash-usr-123"
                      tenantId: "acme_1a2b3c4d"
                      workspaceName: "Acme Traders"
                      onboardingStage: "PHONE_NUMBER_VERIFICATION"
                      completionPercentage: 60
                      nextStep: "Verify your WhatsApp Business phone number."
                      createdAt: "2026-02-07T12:00:00Z"
                    timestamp: "2026-02-07T12:00:00Z"
                    timezone: "UTC"
        "400":
          description: Validation or business rule error.
          headers:
            X-RateLimit-Limit-Minute:
              schema:
                type: integer
            X-RateLimit-Remaining-Minute:
              schema:
                type: integer
            X-RateLimit-Limit-Hour:
              schema:
                type: integer
            X-RateLimit-Remaining-Hour:
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseError'
              examples:
                not_found:
                  summary: User not found
                  value:
                    success: false
                    message: "User not found."
                    timestamp: "2026-02-07T12:00:00Z"
        "401":
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyErrorResponse'
        "429":
          description: API key rate limit exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyErrorResponse'
        "500":
          description: Unexpected server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseError'

  /api/thirdparty/v1/templates:
    get:
      tags:
        - Templates
      summary: List templates
      description: |
        Returns WhatsApp templates for the tenant associated with the
        authenticated API key.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: phoneNumberId
          in: query
          required: false
          description: |
            Optional WhatsApp Business phone number id. When supplied, templates are
            listed using the WABA credentials associated with this phone number.
          schema:
            type: string
        - name: status
          in: query
          required: false
          description: Filter by template approval status.
          schema:
            type: string
        - name: category
          in: query
          required: false
          description: Filter by template category.
          schema:
            type: string
        - name: name
          in: query
          required: false
          description: Filter by exact template name.
          schema:
            type: string
        - name: language
          in: query
          required: false
          description: Filter by language code (for example `en_US`).
          schema:
            type: string
        - name: limit
          in: query
          required: false
          description: Maximum number of records to return.
          schema:
            type: integer
            minimum: 1
            maximum: 250
        - name: before
          in: query
          required: false
          description: Pagination cursor for fetching previous page.
          schema:
            type: string
        - name: after
          in: query
          required: false
          description: Pagination cursor for fetching next page.
          schema:
            type: string
      responses:
        "200":
          description: Template list retrieved.
          headers:
            X-RateLimit-Limit-Minute:
              schema:
                type: integer
            X-RateLimit-Remaining-Minute:
              schema:
                type: integer
            X-RateLimit-Limit-Hour:
              schema:
                type: integer
            X-RateLimit-Remaining-Hour:
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseTemplateListResponse'
              examples:
                templates:
                  value:
                    success: true
                    data:
                      data:
                        - metaTemplateId: "637472920366533"
                          name: "prosper_mentor_session_request"
                          language: "en_US"
                          category: "UTILITY"
                          status: "APPROVED"
                        - metaTemplateId: "637472920366534"
                          name: "pcash_verification_v3"
                          language: "en_US"
                          category: "AUTHENTICATION"
                          status: "APPROVED"
                      paging:
                        before: "QVFIU..."
                        after: "QVFIU..."
                    timestamp: "2026-02-07T12:00:00Z"
        "400":
          description: Validation or business rule error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseError'
        "401":
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyErrorResponse'
        "429":
          description: API key rate limit exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyErrorResponse'
        "500":
          description: Unexpected server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseError'

  /api/thirdparty/v1/templates/{templateId}:
    get:
      tags:
        - Templates
      summary: Get template by id
      description: |
        Retrieves a WhatsApp template by Meta template ID for the tenant
        associated with the authenticated API key.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: templateId
          in: path
          required: true
          description: Meta template ID.
          schema:
            type: string
      responses:
        "200":
          description: Template retrieved.
          headers:
            X-RateLimit-Limit-Minute:
              schema:
                type: integer
            X-RateLimit-Remaining-Minute:
              schema:
                type: integer
            X-RateLimit-Limit-Hour:
              schema:
                type: integer
            X-RateLimit-Remaining-Hour:
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseTemplateRecord'
        "400":
          description: Validation or business rule error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseError'
        "401":
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyErrorResponse'
        "404":
          description: Template not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseError'
        "429":
          description: API key rate limit exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyErrorResponse'
        "500":
          description: Unexpected server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseError'

  /api/thirdparty/v1/templates/by-name:
    get:
      tags:
        - Templates
      summary: Get template by name
      description: |
        Retrieves a WhatsApp template by name (and optional language) for
        the tenant associated with the authenticated API key.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: name
          in: query
          required: true
          description: Template name.
          schema:
            type: string
        - name: language
          in: query
          required: false
          description: Template language code.
          schema:
            type: string
      responses:
        "200":
          description: Template retrieved.
          headers:
            X-RateLimit-Limit-Minute:
              schema:
                type: integer
            X-RateLimit-Remaining-Minute:
              schema:
                type: integer
            X-RateLimit-Limit-Hour:
              schema:
                type: integer
            X-RateLimit-Remaining-Hour:
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseTemplateRecord'
        "400":
          description: Validation or business rule error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseError'
        "401":
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyErrorResponse'
        "404":
          description: Template not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseError'
        "429":
          description: API key rate limit exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyErrorResponse'
        "500":
          description: Unexpected server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseError'

  /api/thirdparty/v1/messages/template/send:
    post:
      tags:
        - Messages
      summary: Send template message
      description: |
        Sends a business-initiated WhatsApp template message for the tenant
        associated with the authenticated API key.
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ThirdPartyTemplateSendRequest'
            examples:
              session_request:
                summary: Mentorship session request
                value:
                  recipient: "+254700000000"
                  templateName: "prosper_mentor_session_request"
                  language: "en_US"
                  bodyParameters:
                    - "David"
                    - "Grace Njeri"
                    - "https://linkedin.com/in/gracenjeri"
                    - "Career Growth"
                    - "Friday, February 20, 2026"
                    - "I need help preparing for leadership interviews."
                    - "https://app.prospermentor.com/app/sessions/review/uuid"
                  responderDisplayName: "ProsperMentor"
              authentication_copy_code:
                summary: Authentication template with copy code
                value:
                  recipient: "+254700000111"
                  templateName: "pcash_verification_v3"
                  language: "en_US"
                  authCode: "583921"
                  responderDisplayName: "Nautix API"
      responses:
        "202":
          description: Message accepted for delivery
          headers:
            X-RateLimit-Limit-Minute:
              schema:
                type: integer
            X-RateLimit-Remaining-Minute:
              schema:
                type: integer
            X-RateLimit-Limit-Hour:
              schema:
                type: integer
            X-RateLimit-Remaining-Hour:
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseSendMessageResponse'
        "400":
          description: Validation or business rule error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseError'
        "401":
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyErrorResponse'
        "429":
          description: API key rate limit exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyErrorResponse'
        "500":
          description: Unexpected server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseError'

  /api/thirdparty/v1/webhooks/endpoints:
    get:
      tags:
        - Webhooks
      summary: List webhook endpoints
      security:
        - ApiKeyAuth: []
      responses:
        "200":
          description: Webhook endpoints returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseWebhookEndpointList'
        "401":
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyErrorResponse'
    post:
      tags:
        - Webhooks
      summary: Create a webhook endpoint
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEndpointCreateRequest'
      responses:
        "201":
          description: Webhook endpoint created. The raw secret is returned once.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseWebhookEndpointResponse'
        "400":
          description: Validation or business rule error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseError'
        "401":
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyErrorResponse'

  /api/thirdparty/v1/webhooks/endpoints/{id}:
    get:
      tags:
        - Webhooks
      summary: Get a webhook endpoint
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/WebhookEndpointId'
      responses:
        "200":
          description: Webhook endpoint returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseWebhookEndpointResponse'
    patch:
      tags:
        - Webhooks
      summary: Update a webhook endpoint
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/WebhookEndpointId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEndpointUpdateRequest'
      responses:
        "200":
          description: Webhook endpoint updated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseWebhookEndpointResponse'
        "400":
          description: Validation or business rule error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseError'
    delete:
      tags:
        - Webhooks
      summary: Disable a webhook endpoint
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/WebhookEndpointId'
      responses:
        "200":
          description: Webhook endpoint disabled.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseSuccessMessage'

  /api/thirdparty/v1/webhooks/endpoints/{id}/rotate-secret:
    post:
      tags:
        - Webhooks
      summary: Rotate a webhook endpoint secret
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/WebhookEndpointId'
      responses:
        "200":
          description: Secret rotated. The raw secret is returned once.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseWebhookEndpointResponse'

  /api/thirdparty/v1/webhooks/endpoints/{id}/test:
    post:
      tags:
        - Webhooks
      summary: Send a test webhook delivery
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/WebhookEndpointId'
      responses:
        "202":
          description: Test delivery queued.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseWebhookDeliveryResponse'

  /api/thirdparty/v1/webhooks/deliveries:
    get:
      tags:
        - Webhooks
      summary: List webhook deliveries
      security:
        - ApiKeyAuth: []
      parameters:
        - name: endpointId
          in: query
          required: false
          schema:
            type: integer
            format: int64
        - name: page
          in: query
          required: false
          schema:
            type: integer
            default: 0
            minimum: 0
        - name: size
          in: query
          required: false
          schema:
            type: integer
            default: 50
            minimum: 1
            maximum: 100
      responses:
        "200":
          description: Webhook deliveries returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseWebhookDeliveryList'

  /api/thirdparty/v1/webhooks/deliveries/{id}:
    get:
      tags:
        - Webhooks
      summary: Get a webhook delivery
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/WebhookDeliveryId'
      responses:
        "200":
          description: Webhook delivery returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseWebhookDeliveryResponse'

  /api/thirdparty/v1/webhooks/deliveries/{id}/replay:
    post:
      tags:
        - Webhooks
      summary: Replay a webhook delivery
      security:
        - ApiKeyAuth: []
      parameters:
        - $ref: '#/components/parameters/WebhookDeliveryId'
      responses:
        "202":
          description: Replay delivery queued.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseWebhookDeliveryResponse'

  /api/thirdparty/v1/webhooks/event-types:
    get:
      tags:
        - Webhooks
      summary: List supported webhook event types
      security:
        - ApiKeyAuth: []
      responses:
        "200":
          description: Supported event types returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponseWebhookEventTypeList'

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: API-KEY
      description: |
        API key provided by Nautix. Required for all third-party endpoints.
        Scope requirements vary by endpoint.

  parameters:
    WebhookEndpointId:
      name: id
      in: path
      required: true
      schema:
        type: integer
        format: int64
      description: Webhook endpoint ID.
    WebhookDeliveryId:
      name: id
      in: path
      required: true
      schema:
        type: integer
        format: int64
      description: Webhook delivery ID.

  schemas:
    PartnerOnboardRequest:
      type: object
      required:
        - externalUserId
        - email
        - fullName
        - companyName
      properties:
        externalUserId:
          type: string
          description: The user's unique identifier in the partner system.
        email:
          type: string
          format: email
          description: User's verified email address.
        fullName:
          type: string
          minLength: 2
          maxLength: 255
          description: User's full name.
        phoneNumber:
          type: string
          description: Optional phone number. Will be normalized if provided.
        companyName:
          type: string
          minLength: 2
          maxLength: 255
          description: Company or business name for the workspace.
        businessEmail:
          type: string
          format: email
          description: Business email address for the workspace (defaults to user email if omitted).
        timezone:
          type: string
          description: IANA timezone ID (e.g., "Africa/Nairobi", "UTC").
        country:
          type: string
          description: ISO 3166-1 alpha-2 country code (e.g., "KE", "US").
        password:
          type: string
          format: password
          description: Optional pre-generated password. If omitted, a secure random password is generated.

    PartnerOnboardResponse:
      type: object
      properties:
        userId:
          type: integer
          format: int64
          description: Nautix user ID.
        externalUserId:
          type: string
          description: External user ID from the partner system.
        tenantId:
          type: string
          description: Unique tenant/workspace identifier.
        workspaceName:
          type: string
          description: Workspace name.
        apiKey:
          type: string
          description: |
            API key for the partner to use for this tenant. Returned only once on initial creation.
        maskedApiKey:
          type: string
          description: Masked API key for display purposes.
        apiKeyExpiresAt:
          type: string
          format: date-time
          description: API key expiration timestamp (UTC).
        onboardingStage:
          $ref: '#/components/schemas/OnboardingStage'
        completionPercentage:
          type: integer
          minimum: 0
          maximum: 100
        embeddedSignupUrl:
          type: string
          format: uri
          description: URL for completing WhatsApp embedded signup.
        accessToken:
          type: string
          description: JWT access token for optional direct access to Nautix.
        nextStep:
          type: string
          description: Human-readable next step in the onboarding journey.
        createdAt:
          type: string
          format: date-time
          description: User creation timestamp (UTC).

    OnboardingStage:
      type: string
      description: High-level onboarding milestone.
      enum:
        - REGISTRATION
        - COMPANY_PROFILE
        - WHATSAPP_ACCOUNT
        - PHONE_NUMBER_VERIFICATION
        - META_APPROVAL
        - CATALOG_SETUP
        - COMPLETED

    ThirdPartyTemplateSendRequest:
      type: object
      required:
        - recipient
        - templateName
      properties:
        recipient:
          type: string
          description: Destination phone number in E.164 format.
        templateName:
          type: string
          description: Approved WhatsApp template name.
        language:
          type: string
          default: en_US
          description: Template language code.
        bodyParameters:
          type: array
          description: Ordered BODY placeholders mapped to {{1}}, {{2}}, ...
          items:
            type: string
        authCode:
          type: string
          maxLength: 15
          description: |
            Optional OTP/copy-code value for AUTHENTICATION templates.
            When provided, the API sends AUTHENTICATION components in OTP format:
            BODY with one `text` parameter (the code), plus BUTTON(index=0,
            sub_type=`url`) with one `text` parameter (the same code).
            This takes precedence over `bodyParameters`.
        fromPhoneNumberId:
          type: string
          description: Optional sender phone number id.
        responderDisplayName:
          type: string
          description: Optional responder display label.

    TemplateRecord:
      type: object
      properties:
        id:
          type: integer
          format: int64
        metaTemplateId:
          type: string
        businessPhoneNumberId:
          type: string
        name:
          type: string
        language:
          type: string
        category:
          type: string
        namespace:
          type: string
        status:
          type: string
        qualityScore:
          type: object
          nullable: true
          additionalProperties: true
        rejectedReason:
          type: string
        components:
          type: object
          nullable: true
          additionalProperties: true
        tags:
          type: array
          items:
            type: string
        lastSyncedAt:
          type: string
          format: date-time
        lastSubmittedAt:
          type: string
          format: date-time
        deletedAt:
          type: string
          format: date-time
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    TemplateListPaging:
      type: object
      properties:
        before:
          type: string
        after:
          type: string

    TemplateListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/TemplateRecord'
        paging:
          $ref: '#/components/schemas/TemplateListPaging'

    ApiResponseTemplateRecord:
      type: object
      properties:
        success:
          type: boolean
        message:
          type: string
        data:
          $ref: '#/components/schemas/TemplateRecord'
        warnings:
          type: array
          items:
            type: string
        timestamp:
          type: string
          format: date-time
        timezone:
          type: string
      required:
        - success
        - timestamp

    ApiResponseTemplateListResponse:
      type: object
      properties:
        success:
          type: boolean
        message:
          type: string
        data:
          $ref: '#/components/schemas/TemplateListResponse'
        warnings:
          type: array
          items:
            type: string
        timestamp:
          type: string
          format: date-time
        timezone:
          type: string
      required:
        - success
        - timestamp

    SendMessageResponse:
      type: object
      properties:
        messageId:
          type: integer
          format: int64
        whatsappMessageId:
          type: string
        status:
          type: string
        type:
          type: string
        createdAt:
          type: string
          format: date-time

    ApiResponseSendMessageResponse:
      type: object
      properties:
        success:
          type: boolean
        message:
          type: string
        data:
          $ref: '#/components/schemas/SendMessageResponse'
        warnings:
          type: array
          items:
            type: string
        timestamp:
          type: string
          format: date-time
        timezone:
          type: string
      required:
        - success
        - timestamp

    WebhookEndpointCreateRequest:
      type: object
      required:
        - url
        - eventTypes
      properties:
        url:
          type: string
          maxLength: 2048
          example: "https://partner.example.com/hooks/nautix"
        description:
          type: string
          maxLength: 500
          example: "CRM webhook endpoint"
        eventTypes:
          type: array
          minItems: 1
          items:
            type: string
          example:
            - message.created
            - conversation.status_changed
        enabled:
          type: boolean
          default: true

    WebhookEndpointUpdateRequest:
      type: object
      properties:
        url:
          type: string
          maxLength: 2048
        description:
          type: string
          maxLength: 500
        eventTypes:
          type: array
          items:
            type: string
        enabled:
          type: boolean

    WebhookEndpointResponse:
      type: object
      properties:
        id:
          type: integer
          format: int64
        url:
          type: string
        description:
          type: string
        eventTypes:
          type: array
          items:
            type: string
        enabled:
          type: boolean
        secret:
          type: string
          description: Returned only when creating or rotating an endpoint secret.
        maskedSecret:
          type: string
        secretCreatedAt:
          type: string
          format: date-time
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    WebhookDeliveryResponse:
      type: object
      properties:
        id:
          type: integer
          format: int64
        eventId:
          type: string
        eventType:
          type: string
        endpointId:
          type: integer
          format: int64
        endpointUrl:
          type: string
        status:
          type: string
          enum:
            - PENDING
            - ENQUEUED
            - PROCESSING
            - SUCCEEDED
            - RETRY_SCHEDULED
            - FAILED
        attemptCount:
          type: integer
        nextAttemptAt:
          type: string
          format: date-time
        lastAttemptAt:
          type: string
          format: date-time
        lastHttpStatus:
          type: integer
        lastError:
          type: string
        lastResponseBodyPreview:
          type: string
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    WebhookEventTypeResponse:
      type: object
      properties:
        value:
          type: string
          example: message.created
        description:
          type: string
          example: Message created

    ApiResponseWebhookEndpointResponse:
      type: object
      properties:
        success:
          type: boolean
        message:
          type: string
        data:
          $ref: '#/components/schemas/WebhookEndpointResponse'
        timestamp:
          type: string
          format: date-time
        timezone:
          type: string
      required:
        - success
        - timestamp

    ApiResponseWebhookEndpointList:
      type: object
      properties:
        success:
          type: boolean
        data:
          type: array
          items:
            $ref: '#/components/schemas/WebhookEndpointResponse'
        timestamp:
          type: string
          format: date-time
        timezone:
          type: string
      required:
        - success
        - timestamp

    ApiResponseWebhookDeliveryResponse:
      type: object
      properties:
        success:
          type: boolean
        message:
          type: string
        data:
          $ref: '#/components/schemas/WebhookDeliveryResponse'
        timestamp:
          type: string
          format: date-time
        timezone:
          type: string
      required:
        - success
        - timestamp

    ApiResponseWebhookDeliveryList:
      type: object
      properties:
        success:
          type: boolean
        data:
          type: array
          items:
            $ref: '#/components/schemas/WebhookDeliveryResponse'
        timestamp:
          type: string
          format: date-time
        timezone:
          type: string
      required:
        - success
        - timestamp

    ApiResponseWebhookEventTypeList:
      type: object
      properties:
        success:
          type: boolean
        data:
          type: array
          items:
            $ref: '#/components/schemas/WebhookEventTypeResponse'
        timestamp:
          type: string
          format: date-time
        timezone:
          type: string
      required:
        - success
        - timestamp

    ApiResponseSuccessMessage:
      type: object
      properties:
        success:
          type: boolean
          example: true
        message:
          type: string
        timestamp:
          type: string
          format: date-time
        timezone:
          type: string
      required:
        - success
        - timestamp

    ApiResponsePartnerOnboardResponse:
      type: object
      properties:
        success:
          type: boolean
        message:
          type: string
        data:
          $ref: '#/components/schemas/PartnerOnboardResponse'
        warnings:
          type: array
          items:
            type: string
        timestamp:
          type: string
          format: date-time
        timezone:
          type: string
          description: IANA timezone ID (e.g., "Africa/Nairobi").
      required:
        - success
        - timestamp

    ApiResponseError:
      type: object
      properties:
        success:
          type: boolean
          example: false
        message:
          type: string
        data:
          nullable: true
        warnings:
          type: array
          items:
            type: string
        timestamp:
          type: string
          format: date-time
        timezone:
          type: string
      required:
        - success
        - timestamp

    ApiKeyErrorResponse:
      type: object
      properties:
        message:
          type: string
        statusCode:
          type: integer
          format: int32
      required:
        - message
        - statusCode
