openapi: 3.1.0
info:
  title: Red Cloud CORE API
  version: 0.2.0
  summary: Integration-ready REST reference for the Red Cloud public CORE API.
  description: >
    Contract-accurate OpenAPI spec derived from the current implementation.


    Important realities reflected exactly:


    - OAuth uses `client_credentials`.

    - The token endpoint returns OAuth-style error bodies, not the CORE error
    wrapper.

    - `sendFrom` is a Red Cloud number identifier, not an E.164 phone number.

    - `POST /api/core/messages/send` requires `Idempotency-Key`.

    - `campaignregId` preserves the implemented request-field spelling.


    Use this specification alongside the authored guides for:


    - first-send sequencing

    - authentication lifecycle behavior

    - queue and idempotency behavior

    - webhook lifecycle handling
servers:
  - url: https://{host}
    description: Environment-specific API host
    variables:
      host:
        default: api.example.com
security:
  - BearerAuth: []
tags:
  - name: Authentication
    description: OAuth token issuance for Red Cloud CORE API access.
  - name: CORE
    description: >-
      Authenticated CORE API routes for identity, numbers, and outbound
      messaging.
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
  schemas:
    OAuthError:
      type: object
      required:
        - error
        - error_description
      properties:
        error:
          type: string
          enum:
            - invalid_request
            - invalid_client
            - unsupported_grant_type
            - server_error
        error_description:
          type: string
    CoreError:
      type: object
      required:
        - code
        - message
        - details
      properties:
        code:
          type: string
          example: INVALID_REQUEST
        message:
          type: string
          example: Idempotency-Key header is required
        details:
          type: object
          additionalProperties: true
    CoreErrorResponse:
      type: object
      required:
        - error
        - requestId
      properties:
        error:
          $ref: '#/components/schemas/CoreError'
        requestId:
          type: string
          example: req_send_001
    ApiClientIdentity:
      type: object
      required:
        - apiClientId
        - apiClientName
        - apiClientStatus
      properties:
        apiClientId:
          type: string
          example: rc_test_client_123
        apiClientName:
          type: string
          example: Red Cloud Test Client
        apiClientStatus:
          type: string
          example: ACTIVE
    OAuthTokenRequest:
      type: object
      required:
        - grant_type
        - client_id
        - client_secret
      description: >
        Request body for the OAuth `client_credentials` token exchange.


        The current implementation accepts form-encoded fields and also supports
        HTTP Basic client authentication.
      properties:
        grant_type:
          type: string
          enum:
            - client_credentials
        client_id:
          type: string
          example: rc_test_client_123
        client_secret:
          type: string
          example: rc_secret_abc123
    OAuthTokenResponse:
      type: object
      required:
        - access_token
        - token_type
        - expires_in
      description: Bearer token response returned by the OAuth token endpoint.
      properties:
        access_token:
          type: string
          example: rc_token_example
        token_type:
          type: string
          enum:
            - bearer
        expires_in:
          type: integer
          example: 3600
    AssignedNumbersSummary:
      type: object
      required:
        - total
        - smsEnabledCount
        - mmsEnabledCount
      properties:
        total:
          type: integer
          example: 1
        smsEnabledCount:
          type: integer
          example: 1
        mmsEnabledCount:
          type: integer
          example: 1
    Number:
      type: object
      required:
        - numberId
        - e164
        - status
        - smsEnabled
        - mmsEnabled
      description: Assigned Red Cloud number available to the authenticated API client.
      properties:
        numberId:
          type: string
          description: Red Cloud number identifier.
          example: num_abc123
        e164:
          type: string
          example: '+15551234567'
        status:
          type: string
          example: ACTIVE
        campaignRegId:
          type: string
          nullable: true
          example: A1B2C3
        smsEnabled:
          type: boolean
          example: true
        mmsEnabled:
          type: boolean
          example: true
        webhookInboundOverride:
          type: string
          nullable: true
          example: https://example.com/webhooks/inbound
        webhookDeliveryOverride:
          type: string
          nullable: true
          example: https://example.com/webhooks/delivery
    WhoAmIResponse:
      type: object
      required:
        - apiClientId
        - apiClientName
        - apiClientStatus
        - capabilities
        - assignedNumbers
        - inboundWebhookConfigured
        - deliveryWebhookConfigured
      allOf:
        - $ref: '#/components/schemas/ApiClientIdentity'
      properties:
        capabilities:
          type: array
          items:
            type: string
          example:
            - numbers:read
            - numbers:search
            - numbers:update
        assignedNumbers:
          $ref: '#/components/schemas/AssignedNumbersSummary'
        inboundWebhookConfigured:
          type: boolean
          example: true
        deliveryWebhookConfigured:
          type: boolean
          example: true
    NumbersResponse:
      type: object
      required:
        - numbers
      properties:
        numbers:
          type: array
          items:
            $ref: '#/components/schemas/Number'
    NumberSearchRequest:
      type: object
      description: >
        Search filters for available number inventory.


        Important implemented constraints:


        - `nxx` requires `npa`

        - `city` requires `state`

        - `rateCenter` requires `state`

        - `rateCenter` search is currently validation-blocked in the
        implementation
      properties:
        npa:
          type: string
          example: '555'
        nxx:
          type: string
          example: '123'
        city:
          type: string
          example: Austin
        state:
          type: string
          example: TX
        zipCode:
          type: string
          example: '73301'
        rateCenter:
          type: string
          example: AUSTIN
    NumberSearchRow:
      type: object
      required:
        - e164
      properties:
        e164:
          type: string
          example: '+15551234567'
        nationalDisplay:
          type: string
          nullable: true
          example: (555) 123-4567
        city:
          type: string
          nullable: true
          example: Austin
        state:
          type: string
          nullable: true
          example: TX
        rateCenter:
          type: string
          nullable: true
          example: AUSTIN
    NumberSearchGroup:
      type: object
      required:
        - rateCenter
        - count
        - numbers
      properties:
        rateCenter:
          type: string
          example: AUSTIN
        count:
          type: integer
          example: 1
        numbers:
          type: array
          items:
            $ref: '#/components/schemas/NumberSearchRow'
    NumberSearchResponse:
      type: object
      required:
        - groups
        - requestedQuantity
      properties:
        groups:
          type: array
          items:
            $ref: '#/components/schemas/NumberSearchGroup'
        providerResultCount:
          type: integer
          nullable: true
          example: 1
        requestedQuantity:
          type: integer
          example: 250
    UpdateNumberRequest:
      type: object
      description: >
        This request preserves the implemented field naming exactly.


        Important: the request field is `campaignregId`, not `campaignRegId`.


        Treat this route as operational configuration. Re-read the number after
        update if messaging behavior matters.
      properties:
        campaignregId:
          type: string
          nullable: true
          example: A1B2C3
        smsEnabled:
          type: boolean
          example: true
        mmsEnabled:
          type: boolean
          example: true
        webhookInboundOverride:
          type: string
          nullable: true
          example: https://example.com/webhooks/inbound
        webhookDeliveryOverride:
          type: string
          nullable: true
          example: https://example.com/webhooks/delivery
    UpdateNumberResponse:
      type: object
      required:
        - number
      properties:
        number:
          allOf:
            - $ref: '#/components/schemas/Number'
          nullable: true
    MessageMediaInput:
      type: object
      required:
        - storageKey
        - mimeType
        - byteLength
      description: Media attachment descriptor for outbound MMS sends.
      properties:
        storageKey:
          type: string
          example: storage_abc123
        mimeType:
          type: string
          example: image/jpeg
        byteLength:
          type: integer
          example: 182340
        sourceUrl:
          type: string
          example: https://example.com/files/catalog.jpg
        fileName:
          type: string
          example: catalog.jpg
    MessageSendRequest:
      type: object
      required:
        - sendFrom
        - to
        - messageType
        - externalReference
      description: >
        Outbound message request accepted into the Red Cloud queue-first
        messaging flow.


        Important implementation realities:


        - `sendFrom` must be a Red Cloud `numberId`

        - `to[]` values must be E.164 phone numbers

        - `SMS` must not include media

        - `MMS` must include media

        - final delivery state is reported asynchronously by webhook

        - ambiguous network outcomes should be retried with the same
        `Idempotency-Key`
      properties:
        sendFrom:
          type: string
          description: Red Cloud number identifier. This is not an E.164 phone number.
          example: num_abc123
        to:
          type: array
          minItems: 1
          description: One or more E.164 recipient phone numbers.
          items:
            type: string
            example: '+15551234567'
        messageType:
          type: string
          enum:
            - SMS
            - MMS
          description: Message mode. `SMS` forbids media. `MMS` requires media.
        text:
          type: string
          description: Message body text.
          example: Hello from Red Cloud
        media:
          type: array
          description: Required for `MMS`. Omit for `SMS`.
          items:
            $ref: '#/components/schemas/MessageMediaInput'
        externalReference:
          type: string
          description: Caller-defined business correlation key.
          example: order_789
    MessageSendResult:
      type: object
      required:
        - to
        - messageId
        - status
      description: Accepted recipient result returned synchronously from the send route.
      properties:
        to:
          type: string
          example: '+15551234567'
        messageId:
          type: string
          example: msg_abc123
        status:
          type: string
          example: QUEUED
    MessageSendError:
      type: object
      required:
        - to
        - error
      description: >-
        Recipient-specific rejection returned when part of a send request cannot
        be accepted.
      properties:
        to:
          type: string
          example: invalid
        error:
          type: object
          required:
            - code
            - message
            - details
          properties:
            code:
              type: string
              example: INVALID_RECIPIENT
            message:
              type: string
              example: Recipient must be a valid E.164 phone number
            details:
              type: object
              additionalProperties: true
    MessageSendResponse:
      type: object
      required:
        - results
        - errors
      description: |
        Synchronous acceptance response from the queue-first send route.

        `results[]` contains accepted recipients.
        `errors[]` contains recipient-specific rejections in the same request.
      properties:
        results:
          type: array
          items:
            $ref: '#/components/schemas/MessageSendResult'
        errors:
          type: array
          items:
            $ref: '#/components/schemas/MessageSendError'
