openapi: 3.0.0
info:
  title: Optimized App API
  description: >
    API for managing profiles, batches, and campaign scheduling in
    Optimized.App.


    # Introduction


    To begin utilizing Optimized.App, it's essential to:


    1. Obtain an API Token

    Access to the API is granted only through a valid API Token. Note: API
    Tokens can only be created by an organization administrator. You can
    generate your token via the Organization Settings section, under Integration
    -> Api Tokens on the Optimized.App dashboard.


    2. Understand Rate and Usage Limits

    Optimized.App has specific rate and usage limitations. Familiarize yourself
    with these to ensure uninterrupted service.


    3. Secure Communication Protocol

    Optimized.App mandates the use of HTTPS-secured communications.

    Any requests sent via HTTP return an HTTP 301 redirect to the corresponding
    HTTPS resources.


    4. Response Format

    All responses from Optimized.App are provided in JSON format, ensuring a
    standardized and easy-to-parse structure for your applications.


    5. All datetime values in requests and responses are in UTC timezone.
    Responses are formatted according to ISO 8601 standard
    (YYYY-MM-DDThh:mm:ss.sssZ).


    By following these guidelines, you'll be set to effectively integrate and
    work with Optimized.App's API.


    # Authentication


    To begin utilizing Optimized.App, it's essential to:


    1. Obtain an API Token

    Access to the API is granted only through a valid API Token. Note: API
    Tokens can only be created by an organization administrator. You can
    generate your token via the Organization Settings section, under Integration
    -> Api Tokens on the Optimized.App dashboard.


    You must include an API token in each request to the Optimized.App API with
    the Authorization request header.


    Example: `Authorization: Bearer [API-TOKEN]`


    If an API token is missing, malformed, or invalid, you will receive an HTTP
    401 Unauthorized response code.


    # Rate and Usage Limits


    The basic rate limit is 250 requests per minute per user basis. Also,
    depending on your plan, you may have different usage limits. If you exceed
    either limit, your request will return an HTTP 429 Too Many Requests status
    code.


    The following set of headers are returned on the response to help you
    identify your use status:


    | Header | Description |

    | ------ | ----------- |

    | X-Ratelimit-Limit | The maximum number of requests that the consumer is
    permitted to make per minute. |

    | X-Ratelimit-Remaining | The number of requests remaining in the current
    rate limit window. |

    | X-Ratelimit-Reset | The time at which the current rate limit window resets
    in UTC epoch seconds. This header is only present when maximum number of
    requests has been consumed for time period. |


    ## Endpoint Specific Rate Limits


    | Endpoint | Description |

    | ------ | ----------- |

    | Profile importing with scheduling | This is a group based rate limit for
    10 requests per minute. |


    # Errors


    Optimized.App employs standard HTTP response codes to signify the outcome of
    an API request. Typically, codes within the 2xx range signify a successful
    operation. Codes within the 4xx range point to an error coming from the
    provided information (for example, missing a necessary parameter or having
    invalid data). Codes in the 5xx range are indicative of issues with
    Optimized.App servers, though these occurrences are uncommon.


    | HTTP status code | Description |

    | ------ | ----------- |

    | 200 | Request was made successfully. |

    | 201 | Request was made successfully and a resource was created. |

    | 204 | Request was made successfully and no content was returned. |

    | 400 | The request was rejected, often due to missing a required parameter.
    |

    | 401 | API token is not valid. |

    | 403 | API token has no permission to perform the request. |

    | 404 | The requested resource does not exist. |

    | 405 | The request method is not allowed for the resource. |

    | 422 | The request was rejected, often due to invalid client data. |

    | 429 | Too many requests hit the API too quickly. We advise implementing an
    exponential backoff strategy for your requests. |

    | 500 | Something went wrong on our end. |

    | 503 | An unexpected spike in API access traffic. The server is usually
    operational within a short period of time. If the outage persists or you
    receive any other form of an HTTP 5XX error, please contact support. |


    ## Validation Errors (422 Unprocessable Entity)


    When a request fails validation, the API will respond with a `422
    Unprocessable Entity` status code and a JSON response containing detailed
    information about the validation errors.


    ### Validation Error Format


    When validation fails for a request, the API will return a JSON response
    that includes:


    1. A high-level error `message` that summarizes the validation issues

    2. An `errors` object containing detailed validation error messages
    organized by field


    ```json

    {
        "message": "The fail on missing attributes field must be true or false.",
        "errors": {
            "fail_on_missing_attributes": [
                "The fail on missing attributes field must be true or false."
            ]
        }
    }

    ```


    ### Understanding the Validation Error Response


    - **message**: A summary of validation errors that occurred. If there are
    multiple errors, the message will indicate how many additional errors exist
    beyond the first one mentioned.


    - **errors**: An object where:
      - Each key represents a field that failed validation
      - Each value is an array of error messages for that field
      - A field may have multiple error messages if it fails multiple validation rules

    ### Handling Array Validation Errors


    When validating array elements, the error keys will include the array
    indices:


    ```json

    {
        "message": "The profiles.1.person.email_address must be a valid email address. (and 1 more error)",
        "errors": {
            "profiles.1.person.email_address": [
                "The profiles.1.person.email_address must be a valid email address."
            ],
            "profiles.0.person.phone_number": [
                "The profiles.0.person.phone_number field contains an invalid number."
            ]
        }
    }

    ```


    For nested fields using "dot" notation, error keys in the response will
    maintain this notation for easy identification:


    ## Best Practices for Handling Validation Errors


    When developing an integration with the Optimized.App API, consider these
    best practices for handling validation errors:


    1. **Always check for the HTTP status code** first - a status code of 422
    indicates validation errors.


    2. **Extract field-specific errors** from the `errors` object to display
    targeted feedback to your users.


    3. **Display all validation errors** to users at once rather than one at a
    time to improve user experience.


    4. **For form submissions**, map the error field names in the API response
    to your form field names for proper error display.


    5. **Implement client-side validation** that mirrors the API's validation
    rules to provide faster feedback to users before submitting requests to the
    API.


    By properly handling validation errors, you can create a smoother
    integration experience with the Optimized.App API and provide clear guidance
    to users when their inputs need correction.


    # Pagination


    Optimized.App API uses pagination for endpoints that return collections of
    resources to improve performance and reduce response sizes. Pagination
    information is included in the JSON response body and follows a consistent
    format across all paginated endpoints.


    ## Pagination Response Format


    Paginated responses include the following standard fields:


    | Field | Description |

    |-------|-------------|

    | `current_page` | The current page number |

    | `data` | Array containing the actual resource items for the current page |

    | `first_page_url` | URL for the first page of results |

    | `from` | Position of the first item on the current page |

    | `next_page_url` | URL for the next page (null if there is no next page) |

    | `path` | Base URL path without query parameters |

    | `per_page` | Number of items displayed per page |

    | `prev_page_url` | URL for the previous page (null if there is no previous
    page) |

    | `to` | Position of the last item on the current page |


    ## Example Pagination Response


    ```json

    {
        "current_page": 1,
        "data": [
            {
                "uuid": "9e072d6f-11af-4a13-a373-924a516d3795",
                "metadata": [],
                "name": "My Group",
                "created_at": "2025-01-22T09:04:02.000000Z",
                "updated_at": "2025-01-22T09:04:02.000000Z",
                "deleted_at": null
            },
            {
                "uuid": "9e072d6f-0ed2-486a-baca-58f889f55cd4",
                "metadata": [],
                "name": "My second group",
                "created_at": "2025-01-22T09:04:02.000000Z",
                "updated_at": "2025-01-22T09:04:02.000000Z",
                "deleted_at": null
            }
        ],
        "first_page_url": "https://api.optimized.app/v1/my-groups&page=1",
        "from": 1,
        "next_page_url": null,
        "path": "https://api.optimized.app/v1/my-groups",
        "per_page": 25,
        "prev_page_url": null,
        "to": 2
    }

    ```


    ### Navigating Paginated Pages


    By default, paginated endpoints return 25 items per page.


    To navigate through pages, you can:


    1. Use the `page` parameter to request a specific page

    2. Follow the URLs provided in the pagination fields:
      - `first_page_url`: Navigate to the first page
      - `next_page_url`: Navigate to the next page
      - `prev_page_url`: Navigate to the previous page

    ### Sorting Paginated Results


    You can sort paginated results using the `sort` parameter:


    ```

    GET /v1/my-groups?sort=created_at

    ```


    For descending order, prefix the field name with a minus sign:


    ```

    GET /v1/my-groups?sort=-created_at

    ```


    ## Best Practices for Pagination


    1. **Always check for null values** in `next_page_url` and `prev_page_url`
    to determine if there are more pages available.


    2. **Use the provided URLs** instead of constructing your own to ensure
    proper formatting and compatibility with API changes.


    3. **Implement cache invalidation strategies** when working with paginated
    data that may change frequently.


    4. **Be mindful of rate limits** when making multiple requests to retrieve
    all pages of a resource. Implement appropriate backoff strategies if
    necessary.
  version: 2.0.1
