openapi: 3.0.3

info:
  title: Courier
  description: |
    Pearson Service Insights Courier - Targeted Messaging Service

    ## Getting Started
    ---
    ### Request Access
    If you haven't already, head over to [PSI's Request Form](https://onboarding.observeng.pearsondev.com/forms?form=request&amp;group=autoeng)
      1. For "Which platform does this request involve?", select "API"
      2. For "Which application does this request involve", select "Courier"
      3. For "Which type of task does this request involve?" select "Access"
      4. Add any email addresses that you wish to receive access for in the "Description" section
      5. Enter your information in the primary contact fields

    ### Authentication

    Once you've received your API key, you must authenticate with this rest API using the combination of two headers:
     - `x-courier-user` : The username for the user
     - `x-api-key` : The corresponding API key for the user

    If a user sends invalid credentials they will receive a 403 error.
    Valid credentials that pass authentication may still receive a 403 error if they request to
    access a resource they aren't authorized to access. Please refer to the "Authorization" section.

    ### Authorization

    While authenticated users are permitted to use the API, they may not be
    authorized to operate against the requested Pinpoint Application.

    Users will receive a 403 error if they authenticate successfully, but attempt to operate against
    a Pinpoint Application they don't have access to.

    ### Examples
    The documentation for each of these endpoints should be enough to get you started, but for more detailed and language
    specific examples, check out our [samples repo](https://gitlab.com/pearsontechnology/IO/service-insights/communications/courier-samples)

    ### Send some messages!
    Check out the [POST /messages endpoint](#post-/messages) and send yourself a message.

    ### Notes
    Any message sent through the dev instance of this API will more than likely be flagged as spam. If you're using it for testing, keep an eye on your [email quarentine](https://security.microsoft.com/quarantine?viewid=Email)
  version: "0.1.0"

servers:
  - url: "http://api.pearsonserviceinsights.com/courier"
    description: Production rest API
  - url: "http://api-uat.pearsonserviceinsights.com/courier"
    description: User Acceptance Testing (UAT) rest API

tags:
  - name: Users
    description: Endpoints for actions related to users.
  - name: Messages
    description: Endpoints for actions related to messages.

paths:
  /:
    options:
      tags:
        - API Documentation
      summary: CORS support for interactive API documentation
      operationId: baseCorsOptions
      description: Enable CORS by returning correct headers
      responses:
        200:
          description: Default response for CORS method
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: string
            Access-Control-Allow-Methods:
              schema:
                type: string
            Access-Control-Allow-Headers:
              schema:
                type: string
          content: {}
      x-amazon-apigateway-integration:
        type: mock
        requestTemplates:
          application/json: |
            {
              "statusCode" : 200
            }
        responses:
          default:
            statusCode: "200"
            responseParameters:
              method.response.header.Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'"
              method.response.header.Access-Control-Allow-Methods: "'*'"
              method.response.header.Access-Control-Allow-Origin: "'*'"
            responseTemplates:
              application/json: |
                {}

    get:
      summary: API landing page
      tags:
        - API Documentation
      operationId: ApiLandingPage
      responses:
        200:
          description: OK
          headers:
            Content-Type:
              schema:
                type: string
            Access-Control-Allow-Origin:
              schema:
                type: string
            Access-Control-Allow-Methods:
              schema:
                type: string
            Access-Control-Allow-Headers:
              schema:
                type: string
      x-amazon-apigateway-integration:
        responses:
          default:
            statusCode: "200"
            responseParameters:
              method.response.header.Content-Type: "'text/html'"
              # Support for CORS as API DOC is served from another origin server, not the API Gateway itself
              method.response.header.Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'"
              method.response.header.Access-Control-Allow-Methods: "'*'"
              method.response.header.Access-Control-Allow-Origin: "'*'"
            responseTemplates:
              text/html: >
                <!DOCTYPE html>
                <html lang="en">
                  <head>
                    <meta charset="utf-8"> <!-- Important: rapi-doc uses utf8 charecters -->
                    <script type="module" src="https://unpkg.com/rapidoc/dist/rapidoc-min.js"></script>
                  </head>
                  <body>
                    <rapi-doc
                      spec-url="https://downloads.pearsoncloudmanagement.com/courier/$context.stage/openapi.yaml"
                      theme="dark"
                      render-style="read"
                      heading-text="Courier"
                      primary-color="#12b2a6"
                      header-color="#003057"
                      bg-color="#151515"
                    >
                      <img slot="nav-logo" src="https://messaging.pearsonserviceinsights.com/static/media/courier.webp">
                      <img slot="logo" src="https://dashboards.pearsonserviceinsights.com/landing-page/assets/pearson-vector-logo.svg">
                    </rapi-doc>
                  </body>
                </html>
        requestTemplates:
          application/json: '{"statusCode": 200}'
        passthroughBehavior: "when_no_match"
        type: "mock"

  /users:
    get:
      tags:
        - Users
      summary: Get user(s).
      description: |
        Without a `user_id` parameter, this endpoint will return a paginated list of all users and their attributes
        that are able to receive a message through Courier.
        To get the next page of results, provide the `next_token` parameter that was returned in the initial response.

        This endpoint is mostly useful for exploring user attributes to know what attributes can be used as substitutions on the [POST /messages endpoint](#post-/messages).
      operationId: getUsers
      security:
        - ApiGatewayAuth: []
          ApiKeyAuth: []
      parameters:
        - in: query
          name: user_id
          schema:
            type: string
            format: email
            example: john.doe@pearson.com
        - in: query
          name: next_token
          schema:
            $ref: "#/components/schemas/NextToken"
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      $ref: "#/components/schemas/User"
                  next_token:
                    $ref: "#/components/schemas/NextToken"
                required:
                  - results
        "400":
          description: Bad Request
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: Resource Not Found
        "500":
          description: Server Error
      x-amazon-apigateway-integration:
        type: aws_proxy
        uri: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:courier-request-handler/invocations"
        httpMethod: POST
        passthroughBehavior: when_no_match
        contentHandling: CONVERT_TO_TEXT

  /user-endpoints:
    get:
      tags:
        - Users
      summary: Get user endpoint(s).
      description: |
        Each user may have multiple endpoints, so this endpoint allows you to view all endpoints associated with a user 
        and endpoint and user attributes. Currently, all users only have an `EMAIL` endpoint by default.
      operationId: getUserEndpoints
      security:
        - ApiGatewayAuth: []
          ApiKeyAuth: []
      parameters:
        - in: query
          name: user_id
          required: true
          schema:
            type: string
            format: email
            example: john.doe@pearson.com
        - in: query
          name: next_token
          schema:
            $ref: "#/components/schemas/NextToken"
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      $ref: "#/components/schemas/UserEndpoint"
                  next_token:
                    $ref: "#/components/schemas/NextToken"
                required:
                  - results
        "400":
          description: Bad Request
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: Resource Not Found
        "500":
          description: Server Error
      x-amazon-apigateway-integration:
        type: aws_proxy
        uri: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:courier-request-handler/invocations"
        httpMethod: POST
        passthroughBehavior: when_no_match
        contentHandling: CONVERT_TO_TEXT

  /messages:
    get:
      tags:
        - Messages
      summary: Get message(s).
      description: |
        If a `request_id` is not provided, this endpoint will return a list of all messages you have sent.
        This endpoint is mostly useful for checking if a message you sent failed to be delivered to its
        recipient so you can try again later once any issues have been resolved.
      operationId: getMessages
      security:
        - ApiGatewayAuth: []
          ApiKeyAuth: []
      parameters:
        - in: query
          name: request_id
          schema:
            type: string
            format: uuid
            example: 0b7a5407-a733-4d45-8db3-d82d137c3359
        - in: query
          name: next_token
          schema:
            $ref: "#/components/schemas/NextToken"
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      $ref: "#/components/schemas/MessageResponse"
                  next_token:
                    $ref: "#/components/schemas/NextToken"
                required:
                  - results
        "400":
          description: Bad Request
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: Resource Not Found
        "500":
          description: Server Error
      x-amazon-apigateway-integration:
        type: aws_proxy
        uri: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:courier-request-handler/invocations"
        httpMethod: POST
        passthroughBehavior: when_no_match
        contentHandling: CONVERT_TO_TEXT

    post:
      tags:
        - Messages
      summary: Send an endpoint message(s).
      description: |
        ## Notes
        If a recipient replies to a an email sent via this endpoint, their reply will be bounced
        unless at least one email address is provided in the `reply_to_addresses` array.

        The `from_address` allows you to specify the local-part of the email address used for the From header in your message.
        You cannot provide the domain portion of the address. In the example below, the message will appear to the recipient
        to have come from no-reply.service@uat.pearsonserviceinsights.com if using the UAT API endpoint, or
        no-reply.service@pearsonserviceinsights.com if using the production API endpoint.

        If no `from_address` is supplied, Courier will default to no-reply@<env.>pearsonserviceinsights.com

        ## Substitutions
        `substitutions` defined in the `email_message` part are used for values that are supposed to remain the same
        regardless of the recipient. In the example below, `example_sub` is set to a date and applied to the subject
        of the message. The subject would then be "2022-10-01 Email Subject" for all recipients.

        In order to specify different substituion values per recipient, use the `substitutions` attached to each recipient
        in the `endpoints` object of the payload.
      operationId: postMessages
      security:
        - ApiGatewayAuth: []
          ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MessageRequest"
      responses:
        "201":
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      $ref: "#/components/schemas/MessageResponse"
                required:
                  - results
        "400":
          description: Bad Request
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: Resource Not Found
        "500":
          description: Server Error
      x-amazon-apigateway-integration:
        type: aws_proxy
        uri: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:courier-request-handler/invocations"
        httpMethod: POST
        passthroughBehavior: when_no_match
        contentHandling: CONVERT_TO_TEXT

  # TODO: /health for health endpoint

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
    ApiGatewayAuth:
      type: apiKey
      in: header
      name: Unused
      x-amazon-apigateway-authorizer:
        type: request
        identitySource: "method.request.header.x-courier-user,method.request.header.x-api-key"
        authorizerUri: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:courier-custom-auth/invocations"
        authorizerResultTtlInSeconds: 300
        enableSimpleResponses: false
      x-amazon-apigateway-authtype: custom

  schemas:
    NextToken:
      type: string
      nullable: true
      example: eyJNYXJrZXIiOiBudWxsLCAiYm90b190cnVuY2F0ZV9hbW91bnQiOiAxfQ==

    User:
      type: object
      properties:
        user_id:
          type: string
          format: email
          example: john.doe@pearson.com
        user_attributes:
          type: object
          nullable: true
      required:
        - user_id
        - user_attributes

    UserEndpoint:
      type: object
      properties:
        address:
          type: string
          example: john.doe@pearson.com
        channel_type:
          type: string
          example: EMAIL
        endpoint_status:
          type: string
          example: ACTIVE
        id:
          type: string
          example: john.doe@pearson.com
        opt_out:
          type: string
          example: NONE
        user:
          $ref: "#/components/schemas/User"
      required:
        - address
        - channel_type
        - endpoint_status
        - id
        - opt_out
        - user

    EmailMessageConfiguration:
      type: object
      properties:
        simple_email:
          type: object
          properties:
            subject:
              type: object
              properties:
                data:
                  type: string
                  example: Email Subject
              required:
                - data
            text_part:
              type: object
              properties:
                data:
                  type: string
                  example: This is an example plain text email body.
              required:
                - data
      required:
        - simple_email

    MessageConfiguration:
      type: object
      properties:
        email_message:
          $ref: "#/components/schemas/EmailMessageConfiguration"
      required:
        - email_message

    MessageRequest:
      type: object
      properties:
        message_configuration:
          $ref: "#/components/schemas/MessageConfiguration"
        endpoints:
          type: object
          additionalProperties:
            type: object
            properties:
              body_override:
                type: string
              raw_content:
                type: string
              title_override:
                type: string
              substitutions:
                type: object
                properties:
                  example_endpoint_sub:
                    type: array
                    items:
                      type: string
                    example:
                      - 2022-10-01
          example:
            {
              "john.doe@pearson.com":
                {
                  "substitutions":
                    {
                      "example_endpoint_sub":
                        ["with example value for endpoint"],
                    },
                },
            }
      required:
        - message_configuration
        - endpoints

    MessageResponse:
      type: object
      properties:
        request_id:
          type: string
          format: uuid
          example: 0b7a5407-a733-4d45-8db3-d82d137c3359
        sender:
          type: string
          example: jane.doe@pearson.com
        user_endpoint_id:
          type: string
          example: john.doe@pearson.com
        address:
          type: string
          example: john.doe@pearson.com
        delivery_status:
          type: string
          example: SUCCESSFUL
        message_id:
          type: string
          nullable: true
          example: gfoj0rsveqptvo59idadahfq59ejvh8mnjsmq5g0
        status_code:
          type: integer
          example: 200
        status_message:
          type: string
          example: 02000000c2pi78qs-c9hnk4u3-ldou-nq2n-rih3-amtbs6i313o0-000000
      required:
        - request_id
        - user_endpoint_id
        - address
        - delivery_status
        - message_id
        - status_code
        - status_message
        - sender