paths:
  /api/auth/token:
    post:
      tags:
        - Authentication
      summary: Issue a bearer access token
      description: >
        Issues a bearer access token using OAuth 2 `client_credentials`.


        This endpoint supports:


        - HTTP Basic client authentication

        - request-body `client_id` and `client_secret`

        - form-encoded request fields


        This endpoint returns OAuth-style error bodies, not the CORE error
        wrapper.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OAuthTokenRequest'
            examples:
              bodyCredentials:
                value:
                  grant_type: client_credentials
                  client_id: rc_test_client_123
                  client_secret: rc_secret_abc123
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/OAuthTokenRequest'
      responses:
        '200':
          description: Bearer token issued
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OAuthTokenResponse'
              examples:
                success:
                  value:
                    access_token: rc_token_example
                    token_type: bearer
                    expires_in: 3600
        '400':
          description: Invalid request or unsupported grant type
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OAuthError'
              examples:
                invalidRequest:
                  value:
                    error: invalid_request
                    error_description: client_id and client_secret are required
                unsupportedGrantType:
                  value:
                    error: unsupported_grant_type
                    error_description: Only client_credentials is supported
        '401':
          description: Client authentication failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OAuthError'
              examples:
                invalidClient:
                  value:
                    error: invalid_client
                    error_description: Client authentication failed
        '500':
          description: Token issuance failure
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OAuthError'
              examples:
                serverError:
                  value:
                    error: server_error
                    error_description: Token issuance failed
  /api/core/whoami:
    get:
      tags:
        - CORE
      summary: Return the current API client context
      description: >-
        Returns the authenticated API client identity, capabilities,
        assigned-number summary, and webhook-configuration flags.
      responses:
        '200':
          description: API client context
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WhoAmIResponse'
              examples:
                success:
                  value:
                    apiClientId: rc_test_client_123
                    apiClientName: Red Cloud Test Client
                    apiClientStatus: ACTIVE
                    capabilities:
                      - numbers:read
                      - numbers:search
                      - numbers:update
                    assignedNumbers:
                      total: 1
                      smsEnabledCount: 1
                      mmsEnabledCount: 1
                    inboundWebhookConfigured: true
                    deliveryWebhookConfigured: true
        '401':
          description: Missing, invalid, or inactive bearer token context
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoreErrorResponse'
              examples:
                invalidToken:
                  value:
                    error:
                      code: UNAUTHORIZED
                      message: Invalid or missing bearer token
                      details: {}
                    requestId: req_auth_001
  /api/core/numbers:
    get:
      tags:
        - CORE
      summary: List active numbers for the authenticated API client
      description: >-
        Returns the Red Cloud numbers currently assigned to the authenticated
        API client. Use the returned `numberId` values as future `sendFrom`
        inputs.
      responses:
        '200':
          description: Active numbers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NumbersResponse'
              examples:
                success:
                  value:
                    numbers:
                      - numberId: num_abc123
                        e164: '+15551234567'
                        status: ACTIVE
                        campaignRegId: A1B2C3
                        smsEnabled: true
                        mmsEnabled: true
                        webhookInboundOverride: https://example.com/webhooks/inbound
                        webhookDeliveryOverride: https://example.com/webhooks/delivery
        '401':
          description: Missing, invalid, or inactive bearer token context
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoreErrorResponse'
              examples:
                invalidToken:
                  value:
                    error:
                      code: UNAUTHORIZED
                      message: Invalid or missing bearer token
                      details: {}
                    requestId: req_auth_001
        '500':
          description: Server failure
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoreErrorResponse'
  /api/core/numbers/search:
    post:
      tags:
        - CORE
      summary: Search available numbers
      description: >-
        Searches available number inventory using supported location and
        numbering filters.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NumberSearchRequest'
            examples:
              search:
                value:
                  npa: '555'
                  city: Austin
                  state: TX
      responses:
        '200':
          description: Search result groups
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NumberSearchResponse'
              examples:
                success:
                  value:
                    groups:
                      - rateCenter: AUSTIN
                        count: 1
                        numbers:
                          - e164: '+15551234567'
                            nationalDisplay: (555) 123-4567
                            city: Austin
                            state: TX
                            rateCenter: AUSTIN
                    providerResultCount: 1
                    requestedQuantity: 250
        '400':
          description: Invalid search criteria
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoreErrorResponse'
              examples:
                invalidSearch:
                  value:
                    error:
                      code: INVALID_REQUEST
                      message: city requires state
                      details: {}
                    requestId: req_search_002
        '401':
          description: Missing, invalid, or inactive bearer token context
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoreErrorResponse'
        '500':
          description: Server failure
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoreErrorResponse'
        '502':
          description: Upstream provider search failure
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoreErrorResponse'
              examples:
                upstreamError:
                  value:
                    error:
                      code: UPSTREAM_ERROR
                      message: Upstream search failed with status 502.
                      details:
                        providerStatus: 502
                    requestId: req_search_001
  /api/core/numbers/{numberId}:
    patch:
      tags:
        - CORE
      summary: Update number settings
      description: >-
        Updates supported number settings for a number owned by the
        authenticated API client. The request preserves the implemented field
        name `campaignregId`.
      parameters:
        - name: numberId
          in: path
          required: true
          schema:
            type: string
            example: num_abc123
          description: Red Cloud number identifier.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateNumberRequest'
            examples:
              update:
                value:
                  campaignregId: A1B2C3
                  smsEnabled: true
                  mmsEnabled: true
                  webhookInboundOverride: https://example.com/webhooks/inbound
                  webhookDeliveryOverride: https://example.com/webhooks/delivery
      responses:
        '200':
          description: Updated number state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UpdateNumberResponse'
              examples:
                success:
                  value:
                    number:
                      numberId: num_abc123
                      e164: '+15551234567'
                      status: ACTIVE
                      campaignRegId: A1B2C3
                      smsEnabled: true
                      mmsEnabled: true
                      webhookInboundOverride: https://example.com/webhooks/inbound
                      webhookDeliveryOverride: https://example.com/webhooks/delivery
        '400':
          description: Invalid request or inactive number
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoreErrorResponse'
              examples:
                invalidUpdate:
                  value:
                    error:
                      code: INVALID_REQUEST
                      message: webhookDeliveryOverride must be a valid URL
                      details: {}
                    requestId: req_number_001
        '401':
          description: Missing, invalid, or inactive bearer token context
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoreErrorResponse'
        '403':
          description: Number is not owned by this API client
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoreErrorResponse'
              examples:
                forbiddenNumber:
                  value:
                    error:
                      code: FORBIDDEN
                      message: numberId is not owned by this API client
                      details: {}
                    requestId: req_number_002
        '404':
          description: Number not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoreErrorResponse'
              examples:
                notFound:
                  value:
                    error:
                      code: NOT_FOUND
                      message: numberId not found
                      details: {}
                    requestId: req_number_003
        '500':
          description: Server failure
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoreErrorResponse'
  /api/core/messages/send:
    post:
      tags:
        - CORE
      summary: Queue an outbound message
      description: |
        Queues outbound messages through the existing Red Cloud pipeline.

        Important:

        - `Idempotency-Key` is required.
        - `sendFrom` must be a Red Cloud number id.
        - `SMS` requests cannot include media.
        - `MMS` requests must include media.
        - successful synchronous acceptance returns `QUEUED`.
        - final delivery outcome is reported later by webhook.
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            example: idem_order_789
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MessageSendRequest'
            examples:
              sms:
                value:
                  sendFrom: num_abc123
                  to:
                    - '+15551234567'
                  messageType: SMS
                  text: Hello from Red Cloud
                  externalReference: order_789
              mms:
                value:
                  sendFrom: num_abc123
                  to:
                    - '+15551234567'
                  messageType: MMS
                  text: Catalog preview
                  media:
                    - storageKey: storage_abc123
                      mimeType: image/jpeg
                      byteLength: 182340
                      sourceUrl: https://example.com/files/catalog.jpg
                      fileName: catalog.jpg
                  externalReference: order_789
      responses:
        '202':
          description: Message request accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MessageSendResponse'
              examples:
                success:
                  value:
                    results:
                      - to: '+15551234567'
                        messageId: msg_abc123
                        status: QUEUED
                    errors: []
                partialSuccess:
                  value:
                    results:
                      - to: '+15551234567'
                        messageId: msg_abc123
                        status: QUEUED
                    errors:
                      - to: invalid
                        error:
                          code: INVALID_RECIPIENT
                          message: Recipient must be a valid E.164 phone number
                          details: {}
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoreErrorResponse'
              examples:
                missingIdempotencyKey:
                  value:
                    error:
                      code: INVALID_REQUEST
                      message: Idempotency-Key header is required
                      details: {}
                    requestId: req_send_001
        '401':
          description: Missing, invalid, or inactive bearer token context
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoreErrorResponse'
              examples:
                invalidToken:
                  value:
                    error:
                      code: UNAUTHORIZED
                      message: Invalid or missing bearer token
                      details: {}
                    requestId: req_auth_001
        '403':
          description: '`sendFrom` is not owned by this API client'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoreErrorResponse'
              examples:
                forbiddenNumber:
                  value:
                    error:
                      code: FORBIDDEN
                      message: sendFrom is not owned by this API client
                      details: {}
                    requestId: req_send_002
        '404':
          description: Number not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoreErrorResponse'
              examples:
                notFound:
                  value:
                    error:
                      code: NOT_FOUND
                      message: sendFrom numberId not found
                      details: {}
                    requestId: req_send_004
        '409':
          description: Idempotency key reused with a different payload
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoreErrorResponse'
              examples:
                idempotencyConflict:
                  value:
                    error:
                      code: CONFLICT
                      message: >-
                        Idempotency-Key was already used with a different
                        payload
                      details: {}
                    requestId: req_send_005
        '500':
          description: Server failure
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoreErrorResponse'
              examples:
                serverError:
                  value:
                    error:
                      code: SERVER_ERROR
                      message: Message send failed unexpectedly
                      details: {}
                    requestId: req_send_006