servers:
  - url: https://api.optimized.app
    description: Production server
paths:
  /v1/groups/{GROUP_ID}/batches:
    post:
      operationId: createBatch
      summary: Create a new batch of profiles with scheduling
      description: >
        Creates a new batch of profiles with scheduling information.


        **This endpoint has a special rate limit of 10 requests per minute per
        group.**


        #### Errors


        This endpoint may fail with a validation error (HTTP 422). See the
        [Errors](/api/section/errors) section for details on error response
        format.


        Common validation errors include: Duplicate or non-unique `external_id`,
        invalid phone number / email address format
      parameters:
        - name: GROUP_ID
          in: path
          required: true
          description: The unique identifier of the group
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                profile_tag:
                  description: >-
                    Optional. Profile tag information for the batch. If missing,
                    a new one will be created automatically.
                  oneOf:
                    - type: object
                      required:
                        - name
                      properties:
                        name:
                          type: string
                          minLength: 5
                          maxLength: 255
                          description: >-
                            Name of the profile tag. If an exact match exists,
                            that tag will be used. Otherwise, a new tag will be
                            created.
                    - type: object
                      required:
                        - uuid
                      properties:
                        uuid:
                          type: string
                          format: uuid
                          description: >-
                            UUID of an existing profile tag. Can be provided
                            instead of name.
                profiles:
                  type: array
                  description: >
                    Array of profile objects to be created in this batch.

                    **Limit:** You can include up to 50 profiles in a single
                    request.
                  maxItems: 50
                  items:
                    type: object
                    required:
                      - person
                    properties:
                      external_id:
                        type: string
                        maxLength: 255
                        description: >-
                          External identifier for the profile. Must be unique
                          within your organization.
                        nullable: true
                      person:
                        type: object
                        required: []
                        properties:
                          first_name:
                            type: string
                            maxLength: 255
                            nullable: true
                            description: First name of the person.
                          last_name:
                            type: string
                            maxLength: 255
                            nullable: true
                            description: Last name of the person.
                          email_address:
                            type: string
                            format: email
                            maxLength: 255
                            nullable: true
                            description: Email address of the person.
                          phone_number:
                            type: string
                            maxLength: 255
                            nullable: true
                            description: >-
                              Phone number of the person in international format
                              (e.g., +35840123456).
                      attributes:
                        type: array
                        maxItems: 50
                        description: >
                          Optional. Array of custom attributes for the profile.


                          By default, any attribute that does not already exist
                          will be created automatically.


                          If `fail_on_missing_attributes` is set to `true`, then
                          all attributes listed here must already exist in the
                          system (created via the UI or API); otherwise, the
                          request will fail with a validation error.
                        items:
                          type: object
                          required:
                            - name
                            - value
                          properties:
                            name:
                              type: string
                              maxLength: 255
                              description: >
                                Name of the attribute. Must only contain English
                                letters, numbers, underscores (_), or hyphens
                                (-). No spaces or other special characters
                                allowed.
                            value:
                              type: string
                              maxLength: 255
                              description: Value of the attribute.
                schedule:
                  type: object
                  description: >-
                    Optional. Scheduling information for the batch. If omitted,
                    the batch will be imported without scheduling.
                  properties:
                    start_at:
                      type: string
                      format: date-time
                      description: >-
                        Optional. When to start the scheduled campaign in UTC.
                        Format 'YYYY-MM-DD HH:MM:SS'. If missing, the campaign
                        will start immediately.
                    calls_per_minute:
                      type: integer
                      nullable: true
                      minimum: 1
                      maximum: 60
                      description: >
                        Optional. The maximum number of calls per minute for the
                        scheduled campaign. Must be between 1 and 60. If
                        omitted, the default rate will be used.
                    campaign:
                      description: >-
                        Campaign information for scheduling. Either `uuid` or
                        `name` must be provided, but not both.
                      oneOf:
                        - type: object
                          required:
                            - uuid
                          properties:
                            uuid:
                              type: string
                              format: uuid
                              description: UUID of the campaign to schedule.
                        - type: object
                          required:
                            - name
                          properties:
                            name:
                              type: string
                              minLength: 5
                              maxLength: 255
                              description: >-
                                Full name of the campaign. Can be provided
                                instead of uuid.
                fail_on_missing_attributes:
                  type: boolean
                  description: >
                    If true, all attributes must already exist in the system.

                    If any attribute does not exist (i.e., has not been created
                    from the UI or via the API), the request will fail with a
                    validation errors.


                    If false or omitted, all missing attributes will be created
                    automatically.
                  default: false
              required:
                - profiles
      responses:
        '200':
          description: Batch created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  metadata:
                    type: array
                    description: Metadata associated with the batch
                    items:
                      type: object
                  profiles:
                    type: array
                    description: List of profiles created in this batch
                    items:
                      type: object
                      properties:
                        external_id:
                          type: string
                          description: >-
                            External identifier for the profile. Unique within
                            your organization.
                          nullable: true
                        uuid:
                          type: string
                          format: uuid
                          description: UUID of the created profile
                  uuid:
                    type: string
                    format: uuid
                    description: UUID of the created batch
                  updated_at:
                    type: string
                    format: date-time
                    description: Timestamp when the batch was last updated
                  created_at:
                    type: string
                    format: date-time
                    description: Timestamp when the batch was created
                  group:
                    $ref: '#/components/schemas/Group'
                  campaign:
                    $ref: '#/components/schemas/Campaign'
                  profile_tag:
                    $ref: '#/components/schemas/ProfileTag'
                  schedule:
                    $ref: '#/components/schemas/Schedule'
                example:
                  metadata: []
                  profiles:
                    - external_id: '0000012'
                      uuid: 9ed84352-3e00-4fa0-9705-27f566205d5f
                  uuid: 9ed84352-4652-4892-9fdc-cd0e1738d432
                  updated_at: '2025-05-06T07:33:15.000000Z'
                  created_at: '2025-05-06T07:33:15.000000Z'
                  group:
                    uuid: 9e072d6f-0ed2-486a-baca-58f889f55cd4
                    metadata: []
                    name: My group
                    created_at: '2025-01-22T09:04:02.000000Z'
                    updated_at: '2025-01-22T09:04:02.000000Z'
                    deleted_at: null
                    type:
                      uuid: 9e072d6e-d6cc-4a15-92ab-3101057353ee
                      name: Organization
                      created_at: '2025-01-22T09:04:01.000000Z'
                      updated_at: '2025-01-22T09:04:01.000000Z'
                      deleted_at: null
                  campaign:
                    uuid: 9e679c93-ff33-4b1e-bfad-1ac77afe1aef
                    status: untested
                    name: Voice with question
                    description: My complicated voice with question campaign
                    metadata: []
                    created_at: '2025-03-11T07:34:17.000000Z'
                    updated_at: '2025-04-08T06:28:10.000000Z'
                    deleted_at: null
                    hidden_at: null
                    view: broadcast
                    revision: 2
                  profile_tag:
                    name: Voice with question - 202505060733 - e39f2636
                    type: basic
                    is_anonymized: false
                    uuid: 9ed84352-3b85-4018-a097-e4e15ff3b127
                    updated_at: '2025-05-06T07:33:15.000000Z'
                    created_at: '2025-05-06T07:33:15.000000Z'
                  schedule:
                    metadata: []
                    status: scheduled
                    start_at: '2025-05-06T07:33:15.000000Z'
                    uuid: 9ed84352-453d-4a9d-82ad-a71062f44596
                    updated_at: '2025-05-06T07:33:15.000000Z'
                    created_at: '2025-05-06T07:33:15.000000Z'
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API Token
      description: >
        The Optimized.App uses API Tokens for authentication. You can generate
        an Optimized.App API token from the Optimized.app - Organizational
        settings.


        You must include an API token in each request to the Optimized.App API
        with the Authorization request header.


        Example: `Authorization: Bearer [API-TOKEN]`


        If an API token is missing, malformed, or invalid, you will receive an
        HTTP 401 Unauthorized response code.
  schemas:
    Campaign:
      type: object
      description: Information about a campaign.
      nullable: true
      properties:
        uuid:
          type: string
          format: uuid
          description: UUID of the campaign
        status:
          type: string
          description: Status of the campaign
          enum:
            - tested
            - untested
        name:
          type: string
          description: Name of the campaign
        description:
          type: string
          description: Description of the campaign
        metadata:
          type: array
          description: Metadata associated with the campaign
          items:
            type: object
        created_at:
          type: string
          format: date-time
          description: Timestamp when the campaign was created
        updated_at:
          type: string
          format: date-time
          description: Timestamp when the campaign was last updated
        deleted_at:
          type: string
          format: date-time
          description: Timestamp when the campaign was deleted (if applicable)
          nullable: true
        hidden_at:
          type: string
          format: date-time
          description: Timestamp when the campaign was hidden (if applicable)
          nullable: true
        view:
          type: string
          description: View type of the campaign
          enum:
            - broadcast
            - sequence
        revision:
          type: integer
          description: Revision number of the campaign
    ProfileTag:
      type: object
      description: >
        Information about the profile tag for this batch. In the Optimized.App
        UI, a profile tag is referred to as a "Contact list". If this field is
        not provided, a new Contact list will be created automatically and a
        name will be generated by the system. Use this object to associate the
        batch with an existing Contact list or to create a new one with a custom
        name.
      properties:
        name:
          type: string
          minLength: 5
          maxLength: 255
          description: Name of the profile tag (Contact list)
        type:
          type: string
          description: Type of the profile tag
          enum:
            - basic
            - hidden
        is_anonymized:
          type: boolean
          description: >
            Indicates whether all profile data in this profile tag (Contact
            list) is currently anonymized. If true, all profiles associated with
            this tag have their personally identifiable information anonymized.
        uuid:
          type: string
          format: uuid
          description: UUID of the profile tag
        updated_at:
          type: string
          format: date-time
          description: Timestamp when the profile tag was last updated
        created_at:
          type: string
          format: date-time
          description: Timestamp when the profile tag was created
    Schedule:
      type: object
      description: >-
        Information about the schedule if included (null if no schedule was
        set).
      nullable: true
      properties:
        metadata:
          type: array
          description: Metadata associated with the schedule
          items:
            type: object
        status:
          type: string
          description: Status of the schedule
          enum:
            - scheduled
        start_at:
          type: string
          format: date-time
          description: Timestamp when the schedule starts
        uuid:
          type: string
          format: uuid
          description: UUID of the schedule
        updated_at:
          type: string
          format: date-time
          description: Timestamp when the schedule was last updated
        created_at:
          type: string
          format: date-time
          description: Timestamp when the schedule was created
    Group:
      type: object
      description: Information about the group this batch belongs to.
      properties:
        uuid:
          type: string
          format: uuid
          description: UUID of the group
        metadata:
          type: array
          description: Metadata associated with the group
          items:
            type: object
        name:
          type: string
          description: Name of the group
        created_at:
          type: string
          format: date-time
          description: Timestamp when the group was created
        updated_at:
          type: string
          format: date-time
          description: Timestamp when the group was last updated
        deleted_at:
          type: string
          format: date-time
          description: Timestamp when the group was deleted (if applicable)
          nullable: true
        type:
          $ref: '#/components/schemas/GroupType'
    GroupType:
      type: object
      description: Type information for the group.
      properties:
        uuid:
          type: string
          format: uuid
          description: UUID of the group type
        name:
          type: string
          description: Name of the group type
        created_at:
          type: string
          format: date-time
          description: Timestamp when the group type was created
        updated_at:
          type: string
          format: date-time
          description: Timestamp when the group type was last updated
        deleted_at:
          type: string
          format: date-time
          description: Timestamp when the group type was deleted (if applicable)
          nullable: true
