openapi: 3.1.0
info:
  title: Record Processing API
  description: >-
    API for receiving individual records for processing in batches.


    **All POST requests with a body must be sent as valid JSON with the
    `Content-Type: application/json` header.**


    Clients can send records to the API through the `/records/send` endpoint.
    Records are grouped into a "batch" and processed based on the chosen
    `batchDuration` strategy.


    - **short (default):** Records are collected in a temporary batch. If no new
    records are received for that batch within a 60-second window, the batch is
    closed and processed into a CSV file.

    - **long:** Records are collected persistently over a longer period and
    processed at a fixed, scheduled time (e.g., daily, weekly, or monthly).
  version: 1.0.5
servers:
  - url: https://api.pioneerdm.com
    description: Testing Server
  - url: https://api.v1.pioneerdm.com
    description: Production Server
tags:
  - name: General
    description: General API information
  - name: Records
    description: Operations for sending records and retrieving processed upload status
components:
  securitySchemes:
    apiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: API Key required for accessing protected record endpoints.
  schemas:
    RecordInput:
      type: object
      description: >-
        Represents a single record to be submitted.

        The `batchId` and `batchDuration` fields are required to identify the
        batch and control its processing strategy.

        Allows for arbitrary additional properties representing record data.
      properties:
        batchId:
          type: string
          description: Explicit identifier for the batch this record belongs to.
          example: mailer_campaign_xyz_20240315
        batchDuration:
          type: string
          description: >-
            Important. Determines the batching strategy. 'short' uses a
            time-based inactivity window (e.g., 60 seconds) to close and process
            the batch. 'long' collects records over a longer period to be
            processed at a fixed, scheduled time (e.g., daily, weekly, or
            monthly). It is very important to select a long batch duration if
            your company plans to send less than 50 records a minute. 
          default: short
          example: short
        mail_date:
          type: string
          format: date
          description: >-
            An optional, desired mail date in YYYY-MM-DD format. This is not a
            guaranteed mailing date but is used as a hint for grouping records
            with similar desired timelines.
          example: '2024-06-15'
        message:
          type: string
          description: >-
            The main body text or message for the mail piece. Can contain merge
            variables (e.g., {{to.name}} or {{offer_code}}).
          example: >-
            Hi {{to.name}}, we've missed you! Here's a special offer just for
            you.
        metadata:
          type: object
          description: >-
            Optional metadata associated with the record. This field is no
            longer used for deriving a batch identifier.
          properties:
            utm_source:
              type: string
              example: google
            utm_medium:
              type: string
              example: cpc
            utm_campaign:
              type: string
              example: spring_sale
            utm_content:
              type: string
              example: banner_ad_1
        meta:
          type: object
          description: Alias for 'metadata'. Optional metadata associated with the record.
          properties:
            utm_source:
              type: string
            utm_medium:
              type: string
            utm_campaign:
              type: string
            utm_content:
              type: string
        description:
          type: string
          example: Summer Sale Postcard
        to:
          type: object
          additionalProperties: true
          example:
            name: Recipient Name
            address_line1: 123 Main St
            city: Anytown
            state: CA
            address_zip: '90210'
        from:
          type: object
          additionalProperties: true
          example:
            name: Sender Name
            address_line1: 456 Sender Ave
            city: Senderville
            state: NY
            address_zip: '10001'
        merge_variables:
          type: object
          description: >-
            An object containing key-value pairs for dynamic data insertion.
            Each key in this object can be used as a placeholder in string
            fields like 'message' (e.g., {{offer_code}}). The system will
            replace the placeholder with the corresponding value. This is useful
            for custom data not available in other top-level fields like 'to' or
            'from'.
          additionalProperties: true
          example:
            offer_code: SAVE20NOW
            username: JaneDoe
        front:
          type: string
          example: tmpl_front_abc
        back:
          type: string
          example: tmpl_back_xyz
        size:
          type: string
          example: 4x6
      additionalProperties: true
      required:
        - batchId
        - batchDuration
      example:
        description: Demo postcard with merge variables
        batchId: daily_lead_upload_20240521
        batchDuration: long
        mail_date: '2024-06-20'
        to:
          name: Alex Ray
          address_line1: 123 Main St
          city: Anytown
          state: CA
          address_zip: '90210'
        from:
          name: Pioneer Corp
          address_line1: 456 Sender Ave
          city: Senderville
          state: NY
          address_zip: '10001'
        front: tmpl_front_abc
        back: tmpl_back_xyz
        message: >-
          Hello {{to.name}}, don't miss out on our summer sale! Use code
          {{offer_code}} for 20% off.
        merge_variables:
          offer_code: SUMMER24
    IdempotencyInfo:
      type: object
      description: >-
        Information regarding the idempotency of the request, present if a
        request was replayed using an Idempotency-Key.
      properties:
        message:
          type: string
          example: Request was already processed. Returning cached response.
        key:
          type: string
          example: a7b2c9d8-e1f2-3456-7890-1k2l3m4n5o6p
      required:
        - message
        - key
    SendShortBatchResponse:
      type: object
      description: Response for a record successfully added to a 'short' duration batch.
      properties:
        message:
          type: string
          example: Record accepted for short batching.
        batchDuration:
          type: string
          enum:
            - short
          example: short
        batchString:
          type: string
          description: >-
            The identifier for the batch the record was added to. Use this
            string to query processed uploads later via /records/get.
          example: my_crm_direct_mail_q1_promo_postcard
        compilesOn:
          type: string
          format: date-time
          description: >-
            An estimated timestamp for when this batch will be processed if no
            more records are sent to it.
          example: '2024-05-21T18:13:02.123Z'
        count:
          type: integer
          format: int32
          description: >-
            The current number of records received for this batch during its
            current lifecycle.
          example: 15
        idempotency:
          $ref: '#/components/schemas/IdempotencyInfo'
          description: >-
            Present if the request was successfully replayed using an
            Idempotency-Key, indicating a cached response.
      required:
        - message
        - batchDuration
        - batchString
        - compilesOn
        - count
    SendLongBatchResponse:
      type: object
      description: Response for a record successfully added to a 'long' duration batch.
      properties:
        message:
          type: string
          example: Record accepted for long-lived batch.
        batchDuration:
          type: string
          enum:
            - long
          example: long
        batchString:
          type: string
          description: >-
            The identifier for the batch the record was added to. Use this
            string to query processed uploads later via /records/get.
          example: daily_lead_upload_20240521
        estProcessingTime:
          type: string
          format: date-time
          description: The scheduled timestamp for when this batch will be processed.
          example: '2024-05-22T05:05:00.000Z'
        currentCount:
          type: integer
          format: int32
          description: >-
            The total number of records collected for this persistent batch so
            far.
          example: 152
        idempotency:
          $ref: '#/components/schemas/IdempotencyInfo'
          description: >-
            Present if the request was successfully replayed using an
            Idempotency-Key, indicating a cached response.
      required:
        - message
        - batchDuration
        - batchString
        - estProcessingTime
        - currentCount
    GetUploadsInput:
      type: object
      properties:
        batchString:
          type: string
          description: The batch identifier to query processed uploads for.
          example: daily_lead_upload_20240521
      required:
        - batchString
    UploadSummary:
      type: object
      description: >-
        Summary information about a processed upload document found in the
        database.
      properties:
        _id:
          type: string
          description: The unique identifier of the Upload document in MongoDB.
          example: 65d6a4b2f3e1c2a3b4d5e6f7
        batchId:
          type: string
          description: The batch identifier associated with this upload.
          example: daily_lead_upload_20240521
        status:
          type: string
          description: The current processing status of the upload.
          example: CSV_UPLOADED
        createdAt:
          type: string
          format: date-time
          description: Timestamp when the Upload document was first created.
          example: '2024-02-21T18:12:02.123Z'
        updatedAt:
          type: string
          format: date-time
          description: Timestamp when the Upload document was last updated.
          example: '2024-02-21T18:15:30.456Z'
        originalRecordCount:
          type: integer
          format: int32
          description: The total number of records in the processed batch.
          example: 150
        csvGeneratedAt:
          type: string
          format: date-time
          description: Timestamp when the CSV file for this batch was generated.
          example: '2024-02-21T18:15:29.987Z'
        s3Key:
          type: string
          description: The key of the generated CSV file in S3.
          example: data/daily_lead_upload_20240521_2024-02-21T18-15-29-987Z.csv
        type:
          type: string
          description: The type associated with the API key used ('e.g., prod', 'test').
          example: prod
      required:
        - _id
        - batchId
        - status
        - createdAt
        - updatedAt
        - originalRecordCount
        - csvGeneratedAt
        - s3Key
        - type
    GetUploadsResponse:
      type: object
      properties:
        uploads:
          type: array
          description: >-
            An array of processed uploads matching the request. The array is
            empty if no matches are found.
          items:
            $ref: '#/components/schemas/UploadSummary'
      required:
        - uploads
    ErrorResponse:
      type: object
      properties:
        message:
          type: string
          description: A descriptive error message.
      required:
        - message