security:
  - BearerAuth: []
x-rateLimit:
  description: >
    ## Rate and Usage Limits


    The basic rate limit is 250 requests per minute per user basis. Also,
    depending on your plan, you may have different usage limits. If you exceed
    either limit, your request will return an HTTP 429 Too Many Requests status
    code.


    The following set of headers are returned on the response to help you
    identify your use status:


    | Header | Description |

    | ------ | ----------- |

    | X-Ratelimit-Limit | The maximum number of requests that the consumer is
    permitted to make per minute. |

    | X-Ratelimit-Remaining | The number of requests remaining in the current
    rate limit window. |

    | X-Ratelimit-Reset | The time at which the current rate limit window resets
    in UTC epoch seconds. This header is only present when maximum number of
    requests has been consumed for time period. |


    ### Endpoint Specific Rate Limits


    | Endpoint | Description |

    | ------ | ----------- |

    | Profile importing | This is a group based rate limit for 10 requests per
    minute. |
x-errorResponses:
  description: >
    ## Errors


    Optimized.App employs standard HTTP response codes to signify the outcome of
    an API request. Typically, codes within the 2xx range signify a successful
    operation. Codes within the 4xx range point to an error coming from the
    provided information (for example, missing a necessary parameter or having
    invalid data). Codes in the 5xx range are indicative of issues with
    Optimized servers, though these occurrences are uncommon.


    | HTTP status code | Description |

    | ------ | ----------- |

    | 2xx | Request was made successfully. |

    | 400 | The request was rejected, often due to missing a required parameter.
    |

    | 401 | API token is not valid. |

    | 403 | API token has no permission to perform the request. |

    | 404 | The requested resource does not exist. |

    | 429 | Too many requests hit the API too quickly. We advise implementing an
    exponential backoff strategy for your requests. |

    | 5xx | Something went wrong on our end. |

    | 503 | An unexpected spike in API access traffic. The server is usually
    operational within a short period of time. If the outage persists or you
    receive any other form of an HTTP 5XX error, please contact support. |