paths:
  /:
    get:
      tags:
        - General
      summary: Check API Status
      description: Returns a simple message indicating the API is running.
      operationId: getApiStatus
      responses:
        '200':
          description: Successful connection
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Successfully connected to the records API.
                  connected:
                    type: boolean
                    example: true
                required:
                  - message
                  - connected
  /records/send:
    post:
      tags:
        - Records
      summary: Send a Single Record
      description: >-
        Submits a single record as a **JSON object** to be added to a batch. The
        request body must be valid JSON and you must include the `Content-Type:
        application/json` header.


        The `batchId` and `batchDuration` fields are required within the JSON
        object.

        This endpoint also requires `x-api-key` and `idempotency-key` headers.
      operationId: sendRecord
      security:
        - apiKeyAuth: []
      parameters:
        - name: idempotency-key
          in: header
          required: true
          description: >-
            A unique key (e.g., UUID) to ensure a request is processed only
            once. If a request with this key has completed, the server returns
            the cached response.
          schema:
            type: string
            format: uuid
            example: a7b2c9d8-e1f2-3456-7890-1k2l3m4n5o6p
      requestBody:
        description: The record object to be submitted.
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RecordInput'
      responses:
        '200':
          description: >-
            **OK.** Standard response for successfully receiving a record for a
            **'short'** duration batch.

            This status is also returned for an idempotent replay of any
            previously successful request (both 'short' and 'long'), in which
            case the `idempotency` field will be populated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SendShortBatchResponse'
        '202':
          description: >-
            **Accepted.** The record was successfully received and stored for a
            **'long'** duration batch. It will be processed at a later,
            scheduled time.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SendLongBatchResponse'
        '400':
          description: >-
            **Bad Request.** The server cannot process the request due to a
            client error. This can occur for several reasons:
              
            1.  **Malformed JSON:** The request body is not valid JSON.

            2.  **Missing Required Fields:** The JSON body is missing `batchId`
            or `batchDuration`.

            3.  **Missing Idempotency-Key Header:** The `idempotency-key` header
            is missing.

            4.  **Invalid `batchDuration` Value:** The value provided is not
            'short' or 'long'.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                MissingBatchInfo:
                  summary: Required fields missing
                  value:
                    message: >-
                      Request body must contain required properties: 'batchId',
                      'batchDuration'.
                MissingIdempotencyKey:
                  summary: Idempotency-Key header missing
                  value:
                    message: Header idempotency-key is required for this operation.
                InvalidBatchDuration:
                  summary: Invalid batch duration
                  value:
                    message: Invalid batch duration 'medium'. Use 'short' or 'long'.
        '401':
          description: Unauthorized - API Key is missing or invalid format.
        '403':
          description: Forbidden - API Key is invalid or inactive.
        '409':
          description: >-
            Conflict - A request with the same `Idempotency-Key` is currently
            being processed.
        '500':
          description: Internal Server Error.
  /records/get:
    post:
      tags:
        - Records
      summary: Get Processed Upload Summaries by Batch
      description: >-
        Retrieves summary information about **processed** uploads from the
        database that match the provided `batchString` and are associated with
        the authenticated API key. This does **not** retrieve records from a
        temporary 'short' batch that has not yet been processed.
      operationId: getProcessedUploadsByBatch
      security:
        - apiKeyAuth: []
      requestBody:
        description: >-
          Specify the batch identifier to retrieve processed upload summaries
          for.
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GetUploadsInput'
      responses:
        '200':
          description: >-
            Successfully retrieved processed upload summaries. The 'uploads'
            array will be empty if no matching processed uploads are found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetUploadsResponse'
        '400':
          description: Bad Request - Missing `batchString` in the request body.
        '401':
          description: Unauthorized - API Key is missing or invalid format.
        '403':
          description: Forbidden - API Key is invalid or inactive.
        '500':
          description: Internal Server Error.
